ee76e72bfe2ddf48c05fbfe423df4c151641d136
[alexxy/gromacs.git] / src / gromacs / gmxpreprocess / editconf.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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.
10  *
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.
15  *
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.
20  *
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.
25  *
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.
33  *
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.
36  */
37 #include "gmxpre.h"
38
39 #include "editconf.h"
40
41 #include <cmath>
42 #include <cstring>
43
44 #include <algorithm>
45 #include <string>
46
47 #include "gromacs/commandline/pargs.h"
48 #include "gromacs/commandline/viewit.h"
49 #include "gromacs/fileio/confio.h"
50 #include "gromacs/fileio/pdbio.h"
51 #include "gromacs/fileio/tpxio.h"
52 #include "gromacs/fileio/trxio.h"
53 #include "gromacs/gmxana/princ.h"
54 #include "gromacs/gmxlib/conformation_utilities.h"
55 #include "gromacs/math/functions.h"
56 #include "gromacs/math/units.h"
57 #include "gromacs/math/vec.h"
58 #include "gromacs/pbcutil/pbc.h"
59 #include "gromacs/pbcutil/rmpbc.h"
60 #include "gromacs/topology/atomprop.h"
61 #include "gromacs/topology/index.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"
69 #include "gromacs/utility/strdb.h"
70
71 static real calc_mass(t_atoms* atoms, gmx_bool bGetMass, AtomProperties* aps)
72 {
73     real tmass;
74     int  i;
75
76     tmass = 0;
77     for (i = 0; (i < atoms->nr); i++)
78     {
79         if (bGetMass)
80         {
81             aps->setAtomProperty(epropMass, std::string(*atoms->resinfo[atoms->atom[i].resind].name),
82                                  std::string(*atoms->atomname[i]), &(atoms->atom[i].m));
83         }
84         tmass += atoms->atom[i].m;
85     }
86
87     return tmass;
88 }
89
90 static real calc_geom(int isize, const int* index, rvec* x, rvec geom_center, rvec minval, rvec maxval, gmx_bool bDiam)
91 {
92     real diam2, d;
93     int  ii, i, j;
94
95     clear_rvec(geom_center);
96     diam2 = 0;
97     if (isize == 0)
98     {
99         clear_rvec(minval);
100         clear_rvec(maxval);
101     }
102     else
103     {
104         if (index)
105         {
106             ii = index[0];
107         }
108         else
109         {
110             ii = 0;
111         }
112         for (j = 0; j < DIM; j++)
113         {
114             minval[j] = maxval[j] = x[ii][j];
115         }
116         for (i = 0; i < isize; i++)
117         {
118             if (index)
119             {
120                 ii = index[i];
121             }
122             else
123             {
124                 ii = i;
125             }
126             rvec_inc(geom_center, x[ii]);
127             for (j = 0; j < DIM; j++)
128             {
129                 if (x[ii][j] < minval[j])
130                 {
131                     minval[j] = x[ii][j];
132                 }
133                 if (x[ii][j] > maxval[j])
134                 {
135                     maxval[j] = x[ii][j];
136                 }
137             }
138             if (bDiam)
139             {
140                 if (index)
141                 {
142                     for (j = i + 1; j < isize; j++)
143                     {
144                         d     = distance2(x[ii], x[index[j]]);
145                         diam2 = std::max(d, diam2);
146                     }
147                 }
148                 else
149                 {
150                     for (j = i + 1; j < isize; j++)
151                     {
152                         d     = distance2(x[i], x[j]);
153                         diam2 = std::max(d, diam2);
154                     }
155                 }
156             }
157         }
158         svmul(1.0 / isize, geom_center, geom_center);
159     }
160
161     return std::sqrt(diam2);
162 }
163
164 static void center_conf(int natom, rvec* x, rvec center, rvec geom_cent)
165 {
166     int  i;
167     rvec shift;
168
169     rvec_sub(center, geom_cent, shift);
170
171     printf("    shift       :%7.3f%7.3f%7.3f (nm)\n", shift[XX], shift[YY], shift[ZZ]);
172
173     for (i = 0; (i < natom); i++)
174     {
175         rvec_inc(x[i], shift);
176     }
177 }
178
179 static void scale_conf(int natom, rvec x[], matrix box, const rvec scale)
180 {
181     int i, j;
182
183     for (i = 0; i < natom; i++)
184     {
185         for (j = 0; j < DIM; j++)
186         {
187             x[i][j] *= scale[j];
188         }
189     }
190     for (i = 0; i < DIM; i++)
191     {
192         for (j = 0; j < DIM; j++)
193         {
194             box[i][j] *= scale[j];
195         }
196     }
197 }
198
199 static void read_bfac(const char* fn, int* n_bfac, double** bfac_val, int** bfac_nr)
200 {
201     int    i;
202     char** bfac_lines;
203
204     *n_bfac = get_lines(fn, &bfac_lines);
205     snew(*bfac_val, *n_bfac);
206     snew(*bfac_nr, *n_bfac);
207     fprintf(stderr, "Reading %d B-factors from %s\n", *n_bfac, fn);
208     for (i = 0; (i < *n_bfac); i++)
209     {
210         sscanf(bfac_lines[i], "%d %lf", &(*bfac_nr)[i], &(*bfac_val)[i]);
211     }
212 }
213
214 static void set_pdb_conf_bfac(int natoms, int nres, t_atoms* atoms, int n_bfac, double* bfac, int* bfac_nr, gmx_bool peratom)
215 {
216     real     bfac_min, bfac_max;
217     int      i, n;
218     gmx_bool found;
219
220     if (n_bfac > atoms->nres)
221     {
222         peratom = TRUE;
223     }
224
225     bfac_max = -1e10;
226     bfac_min = 1e10;
227     for (i = 0; (i < n_bfac); i++)
228     {
229         /*    if ((bfac_nr[i]-1<0) || (bfac_nr[i]-1>=atoms->nr))
230            gmx_fatal(FARGS,"Index of B-Factor %d is out of range: %d (%g)",
231            i+1,bfac_nr[i],bfac[i]); */
232         if (bfac[i] > bfac_max)
233         {
234             bfac_max = bfac[i];
235         }
236         if (bfac[i] < bfac_min)
237         {
238             bfac_min = bfac[i];
239         }
240     }
241     while ((bfac_max > 99.99) || (bfac_min < -99.99))
242     {
243         fprintf(stderr,
244                 "Range of values for B-factors too large (min %g, max %g) "
245                 "will scale down a factor 10\n",
246                 bfac_min, bfac_max);
247         for (i = 0; (i < n_bfac); i++)
248         {
249             bfac[i] /= 10;
250         }
251         bfac_max /= 10;
252         bfac_min /= 10;
253     }
254     while ((std::abs(bfac_max) < 0.5) && (std::abs(bfac_min) < 0.5))
255     {
256         fprintf(stderr,
257                 "Range of values for B-factors too small (min %g, max %g) "
258                 "will scale up a factor 10\n",
259                 bfac_min, bfac_max);
260         for (i = 0; (i < n_bfac); i++)
261         {
262             bfac[i] *= 10;
263         }
264         bfac_max *= 10;
265         bfac_min *= 10;
266     }
267
268     for (i = 0; (i < natoms); i++)
269     {
270         atoms->pdbinfo[i].bfac = 0;
271     }
272
273     if (!peratom)
274     {
275         fprintf(stderr, "Will attach %d B-factors to %d residues\n", n_bfac, nres);
276         for (i = 0; (i < n_bfac); i++)
277         {
278             found = FALSE;
279             for (n = 0; (n < natoms); n++)
280             {
281                 if (bfac_nr[i] == atoms->resinfo[atoms->atom[n].resind].nr)
282                 {
283                     atoms->pdbinfo[n].bfac = bfac[i];
284                     found                  = TRUE;
285                 }
286             }
287             if (!found)
288             {
289                 gmx_warning("Residue nr %d not found\n", bfac_nr[i]);
290             }
291         }
292     }
293     else
294     {
295         fprintf(stderr, "Will attach %d B-factors to %d atoms\n", n_bfac, natoms);
296         for (i = 0; (i < n_bfac); i++)
297         {
298             atoms->pdbinfo[bfac_nr[i] - 1].bfac = bfac[i];
299         }
300     }
301 }
302
303 static void pdb_legend(FILE* out, int natoms, int nres, t_atoms* atoms, rvec x[])
304 {
305     real bfac_min, bfac_max, xmin, ymin, zmin;
306     int  i;
307     int  space = ' ';
308
309     bfac_max = -1e10;
310     bfac_min = 1e10;
311     xmin     = 1e10;
312     ymin     = 1e10;
313     zmin     = 1e10;
314     for (i = 0; (i < natoms); i++)
315     {
316         xmin     = std::min(xmin, x[i][XX]);
317         ymin     = std::min(ymin, x[i][YY]);
318         zmin     = std::min(zmin, x[i][ZZ]);
319         bfac_min = std::min(bfac_min, atoms->pdbinfo[i].bfac);
320         bfac_max = std::max(bfac_max, atoms->pdbinfo[i].bfac);
321     }
322     fprintf(stderr, "B-factors range from %g to %g\n", bfac_min, bfac_max);
323     for (i = 1; (i < 12); i++)
324     {
325         fprintf(out, "%-6s%5d  %-4.4s%3.3s %c%4d%c   %8.3f%8.3f%8.3f%6.2f%6.2f\n", "ATOM  ",
326                 natoms + 1 + i, "CA", "LEG", space, nres + 1, space, (xmin + (i * 0.12)) * 10,
327                 ymin * 10, zmin * 10, 1.0, bfac_min + ((i - 1.0) * (bfac_max - bfac_min) / 10));
328     }
329 }
330
331 static void visualize_images(const char* fn, int ePBC, matrix box)
332 {
333     t_atoms atoms;
334     rvec*   img;
335     char *  c, *ala;
336     int     nat, i;
337
338     nat = NTRICIMG + 1;
339     init_t_atoms(&atoms, nat, FALSE);
340     atoms.nr = nat;
341     snew(img, nat);
342     /* FIXME: Constness should not be cast away */
343     c   = const_cast<char*>("C");
344     ala = const_cast<char*>("ALA");
345     for (i = 0; i < nat; i++)
346     {
347         atoms.atomname[i]        = &c;
348         atoms.atom[i].resind     = i;
349         atoms.resinfo[i].name    = &ala;
350         atoms.resinfo[i].nr      = i + 1;
351         atoms.resinfo[i].chainid = 'A' + i / NCUCVERT;
352     }
353     calc_triclinic_images(box, img + 1);
354
355     write_sto_conf(fn, "Images", &atoms, img, nullptr, ePBC, box);
356
357     done_atom(&atoms);
358     sfree(img);
359 }
360
361 static void visualize_box(FILE* out, int a0, int r0, matrix box, const rvec gridsize)
362 {
363     int*  edge;
364     rvec *vert, shift;
365     int   nx, ny, nz, nbox, nat;
366     int   i, j, x, y, z;
367     int   rectedge[24] = { 0, 1, 1, 3, 3, 2, 0, 2, 0, 4, 1, 5, 3, 7, 2, 6, 4, 5, 5, 7, 7, 6, 6, 4 };
368
369     a0++;
370     r0++;
371
372     nx   = gmx::roundToInt(gridsize[XX]);
373     ny   = gmx::roundToInt(gridsize[YY]);
374     nz   = gmx::roundToInt(gridsize[ZZ]);
375     nbox = nx * ny * nz;
376     if (TRICLINIC(box))
377     {
378         nat = nbox * NCUCVERT;
379         snew(vert, nat);
380         calc_compact_unitcell_vertices(ecenterDEF, box, vert);
381         j = 0;
382         for (z = 0; z < nz; z++)
383         {
384             for (y = 0; y < ny; y++)
385             {
386                 for (x = 0; x < nx; x++)
387                 {
388                     for (i = 0; i < DIM; i++)
389                     {
390                         shift[i] = x * box[0][i] + y * box[1][i] + z * box[2][i];
391                     }
392                     for (i = 0; i < NCUCVERT; i++)
393                     {
394                         rvec_add(vert[i], shift, vert[j]);
395                         j++;
396                     }
397                 }
398             }
399         }
400
401         for (i = 0; i < nat; i++)
402         {
403             gmx_fprintf_pdb_atomline(out, epdbATOM, a0 + i, "C", ' ', "BOX", 'K' + i / NCUCVERT,
404                                      r0 + i, ' ', 10 * vert[i][XX], 10 * vert[i][YY],
405                                      10 * vert[i][ZZ], 1.0, 0.0, "");
406         }
407
408         edge = compact_unitcell_edges();
409         for (j = 0; j < nbox; j++)
410         {
411             for (i = 0; i < NCUCEDGE; i++)
412             {
413                 fprintf(out, "CONECT%5d%5d\n", a0 + j * NCUCVERT + edge[2 * i],
414                         a0 + j * NCUCVERT + edge[2 * i + 1]);
415             }
416         }
417
418         sfree(vert);
419     }
420     else
421     {
422         i = 0;
423         for (z = 0; z <= 1; z++)
424         {
425             for (y = 0; y <= 1; y++)
426             {
427                 for (x = 0; x <= 1; x++)
428                 {
429                     gmx_fprintf_pdb_atomline(out, epdbATOM, a0 + i, "C", ' ', "BOX", 'K' + i / 8,
430                                              r0 + i, ' ', x * 10 * box[XX][XX], y * 10 * box[YY][YY],
431                                              z * 10 * box[ZZ][ZZ], 1.0, 0.0, "");
432                     i++;
433                 }
434             }
435         }
436         for (i = 0; i < 24; i += 2)
437         {
438             fprintf(out, "CONECT%5d%5d\n", a0 + rectedge[i], a0 + rectedge[i + 1]);
439         }
440     }
441 }
442
443 static void calc_rotmatrix(rvec principal_axis, rvec targetvec, matrix rotmatrix)
444 {
445     rvec rotvec;
446     real ux, uy, uz, costheta, sintheta;
447
448     costheta = cos_angle(principal_axis, targetvec);
449     sintheta = std::sqrt(1.0 - costheta * costheta); /* sign is always positive since 0<theta<pi */
450
451     /* Determine rotation from cross product with target vector */
452     cprod(principal_axis, targetvec, rotvec);
453     unitv(rotvec, rotvec);
454     printf("Aligning %g %g %g to %g %g %g : xprod  %g %g %g\n", principal_axis[XX],
455            principal_axis[YY], principal_axis[ZZ], targetvec[XX], targetvec[YY], targetvec[ZZ],
456            rotvec[XX], rotvec[YY], rotvec[ZZ]);
457
458     ux              = rotvec[XX];
459     uy              = rotvec[YY];
460     uz              = rotvec[ZZ];
461     rotmatrix[0][0] = ux * ux + (1.0 - ux * ux) * costheta;
462     rotmatrix[0][1] = ux * uy * (1 - costheta) - uz * sintheta;
463     rotmatrix[0][2] = ux * uz * (1 - costheta) + uy * sintheta;
464     rotmatrix[1][0] = ux * uy * (1 - costheta) + uz * sintheta;
465     rotmatrix[1][1] = uy * uy + (1.0 - uy * uy) * costheta;
466     rotmatrix[1][2] = uy * uz * (1 - costheta) - ux * sintheta;
467     rotmatrix[2][0] = ux * uz * (1 - costheta) - uy * sintheta;
468     rotmatrix[2][1] = uy * uz * (1 - costheta) + ux * sintheta;
469     rotmatrix[2][2] = uz * uz + (1.0 - uz * uz) * costheta;
470
471     printf("Rotation matrix: \n%g %g %g\n%g %g %g\n%g %g %g\n", rotmatrix[0][0], rotmatrix[0][1],
472            rotmatrix[0][2], rotmatrix[1][0], rotmatrix[1][1], rotmatrix[1][2], rotmatrix[2][0],
473            rotmatrix[2][1], rotmatrix[2][2]);
474 }
475
476 static void renum_resnr(t_atoms* atoms, int isize, const int* index, int resnr_start)
477 {
478     int i, resind_prev, resind;
479
480     resind_prev = -1;
481     for (i = 0; i < isize; i++)
482     {
483         resind = atoms->atom[index == nullptr ? i : index[i]].resind;
484         if (resind != resind_prev)
485         {
486             atoms->resinfo[resind].nr = resnr_start;
487             resnr_start++;
488         }
489         resind_prev = resind;
490     }
491 }
492
493 int gmx_editconf(int argc, char* argv[])
494 {
495     const char* desc[] = {
496         "[THISMODULE] converts generic structure format to [REF].gro[ref], [TT].g96[tt]",
497         "or [REF].pdb[ref].",
498         "[PAR]",
499         "The box can be modified with options [TT]-box[tt], [TT]-d[tt] and",
500         "[TT]-angles[tt]. Both [TT]-box[tt] and [TT]-d[tt]",
501         "will center the system in the box, unless [TT]-noc[tt] is used.",
502         "The [TT]-center[tt] option can be used to shift the geometric center",
503         "of the system from the default of (x/2, y/2, z/2) implied by [TT]-c[tt]",
504         "to some other value.",
505         "[PAR]",
506         "Option [TT]-bt[tt] determines the box type: [TT]triclinic[tt] is a",
507         "triclinic box, [TT]cubic[tt] is a rectangular box with all sides equal",
508         "[TT]dodecahedron[tt] represents a rhombic dodecahedron and",
509         "[TT]octahedron[tt] is a truncated octahedron.",
510         "The last two are special cases of a triclinic box.",
511         "The length of the three box vectors of the truncated octahedron is the",
512         "shortest distance between two opposite hexagons.",
513         "Relative to a cubic box with some periodic image distance, the volume of a ",
514         "dodecahedron with this same periodic distance is 0.71 times that of the cube, ",
515         "and that of a truncated octahedron is 0.77 times.",
516         "[PAR]",
517         "Option [TT]-box[tt] requires only",
518         "one value for a cubic, rhombic dodecahedral, or truncated octahedral box.",
519         "[PAR]",
520         "With [TT]-d[tt] and a [TT]triclinic[tt] box the size of the system in the [IT]x[it]-, ",
521         "[IT]y[it]-,",
522         "and [IT]z[it]-directions is used. With [TT]-d[tt] and [TT]cubic[tt],",
523         "[TT]dodecahedron[tt] or [TT]octahedron[tt] boxes, the dimensions are set",
524         "to the diameter of the system (largest distance between atoms) plus twice",
525         "the specified distance.",
526         "[PAR]",
527         "Option [TT]-angles[tt] is only meaningful with option [TT]-box[tt] and",
528         "a triclinic box and cannot be used with option [TT]-d[tt].",
529         "[PAR]",
530         "When [TT]-n[tt] or [TT]-ndef[tt] is set, a group",
531         "can be selected for calculating the size and the geometric center,",
532         "otherwise the whole system is used.",
533         "[PAR]",
534         "[TT]-rotate[tt] rotates the coordinates and velocities.",
535         "[PAR]",
536         "[TT]-princ[tt] aligns the principal axes of the system along the",
537         "coordinate axes, with the longest axis aligned with the [IT]x[it]-axis. ",
538         "This may allow you to decrease the box volume,",
539         "but beware that molecules can rotate significantly in a nanosecond.",
540         "[PAR]",
541         "Scaling is applied before any of the other operations are",
542         "performed. Boxes and coordinates can be scaled to give a certain density (option",
543         "[TT]-density[tt]). Note that this may be inaccurate in case a [REF].gro[ref]",
544         "file is given as input. A special feature of the scaling option is that when the",
545         "factor -1 is given in one dimension, one obtains a mirror image,",
546         "mirrored in one of the planes. When one uses -1 in three dimensions, ",
547         "a point-mirror image is obtained.[PAR]",
548         "Groups are selected after all operations have been applied.[PAR]",
549         "Periodicity can be removed in a crude manner.",
550         "It is important that the box vectors at the bottom of your input file",
551         "are correct when the periodicity is to be removed.",
552         "[PAR]",
553         "When writing [REF].pdb[ref] files, B-factors can be",
554         "added with the [TT]-bf[tt] option. B-factors are read",
555         "from a file with with following format: first line states number of",
556         "entries in the file, next lines state an index",
557         "followed by a B-factor. The B-factors will be attached per residue",
558         "unless the number of B-factors is larger than the number of the residues or unless the",
559         "[TT]-atom[tt] option is set. Obviously, any type of numeric data can",
560         "be added instead of B-factors. [TT]-legend[tt] will produce",
561         "a row of CA atoms with B-factors ranging from the minimum to the",
562         "maximum value found, effectively making a legend for viewing.",
563         "[PAR]",
564         "With the option [TT]-mead[tt] a special [REF].pdb[ref] ([REF].pqr[ref])",
565         "file for the MEAD electrostatics",
566         "program (Poisson-Boltzmann solver) can be made. A further prerequisite",
567         "is that the input file is a run input file.",
568         "The B-factor field is then filled with the Van der Waals radius",
569         "of the atoms while the occupancy field will hold the charge.",
570         "[PAR]",
571         "The option [TT]-grasp[tt] is similar, but it puts the charges in the B-factor",
572         "and the radius in the occupancy.",
573         "[PAR]",
574         "Option [TT]-align[tt] allows alignment",
575         "of the principal axis of a specified group against the given vector, ",
576         "with an optional center of rotation specified by [TT]-aligncenter[tt].",
577         "[PAR]",
578         "Finally, with option [TT]-label[tt], [TT]editconf[tt] can add a chain identifier",
579         "to a [REF].pdb[ref] file, which can be useful for analysis with e.g. Rasmol.",
580         "[PAR]",
581         "To convert a truncated octrahedron file produced by a package which uses",
582         "a cubic box with the corners cut off (such as GROMOS), use::",
583         "",
584         "  gmx editconf -f in -rotate 0 45 35.264 -bt o -box veclen -o out",
585         "",
586         "where [TT]veclen[tt] is the size of the cubic box times [SQRT]3[sqrt]/2."
587     };
588     const char* bugs[] = {
589         "For complex molecules, the periodicity removal routine may break down, ",
590         "in that case you can use [gmx-trjconv]."
591     };
592     static real     dist = 0.0;
593     static gmx_bool bNDEF = FALSE, bRMPBC = FALSE, bCenter = FALSE, bReadVDW = FALSE, bCONECT = FALSE;
594     static gmx_bool peratom = FALSE, bLegend = FALSE, bOrient = FALSE, bMead = FALSE,
595                     bGrasp = FALSE, bSig56 = FALSE;
596     static rvec scale = { 1, 1, 1 }, newbox = { 0, 0, 0 }, newang = { 90, 90, 90 };
597     static real rho = 1000.0, rvdw = 0.12;
598     static rvec center = { 0, 0, 0 }, translation = { 0, 0, 0 }, rotangles = { 0, 0, 0 },
599                 aligncenter = { 0, 0, 0 }, targetvec = { 0, 0, 0 };
600     static const char *btype[] = { nullptr,        "triclinic",  "cubic",
601                                    "dodecahedron", "octahedron", nullptr },
602                       *label   = "A";
603     static rvec visbox         = { 0, 0, 0 };
604     static int  resnr_start    = -1;
605     t_pargs     pa[]           = {
606         { "-ndef", FALSE, etBOOL, { &bNDEF }, "Choose output from default index groups" },
607         { "-visbox",
608           FALSE,
609           etRVEC,
610           { visbox },
611           "HIDDENVisualize a grid of boxes, -1 visualizes the 14 box images" },
612         { "-bt", FALSE, etENUM, { btype }, "Box type for [TT]-box[tt] and [TT]-d[tt]" },
613         { "-box", FALSE, etRVEC, { newbox }, "Box vector lengths (a,b,c)" },
614         { "-angles", FALSE, etRVEC, { newang }, "Angles between the box vectors (bc,ac,ab)" },
615         { "-d", FALSE, etREAL, { &dist }, "Distance between the solute and the box" },
616         { "-c",
617           FALSE,
618           etBOOL,
619           { &bCenter },
620           "Center molecule in box (implied by [TT]-box[tt] and [TT]-d[tt])" },
621         { "-center", FALSE, etRVEC, { center }, "Shift the geometrical center to (x,y,z)" },
622         { "-aligncenter", FALSE, etRVEC, { aligncenter }, "Center of rotation for alignment" },
623         { "-align", FALSE, etRVEC, { targetvec }, "Align to target vector" },
624         { "-translate", FALSE, etRVEC, { translation }, "Translation" },
625         { "-rotate",
626           FALSE,
627           etRVEC,
628           { rotangles },
629           "Rotation around the X, Y and Z axes in degrees" },
630         { "-princ", FALSE, etBOOL, { &bOrient }, "Orient molecule(s) along their principal axes" },
631         { "-scale", FALSE, etRVEC, { scale }, "Scaling factor" },
632         { "-density",
633           FALSE,
634           etREAL,
635           { &rho },
636           "Density (g/L) of the output box achieved by scaling" },
637         { "-pbc", FALSE, etBOOL, { &bRMPBC }, "Remove the periodicity (make molecule whole again)" },
638         { "-resnr", FALSE, etINT, { &resnr_start }, " Renumber residues starting from resnr" },
639         { "-grasp",
640           FALSE,
641           etBOOL,
642           { &bGrasp },
643           "Store the charge of the atom in the B-factor field and the radius of the atom in the "
644           "occupancy field" },
645         { "-rvdw",
646           FALSE,
647           etREAL,
648           { &rvdw },
649           "Default Van der Waals radius (in nm) if one can not be found in the database or if no "
650           "parameters are present in the topology file" },
651         { "-sig56",
652           FALSE,
653           etBOOL,
654           { &bSig56 },
655           "Use rmin/2 (minimum in the Van der Waals potential) rather than [GRK]sigma[grk]/2 " },
656         { "-vdwread",
657           FALSE,
658           etBOOL,
659           { &bReadVDW },
660           "Read the Van der Waals radii from the file [TT]vdwradii.dat[tt] rather than computing "
661           "the radii based on the force field" },
662         { "-atom", FALSE, etBOOL, { &peratom }, "Force B-factor attachment per atom" },
663         { "-legend", FALSE, etBOOL, { &bLegend }, "Make B-factor legend" },
664         { "-label", FALSE, etSTR, { &label }, "Add chain label for all residues" },
665         { "-conect",
666           FALSE,
667           etBOOL,
668           { &bCONECT },
669           "Add CONECT records to a [REF].pdb[ref] file when written. Can only be done when a "
670           "topology is present" }
671     };
672 #define NPA asize(pa)
673
674     FILE*             out;
675     const char *      infile, *outfile;
676     int               outftp, inftp, natom, i, j, n_bfac, itype, ntype;
677     double *          bfac    = nullptr, c6, c12;
678     int*              bfac_nr = nullptr;
679     t_topology*       top     = nullptr;
680     char *            grpname, *sgrpname, *agrpname;
681     int               isize, ssize, numAlignmentAtoms;
682     int *             index, *sindex, *aindex;
683     rvec *            x, *v, gc, rmin, rmax, size;
684     int               ePBC;
685     matrix            box, rotmatrix, trans;
686     rvec              princd, tmpvec;
687     gmx_bool          bIndex, bSetSize, bSetAng, bDist, bSetCenter, bAlign;
688     gmx_bool          bHaveV, bScale, bRho, bTranslate, bRotate, bCalcGeom, bCalcDiam;
689     real              diam = 0, mass = 0, d, vdw;
690     gmx_conect        conect;
691     gmx_output_env_t* oenv;
692     t_filenm          fnm[] = { { efSTX, "-f", nullptr, ffREAD },
693                        { efNDX, "-n", nullptr, ffOPTRD },
694                        { efSTO, nullptr, nullptr, ffOPTWR },
695                        { efPQR, "-mead", "mead", ffOPTWR },
696                        { efDAT, "-bf", "bfact", ffOPTRD } };
697 #define NFILE asize(fnm)
698
699     if (!parse_common_args(&argc, argv, PCA_CAN_VIEW, NFILE, fnm, NPA, pa, asize(desc), desc,
700                            asize(bugs), bugs, &oenv))
701     {
702         return 0;
703     }
704     fprintf(stdout,
705             "Note that major changes are planned in future for "
706             "editconf, to improve usability and utility.\n");
707
708     bIndex     = opt2bSet("-n", NFILE, fnm) || bNDEF;
709     bMead      = opt2bSet("-mead", NFILE, fnm);
710     bSetSize   = opt2parg_bSet("-box", NPA, pa);
711     bSetAng    = opt2parg_bSet("-angles", NPA, pa);
712     bSetCenter = opt2parg_bSet("-center", NPA, pa);
713     bDist      = opt2parg_bSet("-d", NPA, pa);
714     bAlign     = opt2parg_bSet("-align", NPA, pa);
715     /* Only automatically turn on centering without -noc */
716     if ((bDist || bSetSize || bSetCenter) && !opt2parg_bSet("-c", NPA, pa))
717     {
718         bCenter = TRUE;
719     }
720     bScale     = opt2parg_bSet("-scale", NPA, pa);
721     bRho       = opt2parg_bSet("-density", NPA, pa);
722     bTranslate = opt2parg_bSet("-translate", NPA, pa);
723     bRotate    = opt2parg_bSet("-rotate", NPA, pa);
724     if (bScale && bRho)
725     {
726         fprintf(stderr, "WARNING: setting -density overrides -scale\n");
727     }
728     bScale    = bScale || bRho;
729     bCalcGeom = bCenter || bRotate || bOrient || bScale;
730
731     GMX_RELEASE_ASSERT(btype[0] != nullptr, "Option setting inconsistency; btype[0] is NULL");
732
733     bCalcDiam = (btype[0][0] == 'c' || btype[0][0] == 'd' || btype[0][0] == 'o');
734
735     infile = ftp2fn(efSTX, NFILE, fnm);
736     if (bMead)
737     {
738         outfile = ftp2fn(efPQR, NFILE, fnm);
739     }
740     else
741     {
742         outfile = ftp2fn(efSTO, NFILE, fnm);
743     }
744     outftp = fn2ftp(outfile);
745     inftp  = fn2ftp(infile);
746
747     AtomProperties aps;
748
749     if (bMead && bGrasp)
750     {
751         printf("Incompatible options -mead and -grasp. Turning off -grasp\n");
752         bGrasp = FALSE;
753     }
754     if (bGrasp && (outftp != efPDB))
755     {
756         gmx_fatal(FARGS,
757                   "Output file should be a .pdb file"
758                   " when using the -grasp option\n");
759     }
760     if ((bMead || bGrasp) && (fn2ftp(infile) != efTPR))
761     {
762         gmx_fatal(FARGS,
763                   "Input file should be a .tpr file"
764                   " when using the -mead option\n");
765     }
766
767     t_symtab symtab;
768     char*    name;
769     t_atoms  atoms;
770     open_symtab(&symtab);
771     readConfAndAtoms(infile, &symtab, &name, &atoms, &ePBC, &x, &v, box);
772     natom = atoms.nr;
773     if (atoms.pdbinfo == nullptr)
774     {
775         snew(atoms.pdbinfo, atoms.nr);
776     }
777     atoms.havePdbInfo = TRUE;
778
779     if (fn2ftp(infile) == efPDB)
780     {
781         get_pdb_atomnumber(&atoms, &aps);
782     }
783     printf("Read %d atoms\n", atoms.nr);
784
785     /* Get the element numbers if available in a pdb file */
786     if (fn2ftp(infile) == efPDB)
787     {
788         get_pdb_atomnumber(&atoms, &aps);
789     }
790
791     if (ePBC != epbcNONE)
792     {
793         real vol = det(box);
794         printf("Volume: %g nm^3, corresponds to roughly %d electrons\n", vol,
795                100 * (static_cast<int>(vol * 4.5)));
796     }
797
798     if (bMead || bGrasp || bCONECT)
799     {
800         top = read_top(infile, nullptr);
801     }
802
803     if (bMead || bGrasp)
804     {
805         if (atoms.nr != top->atoms.nr)
806         {
807             gmx_fatal(FARGS, "Atom numbers don't match (%d vs. %d)", atoms.nr, top->atoms.nr);
808         }
809         snew(atoms.pdbinfo, top->atoms.nr);
810         ntype = top->idef.atnr;
811         for (i = 0; (i < atoms.nr); i++)
812         {
813             /* Determine the Van der Waals radius from the force field */
814             if (bReadVDW)
815             {
816                 if (!aps.setAtomProperty(epropVDW, *top->atoms.resinfo[top->atoms.atom[i].resind].name,
817                                          *top->atoms.atomname[i], &vdw))
818                 {
819                     vdw = rvdw;
820                 }
821             }
822             else
823             {
824                 itype = top->atoms.atom[i].type;
825                 c12   = top->idef.iparams[itype * ntype + itype].lj.c12;
826                 c6    = top->idef.iparams[itype * ntype + itype].lj.c6;
827                 if ((c6 != 0) && (c12 != 0))
828                 {
829                     real sig6;
830                     if (bSig56)
831                     {
832                         sig6 = 2 * c12 / c6;
833                     }
834                     else
835                     {
836                         sig6 = c12 / c6;
837                     }
838                     vdw = 0.5 * gmx::sixthroot(sig6);
839                 }
840                 else
841                 {
842                     vdw = rvdw;
843                 }
844             }
845             /* Factor of 10 for nm -> Angstroms */
846             vdw *= 10;
847
848             if (bMead)
849             {
850                 atoms.pdbinfo[i].occup = top->atoms.atom[i].q;
851                 atoms.pdbinfo[i].bfac  = vdw;
852             }
853             else
854             {
855                 atoms.pdbinfo[i].occup = vdw;
856                 atoms.pdbinfo[i].bfac  = top->atoms.atom[i].q;
857             }
858         }
859     }
860     bHaveV = FALSE;
861     for (i = 0; (i < natom) && !bHaveV; i++)
862     {
863         for (j = 0; (j < DIM) && !bHaveV; j++)
864         {
865             bHaveV = bHaveV || (v[i][j] != 0);
866         }
867     }
868     printf("%selocities found\n", bHaveV ? "V" : "No v");
869
870     if (visbox[0] > 0)
871     {
872         if (bIndex)
873         {
874             gmx_fatal(FARGS, "Sorry, can not visualize box with index groups");
875         }
876         if (outftp != efPDB)
877         {
878             gmx_fatal(FARGS, "Sorry, can only visualize box with a pdb file");
879         }
880     }
881     else if (visbox[0] == -1)
882     {
883         visualize_images("images.pdb", ePBC, box);
884     }
885
886     /* remove pbc */
887     if (bRMPBC)
888     {
889         rm_gropbc(&atoms, x, box);
890     }
891
892     if (bCalcGeom)
893     {
894         if (bIndex)
895         {
896             fprintf(stderr, "\nSelect a group for determining the system size:\n");
897             get_index(&atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &ssize, &sindex, &sgrpname);
898         }
899         else
900         {
901             ssize  = atoms.nr;
902             sindex = nullptr;
903         }
904         diam = calc_geom(ssize, sindex, x, gc, rmin, rmax, bCalcDiam);
905         rvec_sub(rmax, rmin, size);
906         printf("    system size :%7.3f%7.3f%7.3f (nm)\n", size[XX], size[YY], size[ZZ]);
907         if (bCalcDiam)
908         {
909             printf("    diameter    :%7.3f               (nm)\n", diam);
910         }
911         printf("    center      :%7.3f%7.3f%7.3f (nm)\n", gc[XX], gc[YY], gc[ZZ]);
912         printf("    box vectors :%7.3f%7.3f%7.3f (nm)\n", norm(box[XX]), norm(box[YY]), norm(box[ZZ]));
913         printf("    box angles  :%7.2f%7.2f%7.2f (degrees)\n",
914                norm2(box[ZZ]) == 0 ? 0 : RAD2DEG * gmx_angle(box[YY], box[ZZ]),
915                norm2(box[ZZ]) == 0 ? 0 : RAD2DEG * gmx_angle(box[XX], box[ZZ]),
916                norm2(box[YY]) == 0 ? 0 : RAD2DEG * gmx_angle(box[XX], box[YY]));
917         printf("    box volume  :%7.2f               (nm^3)\n", det(box));
918     }
919
920     if (bRho || bOrient || bAlign)
921     {
922         mass = calc_mass(&atoms, !fn2bTPX(infile), &aps);
923     }
924
925     if (bOrient)
926     {
927         int*  index;
928         char* grpnames;
929
930         /* Get a group for principal component analysis */
931         fprintf(stderr, "\nSelect group for the determining the orientation\n");
932         get_index(&atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpnames);
933
934         /* Orient the principal axes along the coordinate axes */
935         orient_princ(&atoms, isize, index, natom, x, bHaveV ? v : nullptr, nullptr);
936         sfree(index);
937         sfree(grpnames);
938     }
939
940     if (bScale)
941     {
942         /* scale coordinates and box */
943         if (bRho)
944         {
945             /* Compute scaling constant */
946             real vol, dens;
947
948             vol  = det(box);
949             dens = (mass * AMU) / (vol * NANO * NANO * NANO);
950             fprintf(stderr, "Volume  of input %g (nm^3)\n", vol);
951             fprintf(stderr, "Mass    of input %g (a.m.u.)\n", mass);
952             fprintf(stderr, "Density of input %g (g/l)\n", dens);
953             if (vol == 0 || mass == 0)
954             {
955                 gmx_fatal(FARGS,
956                           "Cannot scale density with "
957                           "zero mass (%g) or volume (%g)\n",
958                           mass, vol);
959             }
960
961             scale[XX] = scale[YY] = scale[ZZ] = std::cbrt(dens / rho);
962             fprintf(stderr, "Scaling all box vectors by %g\n", scale[XX]);
963         }
964         scale_conf(atoms.nr, x, box, scale);
965     }
966
967     if (bAlign)
968     {
969         if (bIndex)
970         {
971             fprintf(stderr, "\nSelect a group that you want to align:\n");
972             get_index(&atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &numAlignmentAtoms, &aindex, &agrpname);
973         }
974         else
975         {
976             numAlignmentAtoms = atoms.nr;
977             snew(aindex, numAlignmentAtoms);
978             for (i = 0; i < numAlignmentAtoms; i++)
979             {
980                 aindex[i] = i;
981             }
982         }
983         printf("Aligning %d atoms (out of %d) to %g %g %g, center of rotation %g %g %g\n",
984                numAlignmentAtoms, natom, targetvec[XX], targetvec[YY], targetvec[ZZ],
985                aligncenter[XX], aligncenter[YY], aligncenter[ZZ]);
986         /*subtract out pivot point*/
987         for (i = 0; i < numAlignmentAtoms; i++)
988         {
989             rvec_dec(x[aindex[i]], aligncenter);
990         }
991         /*now determine transform and rotate*/
992         /*will this work?*/
993         principal_comp(numAlignmentAtoms, aindex, atoms.atom, x, trans, princd);
994
995         unitv(targetvec, targetvec);
996         printf("Using %g %g %g as principal axis\n", trans[0][2], trans[1][2], trans[2][2]);
997         tmpvec[XX] = trans[0][2];
998         tmpvec[YY] = trans[1][2];
999         tmpvec[ZZ] = trans[2][2];
1000         calc_rotmatrix(tmpvec, targetvec, rotmatrix);
1001         /* rotmatrix finished */
1002
1003         for (i = 0; i < numAlignmentAtoms; ++i)
1004         {
1005             mvmul(rotmatrix, x[aindex[i]], tmpvec);
1006             copy_rvec(tmpvec, x[aindex[i]]);
1007         }
1008
1009         /*add pivot point back*/
1010         for (i = 0; i < numAlignmentAtoms; i++)
1011         {
1012             rvec_inc(x[aindex[i]], aligncenter);
1013         }
1014         if (!bIndex)
1015         {
1016             sfree(aindex);
1017         }
1018     }
1019
1020     if (bTranslate)
1021     {
1022         if (bIndex)
1023         {
1024             fprintf(stderr, "\nSelect a group that you want to translate:\n");
1025             get_index(&atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &ssize, &sindex, &sgrpname);
1026         }
1027         else
1028         {
1029             ssize  = atoms.nr;
1030             sindex = nullptr;
1031         }
1032         printf("Translating %d atoms (out of %d) by %g %g %g nm\n", ssize, natom, translation[XX],
1033                translation[YY], translation[ZZ]);
1034         if (sindex)
1035         {
1036             for (i = 0; i < ssize; i++)
1037             {
1038                 rvec_inc(x[sindex[i]], translation);
1039             }
1040         }
1041         else
1042         {
1043             for (i = 0; i < natom; i++)
1044             {
1045                 rvec_inc(x[i], translation);
1046             }
1047         }
1048     }
1049     if (bRotate)
1050     {
1051         /* Rotate */
1052         printf("Rotating %g, %g, %g degrees around the X, Y and Z axis respectively\n",
1053                rotangles[XX], rotangles[YY], rotangles[ZZ]);
1054         for (i = 0; i < DIM; i++)
1055         {
1056             rotangles[i] *= DEG2RAD;
1057         }
1058         rotate_conf(natom, x, v, rotangles[XX], rotangles[YY], rotangles[ZZ]);
1059     }
1060
1061     if (bCalcGeom)
1062     {
1063         /* recalc geometrical center and max and min coordinates and size */
1064         calc_geom(ssize, sindex, x, gc, rmin, rmax, FALSE);
1065         rvec_sub(rmax, rmin, size);
1066         if (bScale || bOrient || bRotate)
1067         {
1068             printf("new system size : %6.3f %6.3f %6.3f\n", size[XX], size[YY], size[ZZ]);
1069         }
1070     }
1071
1072     if ((btype[0] != nullptr) && (bSetSize || bDist || (btype[0][0] == 't' && bSetAng)))
1073     {
1074         ePBC = epbcXYZ;
1075         if (!(bSetSize || bDist))
1076         {
1077             for (i = 0; i < DIM; i++)
1078             {
1079                 newbox[i] = norm(box[i]);
1080             }
1081         }
1082         clear_mat(box);
1083         /* calculate new boxsize */
1084         switch (btype[0][0])
1085         {
1086             case 't':
1087                 if (bDist)
1088                 {
1089                     for (i = 0; i < DIM; i++)
1090                     {
1091                         newbox[i] = size[i] + 2 * dist;
1092                     }
1093                 }
1094                 if (!bSetAng)
1095                 {
1096                     box[XX][XX] = newbox[XX];
1097                     box[YY][YY] = newbox[YY];
1098                     box[ZZ][ZZ] = newbox[ZZ];
1099                 }
1100                 else
1101                 {
1102                     matrix_convert(box, newbox, newang);
1103                 }
1104                 break;
1105             case 'c':
1106             case 'd':
1107             case 'o':
1108                 if (bSetSize)
1109                 {
1110                     d = newbox[0];
1111                 }
1112                 else
1113                 {
1114                     d = diam + 2 * dist;
1115                 }
1116                 if (btype[0][0] == 'c')
1117                 {
1118                     for (i = 0; i < DIM; i++)
1119                     {
1120                         box[i][i] = d;
1121                     }
1122                 }
1123                 else if (btype[0][0] == 'd')
1124                 {
1125                     box[XX][XX] = d;
1126                     box[YY][YY] = d;
1127                     box[ZZ][XX] = d / 2;
1128                     box[ZZ][YY] = d / 2;
1129                     box[ZZ][ZZ] = d * std::sqrt(2.0) / 2.0;
1130                 }
1131                 else
1132                 {
1133                     box[XX][XX] = d;
1134                     box[YY][XX] = d / 3;
1135                     box[YY][YY] = d * std::sqrt(2.0) * 2.0 / 3.0;
1136                     box[ZZ][XX] = -d / 3;
1137                     box[ZZ][YY] = d * std::sqrt(2.0) / 3.0;
1138                     box[ZZ][ZZ] = d * std::sqrt(6.0) / 3.0;
1139                 }
1140                 break;
1141         }
1142     }
1143
1144     /* calculate new coords for geometrical center */
1145     if (!bSetCenter)
1146     {
1147         calc_box_center(ecenterDEF, box, center);
1148     }
1149
1150     /* center molecule on 'center' */
1151     if (bCenter)
1152     {
1153         center_conf(natom, x, center, gc);
1154     }
1155
1156     /* print some */
1157     if (bCalcGeom)
1158     {
1159         calc_geom(ssize, sindex, x, gc, rmin, rmax, FALSE);
1160         printf("new center      :%7.3f%7.3f%7.3f (nm)\n", gc[XX], gc[YY], gc[ZZ]);
1161     }
1162     if (bOrient || bScale || bDist || bSetSize)
1163     {
1164         printf("new box vectors :%7.3f%7.3f%7.3f (nm)\n", norm(box[XX]), norm(box[YY]), norm(box[ZZ]));
1165         printf("new box angles  :%7.2f%7.2f%7.2f (degrees)\n",
1166                norm2(box[ZZ]) == 0 ? 0 : RAD2DEG * gmx_angle(box[YY], box[ZZ]),
1167                norm2(box[ZZ]) == 0 ? 0 : RAD2DEG * gmx_angle(box[XX], box[ZZ]),
1168                norm2(box[YY]) == 0 ? 0 : RAD2DEG * gmx_angle(box[XX], box[YY]));
1169         printf("new box volume  :%7.2f               (nm^3)\n", det(box));
1170     }
1171
1172     if (check_box(epbcXYZ, box))
1173     {
1174         printf("\nWARNING: %s\n"
1175                "See the GROMACS manual for a description of the requirements that\n"
1176                "must be satisfied by descriptions of simulation cells.\n",
1177                check_box(epbcXYZ, box));
1178     }
1179
1180     if (bDist && btype[0][0] == 't')
1181     {
1182         if (TRICLINIC(box))
1183         {
1184             printf("\nWARNING: Your box is triclinic with non-orthogonal axes. In this case, the\n"
1185                    "distance from the solute to a box surface along the corresponding normal\n"
1186                    "vector might be somewhat smaller than your specified value %f.\n"
1187                    "You can check the actual value with g_mindist -pi\n",
1188                    dist);
1189         }
1190         else if (!opt2parg_bSet("-bt", NPA, pa))
1191         {
1192             printf("\nWARNING: No boxtype specified - distance condition applied in each "
1193                    "dimension.\n"
1194                    "If the molecule rotates the actual distance will be smaller. You might want\n"
1195                    "to use a cubic box instead, or why not try a dodecahedron today?\n");
1196         }
1197     }
1198     if (bCONECT && (outftp == efPDB) && (inftp == efTPR))
1199     {
1200         conect = gmx_conect_generate(top);
1201     }
1202     else
1203     {
1204         conect = nullptr;
1205     }
1206
1207     if (bIndex)
1208     {
1209         fprintf(stderr, "\nSelect a group for output:\n");
1210         get_index(&atoms, opt2fn_null("-n", NFILE, fnm), 1, &isize, &index, &grpname);
1211
1212         if (resnr_start >= 0)
1213         {
1214             renum_resnr(&atoms, isize, index, resnr_start);
1215         }
1216
1217         if (opt2parg_bSet("-label", NPA, pa))
1218         {
1219             for (i = 0; (i < atoms.nr); i++)
1220             {
1221                 atoms.resinfo[atoms.atom[i].resind].chainid = label[0];
1222             }
1223         }
1224
1225         if (opt2bSet("-bf", NFILE, fnm) || bLegend)
1226         {
1227             gmx_fatal(FARGS, "Sorry, cannot do bfactors with an index group.");
1228         }
1229
1230         if (outftp == efPDB)
1231         {
1232             out = gmx_ffopen(outfile, "w");
1233             write_pdbfile_indexed(out, name, &atoms, x, ePBC, box, ' ', 1, isize, index, conect, FALSE);
1234             gmx_ffclose(out);
1235         }
1236         else
1237         {
1238             write_sto_conf_indexed(outfile, name, &atoms, x, bHaveV ? v : nullptr, ePBC, box, isize, index);
1239         }
1240         sfree(grpname);
1241         sfree(index);
1242     }
1243     else
1244     {
1245         if (resnr_start >= 0)
1246         {
1247             renum_resnr(&atoms, atoms.nr, nullptr, resnr_start);
1248         }
1249
1250         if ((outftp == efPDB) || (outftp == efPQR))
1251         {
1252             out = gmx_ffopen(outfile, "w");
1253             if (bMead)
1254             {
1255                 fprintf(out,
1256                         "REMARK    "
1257                         "The B-factors in this file hold atomic radii\n");
1258                 fprintf(out,
1259                         "REMARK    "
1260                         "The occupancy in this file hold atomic charges\n");
1261             }
1262             else if (bGrasp)
1263             {
1264                 fprintf(out, "GRASP PDB FILE\nFORMAT NUMBER=1\n");
1265                 fprintf(out,
1266                         "REMARK    "
1267                         "The B-factors in this file hold atomic charges\n");
1268                 fprintf(out,
1269                         "REMARK    "
1270                         "The occupancy in this file hold atomic radii\n");
1271             }
1272             else if (opt2bSet("-bf", NFILE, fnm))
1273             {
1274                 read_bfac(opt2fn("-bf", NFILE, fnm), &n_bfac, &bfac, &bfac_nr);
1275                 set_pdb_conf_bfac(atoms.nr, atoms.nres, &atoms, n_bfac, bfac, bfac_nr, peratom);
1276             }
1277             if (opt2parg_bSet("-label", NPA, pa))
1278             {
1279                 for (i = 0; (i < atoms.nr); i++)
1280                 {
1281                     atoms.resinfo[atoms.atom[i].resind].chainid = label[0];
1282                 }
1283             }
1284             /* Need to bypass the regular write_pdbfile because I don't want to change
1285              * all instances to include the boolean flag for writing out PQR files.
1286              */
1287             int* index;
1288             snew(index, atoms.nr);
1289             for (int i = 0; i < atoms.nr; i++)
1290             {
1291                 index[i] = i;
1292             }
1293             write_pdbfile_indexed(out, name, &atoms, x, ePBC, box, ' ', -1, atoms.nr, index, conect,
1294                                   outftp == efPQR);
1295             sfree(index);
1296             if (bLegend)
1297             {
1298                 pdb_legend(out, atoms.nr, atoms.nres, &atoms, x);
1299             }
1300             if (visbox[0] > 0)
1301             {
1302                 visualize_box(out, bLegend ? atoms.nr + 12 : atoms.nr,
1303                               bLegend ? atoms.nres = 12 : atoms.nres, box, visbox);
1304             }
1305             gmx_ffclose(out);
1306         }
1307         else
1308         {
1309             write_sto_conf(outfile, name, &atoms, x, bHaveV ? v : nullptr, ePBC, box);
1310         }
1311     }
1312     done_atom(&atoms);
1313     done_symtab(&symtab);
1314     sfree(name);
1315     if (x)
1316     {
1317         sfree(x);
1318     }
1319     if (v)
1320     {
1321         sfree(v);
1322     }
1323     do_view(oenv, outfile, nullptr);
1324     output_env_done(oenv);
1325
1326     return 0;
1327 }