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,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.
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.
48 #include "gromacs/fileio/gmxfio.h"
49 #include "gromacs/math/units.h"
50 #include "gromacs/math/vec.h"
51 #include "gromacs/pbcutil/pbc.h"
52 #include "gromacs/topology/atomprop.h"
53 #include "gromacs/topology/ifunc.h"
54 #include "gromacs/topology/residuetypes.h"
55 #include "gromacs/topology/symtab.h"
56 #include "gromacs/topology/topology.h"
57 #include "gromacs/utility/coolstuff.h"
58 #include "gromacs/utility/cstringutil.h"
59 #include "gromacs/utility/fatalerror.h"
60 #include "gromacs/utility/futil.h"
61 #include "gromacs/utility/smalloc.h"
62 #include "gromacs/utility/snprintf.h"
68 typedef struct gmx_conect_t {
71 gmx_conection_t *conect;
74 static const char *pdbtp[epdbNR] = {
75 "ATOM ", "HETATM", "ANISOU", "CRYST1",
76 "COMPND", "MODEL", "ENDMDL", "TER", "HEADER", "TITLE", "REMARK",
80 #define REMARK_SIM_BOX "REMARK THIS IS A SIMULATION BOX"
82 static void xlate_atomname_pdb2gmx(char *name)
87 length = std::strlen(name);
88 if (length > 3 && std::isdigit(name[0]))
91 for (i = 1; i < length; i++)
95 name[length-1] = temp;
99 // Deliberately taking a copy of name to return it later
100 static std::string xlate_atomname_gmx2pdb(std::string name)
102 size_t length = name.size();
103 if (length > 3 && std::isdigit(name[length-1]))
105 char temp = name[length-1];
106 for (size_t i = length-1; i > 0; --i)
116 void gmx_write_pdb_box(FILE *out, int ePBC, const matrix box)
118 real alpha, beta, gamma;
122 ePBC = guess_ePBC(box);
125 if (ePBC == epbcNONE)
130 if (norm2(box[YY])*norm2(box[ZZ]) != 0)
132 alpha = RAD2DEG*gmx_angle(box[YY], box[ZZ]);
138 if (norm2(box[XX])*norm2(box[ZZ]) != 0)
140 beta = RAD2DEG*gmx_angle(box[XX], box[ZZ]);
146 if (norm2(box[XX])*norm2(box[YY]) != 0)
148 gamma = RAD2DEG*gmx_angle(box[XX], box[YY]);
154 fprintf(out, "REMARK THIS IS A SIMULATION BOX\n");
155 if (ePBC != epbcSCREW)
157 fprintf(out, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n",
158 10*norm(box[XX]), 10*norm(box[YY]), 10*norm(box[ZZ]),
159 alpha, beta, gamma, "P 1", 1);
163 /* Double the a-vector length and write the correct space group */
164 fprintf(out, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n",
165 20*norm(box[XX]), 10*norm(box[YY]), 10*norm(box[ZZ]),
166 alpha, beta, gamma, "P 21 1 1", 1);
171 static void read_cryst1(char *line, int *ePBC, matrix box)
174 char sa[12], sb[12], sc[12], sg[SG_SIZE+1], ident;
175 double fa, fb, fc, alpha, beta, gamma, cosa, cosb, cosg, sing;
176 int syma, symb, symc;
179 sscanf(line, "%*s%s%s%s%lf%lf%lf", sa, sb, sc, &alpha, &beta, &gamma);
182 if (strlen(line) >= 55)
184 strncpy(sg, line+55, SG_SIZE);
190 sscanf(sg, "%c %d %d %d", &ident, &syma, &symb, &symc);
191 if (ident == 'P' && syma == 1 && symb <= 1 && symc <= 1)
193 fc = strtod(sc, nullptr)*0.1;
194 ePBC_file = (fc > 0 ? epbcXYZ : epbcXY);
196 if (ident == 'P' && syma == 21 && symb == 1 && symc == 1)
198 ePBC_file = epbcSCREW;
208 fa = strtod(sa, nullptr)*0.1;
209 fb = strtod(sb, nullptr)*0.1;
210 fc = strtod(sc, nullptr)*0.1;
211 if (ePBC_file == epbcSCREW)
217 if ((alpha != 90.0) || (beta != 90.0) || (gamma != 90.0))
221 cosa = std::cos(alpha*DEG2RAD);
229 cosb = std::cos(beta*DEG2RAD);
237 cosg = std::cos(gamma*DEG2RAD);
238 sing = std::sin(gamma*DEG2RAD);
245 box[YY][XX] = fb*cosg;
246 box[YY][YY] = fb*sing;
247 box[ZZ][XX] = fc*cosb;
248 box[ZZ][YY] = fc*(cosa - cosb*cosg)/sing;
249 box[ZZ][ZZ] = std::sqrt(fc*fc
250 - box[ZZ][XX]*box[ZZ][XX] - box[ZZ][YY]*box[ZZ][YY]);
261 gmx_fprintf_pqr_atomline(FILE * fp,
262 enum PDB_record record,
264 const char * atom_name,
265 const char * res_name,
274 GMX_RELEASE_ASSERT(record == epdbATOM || record == epdbHETATM,
275 "Can only print PQR atom lines as ATOM or HETATM records");
277 /* Check atom name */
278 GMX_RELEASE_ASSERT(atom_name != nullptr,
279 "Need atom information to print pqr");
281 /* Check residue name */
282 GMX_RELEASE_ASSERT(res_name != nullptr,
283 "Need residue information to print pqr");
285 /* Truncate integers so they fit */
286 atom_seq_number = atom_seq_number % 100000;
287 res_seq_number = res_seq_number % 10000;
290 "%s %d %s %s %c %d %8.3f %8.3f %8.3f %6.2f %6.2f\n",
304 void write_pdbfile_indexed(FILE *out, const char *title,
305 const t_atoms *atoms, const rvec x[],
306 int ePBC, const matrix box, char chainid,
307 int model_nr, int nindex, const int index[],
308 gmx_conect conect, gmx_bool bTerSepChains,
311 gmx_conect_t *gc = static_cast<gmx_conect_t *>(conect);
312 enum PDB_record type;
318 fprintf(out, "TITLE %s\n", (title && title[0]) ? title : gmx::bromacs().c_str());
319 if (box && ( (norm2(box[XX]) != 0.0f) || (norm2(box[YY]) != 0.0f) || (norm2(box[ZZ]) != 0.0f) ) )
321 gmx_write_pdb_box(out, ePBC, box);
323 if (atoms->havePdbInfo)
325 /* Check whether any occupancies are set, in that case leave it as is,
326 * otherwise set them all to one
329 for (int ii = 0; (ii < nindex) && bOccup; ii++)
332 bOccup = bOccup && (atoms->pdbinfo[i].occup == 0.0);
340 fprintf(out, "MODEL %8d\n", model_nr > 0 ? model_nr : 1);
342 int lastchainnum = -1;
343 std::string prevRestype;
344 std::string lastRestype;
347 for (int ii = 0; ii < nindex; ii++)
350 int resind = atoms->atom[i].resind;
351 int chainnum = atoms->resinfo[resind].chainnum;
352 lastRestype = prevRestype;
353 prevRestype = rt.typeNameForIndexedResidue(*atoms->resinfo[resind].name);
355 /* Add a TER record if we changed chain, and if either the previous or this chain is protein/DNA/RNA. */
356 if (bTerSepChains && ii > 0 && chainnum != lastchainnum)
358 /* Only add TER if the previous chain contained protein/DNA/RNA. */
359 if (rt.namedResidueHasType(lastRestype, "Protein") ||
360 rt.namedResidueHasType(lastRestype, "DNA") ||
361 rt.namedResidueHasType(lastRestype, "RNA"))
363 fprintf(out, "TER\n");
365 lastchainnum = chainnum;
368 std::string resnm = *atoms->resinfo[resind].name;
369 std::string nm = *atoms->atomname[i];
371 /* rename HG12 to 2HG1, etc. */
372 nm = xlate_atomname_gmx2pdb(nm);
373 int resnr = atoms->resinfo[resind].nr;
374 unsigned char resic = atoms->resinfo[resind].ic;
382 ch = atoms->resinfo[resind].chainid;
391 resnr = resnr % 10000;
394 if (atoms->pdbinfo != nullptr)
396 pdbinfo = atoms->pdbinfo[i];
400 gmx_pdbinfo_init_default(&pdbinfo);
402 type = static_cast<enum PDB_record>(pdbinfo.type);
403 altloc = pdbinfo.altloc;
404 if (!isalnum(altloc))
408 occup = bOccup ? 1.0 : pdbinfo.occup;
412 gmx_fprintf_pdb_atomline(out,
421 10*x[i][XX], 10*x[i][YY], 10*x[i][ZZ],
424 atoms->atom[i].elem);
426 if (atoms->pdbinfo && atoms->pdbinfo[i].bAnisotropic)
428 fprintf(out, "ANISOU%5d %-4.4s%4.4s%c%4d%c %7d%7d%7d%7d%7d%7d\n",
429 (i+1)%100000, nm.c_str(), resnm.c_str(), ch, resnr,
430 (resic == '\0') ? ' ' : resic,
431 atoms->pdbinfo[i].uij[0], atoms->pdbinfo[i].uij[1],
432 atoms->pdbinfo[i].uij[2], atoms->pdbinfo[i].uij[3],
433 atoms->pdbinfo[i].uij[4], atoms->pdbinfo[i].uij[5]);
438 gmx_fprintf_pqr_atomline(out,
445 10*x[i][XX], 10*x[i][YY], 10*x[i][ZZ],
451 fprintf(out, "TER\n");
452 fprintf(out, "ENDMDL\n");
456 /* Write conect records */
457 for (int i = 0; (i < gc->nconect); i++)
459 fprintf(out, "CONECT%5d%5d\n", gc->conect[i].ai+1, gc->conect[i].aj+1);
464 void write_pdbfile(FILE *out, const char *title, const t_atoms *atoms, const rvec x[],
465 int ePBC, const matrix box, char chainid, int model_nr, gmx_conect conect, gmx_bool bTerSepChains)
469 snew(index, atoms->nr);
470 for (i = 0; i < atoms->nr; i++)
474 write_pdbfile_indexed(out, title, atoms, x, ePBC, box, chainid, model_nr,
475 atoms->nr, index, conect, bTerSepChains, false);
479 static int line2type(const char *line)
484 for (k = 0; (k < 6); k++)
490 for (k = 0; (k < epdbNR); k++)
492 if (std::strncmp(type, pdbtp[k], strlen(pdbtp[k])) == 0)
501 static void read_anisou(char line[], int natom, t_atoms *atoms)
505 char anr[12], anm[12];
509 for (k = 0; (k < 5); k++, j++)
515 for (k = 0; (k < 4); k++, j++)
522 /* Strip off spaces */
525 /* Search backwards for number and name only */
526 atomnr = std::strtol(anr, nullptr, 10);
527 for (i = natom-1; (i >= 0); i--)
529 if ((std::strcmp(anm, *(atoms->atomname[i])) == 0) &&
530 (atomnr == atoms->pdbinfo[i].atomnr))
537 fprintf(stderr, "Skipping ANISOU record (atom %s %d not found)\n",
542 if (sscanf(line+29, "%d%d%d%d%d%d",
543 &atoms->pdbinfo[i].uij[U11], &atoms->pdbinfo[i].uij[U22],
544 &atoms->pdbinfo[i].uij[U33], &atoms->pdbinfo[i].uij[U12],
545 &atoms->pdbinfo[i].uij[U13], &atoms->pdbinfo[i].uij[U23])
548 atoms->pdbinfo[i].bAnisotropic = TRUE;
552 fprintf(stderr, "Invalid ANISOU record for atom %d\n", i);
553 atoms->pdbinfo[i].bAnisotropic = FALSE;
558 void get_pdb_atomnumber(const t_atoms *atoms, AtomProperties *aps)
560 int i, atomnumber, len;
562 char anm[6], anm_copy[6];
568 gmx_incons("Trying to deduce atomnumbers when no pdb information is present");
570 for (i = 0; (i < atoms->nr); i++)
572 std::strcpy(anm, atoms->pdbinfo[i].atomnm);
573 std::strcpy(anm_copy, atoms->pdbinfo[i].atomnm);
574 bool atomNumberSet = false;
576 if ((anm[0] != ' ') && ((len <= 2) || !std::isdigit(anm[2])))
579 if (aps->setAtomProperty(epropElement, "???", anm_copy, &eval))
581 atomnumber = gmx::roundToInt(eval);
582 atomNumberSet = true;
587 if (aps->setAtomProperty(epropElement, "???", anm_copy, &eval))
589 atomnumber = gmx::roundToInt(eval);
590 atomNumberSet = true;
597 while ((k < std::strlen(anm)) && (std::isspace(anm[k]) || std::isdigit(anm[k])))
601 anm_copy[0] = anm[k];
603 if (aps->setAtomProperty(epropElement, "???", anm_copy, &eval))
605 atomnumber = gmx::roundToInt(eval);
606 atomNumberSet = true;
612 atoms->atom[i].atomnumber = atomnumber;
613 buf = aps->elementFromAtomNumber(atomnumber);
616 fprintf(debug, "Atomnumber for atom '%s' is %d\n",
621 std::strncpy(atoms->atom[i].elem, buf.c_str(), 4);
625 static int read_atom(t_symtab *symtab,
626 const char line[], int type, int natom,
627 t_atoms *atoms, rvec x[], int chainnum, gmx_bool bChange)
632 char anr[12], anm[12], anm_copy[12], altloc, resnm[12], rnr[12], elem[3];
633 char xc[12], yc[12], zc[12], occup[12], bfac[12];
636 int resnr, atomnumber;
638 if (natom >= atoms->nr)
640 gmx_fatal(FARGS, "\nFound more atoms (%d) in pdb file than expected (%d)",
646 for (k = 0; (k < 5); k++, j++)
653 for (k = 0; (k < 4); k++, j++)
658 std::strcpy(anm_copy, anm);
664 for (k = 0; (k < 4); k++, j++)
674 for (k = 0; (k < 4); k++, j++)
680 resnr = std::strtol(rnr, nullptr, 10);
684 /* X,Y,Z Coordinate */
685 for (k = 0; (k < 8); k++, j++)
690 for (k = 0; (k < 8); k++, j++)
695 for (k = 0; (k < 8); k++, j++)
702 for (k = 0; (k < 6); k++, j++)
709 for (k = 0; (k < 7); k++, j++)
719 for (k = 0; (k < 2); k++, j++)
728 atomn = &(atoms->atom[natom]);
730 atoms->resinfo[atoms->atom[natom-1].resind].nr != resnr ||
731 atoms->resinfo[atoms->atom[natom-1].resind].ic != resic ||
732 (strcmp(*atoms->resinfo[atoms->atom[natom-1].resind].name, resnm) != 0))
740 atomn->resind = atoms->atom[natom-1].resind + 1;
742 atoms->nres = atomn->resind + 1;
743 t_atoms_set_resinfo(atoms, natom, symtab, resnm, resnr, resic, chainnum, chainid);
747 atomn->resind = atoms->atom[natom-1].resind;
751 xlate_atomname_pdb2gmx(anm);
753 atoms->atomname[natom] = put_symtab(symtab, anm);
756 atomn->atomnumber = atomnumber;
757 strncpy(atomn->elem, elem, 4);
759 x[natom][XX] = strtod(xc, nullptr)*0.1;
760 x[natom][YY] = strtod(yc, nullptr)*0.1;
761 x[natom][ZZ] = strtod(zc, nullptr)*0.1;
764 atoms->pdbinfo[natom].type = type;
765 atoms->pdbinfo[natom].atomnr = strtol(anr, nullptr, 10);
766 atoms->pdbinfo[natom].altloc = altloc;
767 strcpy(atoms->pdbinfo[natom].atomnm, anm_copy);
768 atoms->pdbinfo[natom].bfac = strtod(bfac, nullptr);
769 atoms->pdbinfo[natom].occup = strtod(occup, nullptr);
776 gmx_bool is_hydrogen(const char *nm)
780 std::strcpy(buf, nm);
787 else if ((std::isdigit(buf[0])) && (buf[1] == 'H'))
794 gmx_bool is_dummymass(const char *nm)
798 std::strcpy(buf, nm);
801 return (buf[0] == 'M') && (std::isdigit(buf[strlen(buf)-1]) != 0);
804 static void gmx_conect_addline(gmx_conect_t *con, char *line)
808 std::string form2 = "%%*s";
809 std::string format = form2 + "%%d";
810 if (sscanf(line, format.c_str(), &ai) == 1)
815 format = form2 + "%%d";
816 n = sscanf(line, format.c_str(), &aj);
819 srenew(con->conect, ++con->nconect);
820 con->conect[con->nconect-1].ai = ai-1;
821 con->conect[con->nconect-1].aj = aj-1;
828 void gmx_conect_dump(FILE *fp, gmx_conect conect)
830 gmx_conect_t *gc = static_cast<gmx_conect_t *>(conect);
833 for (i = 0; (i < gc->nconect); i++)
835 fprintf(fp, "%6s%5d%5d\n", "CONECT",
836 gc->conect[i].ai+1, gc->conect[i].aj+1);
840 gmx_conect gmx_conect_init()
849 void gmx_conect_done(gmx_conect conect)
851 gmx_conect_t *gc = conect;
856 gmx_bool gmx_conect_exist(gmx_conect conect, int ai, int aj)
858 gmx_conect_t *gc = conect;
864 for (i = 0; (i < gc->nconect); i++)
866 if (((gc->conect[i].ai == ai) &&
867 (gc->conect[i].aj == aj)) ||
868 ((gc->conect[i].aj == ai) &&
869 (gc->conect[i].ai == aj)))
877 void gmx_conect_add(gmx_conect conect, int ai, int aj)
879 gmx_conect_t *gc = static_cast<gmx_conect_t *>(conect);
884 if (!gmx_conect_exist(conect, ai, aj))
886 srenew(gc->conect, ++gc->nconect);
887 gc->conect[gc->nconect-1].ai = ai;
888 gc->conect[gc->nconect-1].aj = aj;
892 int read_pdbfile(FILE *in, char *title, int *model_nr,
893 t_atoms *atoms, t_symtab *symtab, rvec x[], int *ePBC,
894 matrix box, gmx_bool bChange, gmx_conect conect)
896 gmx_conect_t *gc = conect;
898 gmx_bool bConnWarn = FALSE;
903 gmx_bool bStop = FALSE;
907 /* Only assume pbc when there is a CRYST1 entry */
915 atoms->haveMass = FALSE;
916 atoms->haveCharge = FALSE;
917 atoms->haveType = FALSE;
918 atoms->haveBState = FALSE;
919 atoms->havePdbInfo = (atoms->pdbinfo != nullptr);
925 while (!bStop && (fgets2(line, STRLEN, in) != nullptr))
927 line_type = line2type(line);
933 natom = read_atom(symtab, line, line_type, natom, atoms, x, chainnum, bChange);
937 if (atoms->havePdbInfo)
939 read_anisou(line, natom, atoms);
944 read_cryst1(line, ePBC, box);
949 if (std::strlen(line) > 6)
952 /* skip HEADER or TITLE and spaces */
961 /* truncate after title */
962 d = std::strstr(c, " ");
967 if (std::strlen(c) > 0)
969 std::strcpy(title, c);
975 if ((!std::strstr(line, ": ")) || (std::strstr(line+6, "MOLECULE:")))
977 if (!(c = std::strstr(line+6, "MOLECULE:")) )
981 /* skip 'MOLECULE:' and spaces */
990 /* truncate after title */
994 while ( (d[-1] == ';') && d > c)
1004 std::strcat(title, "; ");
1005 std::strcat(title, c);
1009 std::strcpy(title, c);
1023 sscanf(line, "%*s%d", model_nr);
1033 gmx_conect_addline(gc, line);
1035 else if (!bConnWarn)
1037 fprintf(stderr, "WARNING: all CONECT records are ignored\n");
1050 void get_pdb_coordnum(FILE *in, int *natoms)
1055 while (fgets2(line, STRLEN, in))
1057 if (std::strncmp(line, "ENDMDL", 6) == 0)
1061 if ((std::strncmp(line, "ATOM ", 6) == 0) || (std::strncmp(line, "HETATM", 6) == 0))
1068 void gmx_pdb_read_conf(const char *infile,
1069 t_symtab *symtab, char **name, t_atoms *atoms,
1070 rvec x[], int *ePBC, matrix box)
1072 FILE *in = gmx_fio_fopen(infile, "r");
1074 read_pdbfile(in, title, nullptr, atoms, symtab, x, ePBC, box, TRUE, nullptr);
1075 if (name != nullptr)
1077 *name = gmx_strdup(title);
1082 gmx_conect gmx_conect_generate(const t_topology *top)
1087 /* Fill the conect records */
1088 gc = gmx_conect_init();
1090 for (f = 0; (f < F_NRE); f++)
1094 for (i = 0; (i < top->idef.il[f].nr); i += interaction_function[f].nratoms+1)
1096 gmx_conect_add(gc, top->idef.il[f].iatoms[i+1],
1097 top->idef.il[f].iatoms[i+2]);
1105 gmx_fprintf_pdb_atomline(FILE * fp,
1106 enum PDB_record record,
1107 int atom_seq_number,
1108 const char * atom_name,
1109 char alternate_location,
1110 const char * res_name,
1113 char res_insertion_code,
1119 const char * element)
1121 char tmp_atomname[6], tmp_resname[6];
1122 gmx_bool start_name_in_col13;
1125 if (record != epdbATOM && record != epdbHETATM)
1127 gmx_fatal(FARGS, "Can only print PDB atom lines as ATOM or HETATM records");
1130 /* Format atom name */
1131 if (atom_name != nullptr)
1133 /* If the atom name is an element name with two chars, it should start already in column 13.
1134 * Otherwise it should start in column 14, unless the name length is 4 chars.
1136 if ( (element != nullptr) && (std::strlen(element) >= 2) && (gmx_strncasecmp(atom_name, element, 2) == 0) )
1138 start_name_in_col13 = TRUE;
1142 start_name_in_col13 = (std::strlen(atom_name) >= 4);
1144 snprintf(tmp_atomname, sizeof(tmp_atomname), start_name_in_col13 ? "" : " ");
1145 std::strncat(tmp_atomname, atom_name, 4);
1146 tmp_atomname[5] = '\0';
1150 tmp_atomname[0] = '\0';
1153 /* Format residue name */
1154 std::strncpy(tmp_resname, (res_name != nullptr) ? res_name : "", 4);
1155 /* Make sure the string is terminated if strlen was > 4 */
1156 tmp_resname[4] = '\0';
1157 /* String is properly terminated, so now we can use strcat. By adding a
1158 * space we can write it right-justified, and if the original name was
1159 * three characters or less there will be a space added on the right side.
1161 std::strcat(tmp_resname, " ");
1163 /* Truncate integers so they fit */
1164 atom_seq_number = atom_seq_number % 100000;
1165 res_seq_number = res_seq_number % 10000;
1168 "%-6s%5d %-4.4s%c%4.4s%c%4d%c %8.3f%8.3f%8.3f%6.2f%6.2f %2s\n",
1180 (element != nullptr) ? element : "");