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 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.
40 #include "gromacs/fileio/pdbio.h"
49 #include "gromacs/fileio/gmxfio.h"
50 #include "gromacs/math/units.h"
51 #include "gromacs/math/vec.h"
52 #include "gromacs/pbcutil/pbc.h"
53 #include "gromacs/topology/atomprop.h"
54 #include "gromacs/topology/ifunc.h"
55 #include "gromacs/topology/residuetypes.h"
56 #include "gromacs/topology/symtab.h"
57 #include "gromacs/topology/topology.h"
58 #include "gromacs/utility/coolstuff.h"
59 #include "gromacs/utility/cstringutil.h"
60 #include "gromacs/utility/fatalerror.h"
61 #include "gromacs/utility/futil.h"
62 #include "gromacs/utility/smalloc.h"
63 #include "gromacs/utility/snprintf.h"
70 typedef struct gmx_conect_t
74 gmx_conection_t
* conect
;
77 static const char* pdbtp
[epdbNR
] = { "ATOM ", "HETATM", "ANISOU", "CRYST1", "COMPND", "MODEL",
78 "ENDMDL", "TER", "HEADER", "TITLE", "REMARK", "CONECT" };
80 #define REMARK_SIM_BOX "REMARK THIS IS A SIMULATION BOX"
82 void gmx_write_pdb_box(FILE* out
, PbcType pbcType
, const matrix box
)
84 real alpha
, beta
, gamma
;
86 if (pbcType
== PbcType::Unset
)
88 pbcType
= guessPbcType(box
);
91 if (pbcType
== PbcType::No
)
96 if (norm2(box
[YY
]) * norm2(box
[ZZ
]) != 0)
98 alpha
= RAD2DEG
* gmx_angle(box
[YY
], box
[ZZ
]);
104 if (norm2(box
[XX
]) * norm2(box
[ZZ
]) != 0)
106 beta
= RAD2DEG
* gmx_angle(box
[XX
], box
[ZZ
]);
112 if (norm2(box
[XX
]) * norm2(box
[YY
]) != 0)
114 gamma
= RAD2DEG
* gmx_angle(box
[XX
], box
[YY
]);
120 fprintf(out
, "REMARK THIS IS A SIMULATION BOX\n");
121 if (pbcType
!= PbcType::Screw
)
123 fprintf(out
, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n", 10 * norm(box
[XX
]),
124 10 * norm(box
[YY
]), 10 * norm(box
[ZZ
]), alpha
, beta
, gamma
, "P 1", 1);
128 /* Double the a-vector length and write the correct space group */
129 fprintf(out
, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n", 20 * norm(box
[XX
]),
130 10 * norm(box
[YY
]), 10 * norm(box
[ZZ
]), alpha
, beta
, gamma
, "P 21 1 1", 1);
134 static void read_cryst1(char* line
, PbcType
* pbcType
, matrix box
)
137 char sa
[12], sb
[12], sc
[12], sg
[SG_SIZE
+ 1], ident
;
138 double fa
, fb
, fc
, alpha
, beta
, gamma
, cosa
, cosb
, cosg
, sing
;
139 int syma
, symb
, symc
;
142 sscanf(line
, "%*s%s%s%s%lf%lf%lf", sa
, sb
, sc
, &alpha
, &beta
, &gamma
);
144 pbcTypeFile
= PbcType::Unset
;
145 if (strlen(line
) >= 55)
147 strncpy(sg
, line
+ 55, SG_SIZE
);
153 sscanf(sg
, "%c %d %d %d", &ident
, &syma
, &symb
, &symc
);
154 if (ident
== 'P' && syma
== 1 && symb
<= 1 && symc
<= 1)
156 fc
= strtod(sc
, nullptr) * 0.1;
157 pbcTypeFile
= (fc
> 0 ? PbcType::Xyz
: PbcType::XY
);
159 if (ident
== 'P' && syma
== 21 && symb
== 1 && symc
== 1)
161 pbcTypeFile
= PbcType::Screw
;
166 *pbcType
= pbcTypeFile
;
171 fa
= strtod(sa
, nullptr) * 0.1;
172 fb
= strtod(sb
, nullptr) * 0.1;
173 fc
= strtod(sc
, nullptr) * 0.1;
174 if (pbcTypeFile
== PbcType::Screw
)
180 if ((alpha
!= 90.0) || (beta
!= 90.0) || (gamma
!= 90.0))
184 cosa
= std::cos(alpha
* DEG2RAD
);
192 cosb
= std::cos(beta
* DEG2RAD
);
200 cosg
= std::cos(gamma
* DEG2RAD
);
201 sing
= std::sin(gamma
* DEG2RAD
);
208 box
[YY
][XX
] = fb
* cosg
;
209 box
[YY
][YY
] = fb
* sing
;
210 box
[ZZ
][XX
] = fc
* cosb
;
211 box
[ZZ
][YY
] = fc
* (cosa
- cosb
* cosg
) / sing
;
212 box
[ZZ
][ZZ
] = std::sqrt(fc
* fc
- box
[ZZ
][XX
] * box
[ZZ
][XX
] - box
[ZZ
][YY
] * box
[ZZ
][YY
]);
222 static int gmx_fprintf_pqr_atomline(FILE* fp
,
223 enum PDB_record record
,
225 const char* atom_name
,
226 const char* res_name
,
235 GMX_RELEASE_ASSERT(record
== epdbATOM
|| record
== epdbHETATM
,
236 "Can only print PQR atom lines as ATOM or HETATM records");
238 /* Check atom name */
239 GMX_RELEASE_ASSERT(atom_name
!= nullptr, "Need atom information to print pqr");
241 /* Check residue name */
242 GMX_RELEASE_ASSERT(res_name
!= nullptr, "Need residue information to print pqr");
244 /* Truncate integers so they fit */
245 atom_seq_number
= atom_seq_number
% 100000;
246 res_seq_number
= res_seq_number
% 10000;
248 int n
= fprintf(fp
, "%-6s%5d %-4.4s%4.4s%c%4d %8.3f %8.3f %8.3f %6.2f %6.2f\n", pdbtp
[record
],
249 atom_seq_number
, atom_name
, res_name
, chain_id
, res_seq_number
, x
, y
, z
,
250 occupancy
, b_factor
);
255 void write_pdbfile_indexed(FILE* out
,
257 const t_atoms
* atoms
,
268 gmx_conect_t
* gc
= static_cast<gmx_conect_t
*>(conect
);
269 enum PDB_record type
;
275 fprintf(out
, "TITLE %s\n", (title
&& title
[0]) ? title
: gmx::bromacs().c_str());
276 if (box
&& ((norm2(box
[XX
]) != 0.0F
) || (norm2(box
[YY
]) != 0.0F
) || (norm2(box
[ZZ
]) != 0.0F
)))
278 gmx_write_pdb_box(out
, pbcType
, box
);
280 if (atoms
->havePdbInfo
)
282 /* Check whether any occupancies are set, in that case leave it as is,
283 * otherwise set them all to one
286 for (int ii
= 0; (ii
< nindex
) && bOccup
; ii
++)
289 bOccup
= bOccup
&& (atoms
->pdbinfo
[i
].occup
== 0.0);
297 fprintf(out
, "MODEL %8d\n", model_nr
> 0 ? model_nr
: 1);
300 for (int ii
= 0; ii
< nindex
; ii
++)
303 int resind
= atoms
->atom
[i
].resind
;
304 std::string resnm
= *atoms
->resinfo
[resind
].name
;
305 std::string nm
= *atoms
->atomname
[i
];
307 int resnr
= atoms
->resinfo
[resind
].nr
;
308 unsigned char resic
= atoms
->resinfo
[resind
].ic
;
316 ch
= atoms
->resinfo
[resind
].chainid
;
325 resnr
= resnr
% 10000;
328 if (atoms
->pdbinfo
!= nullptr)
330 pdbinfo
= atoms
->pdbinfo
[i
];
334 gmx_pdbinfo_init_default(&pdbinfo
);
336 type
= static_cast<enum PDB_record
>(pdbinfo
.type
);
337 altloc
= pdbinfo
.altloc
;
338 if (!isalnum(altloc
))
342 occup
= bOccup
? 1.0 : pdbinfo
.occup
;
346 gmx_fprintf_pdb_atomline(out
, type
, i
+ 1, nm
.c_str(), altloc
, resnm
.c_str(), ch
, resnr
,
347 resic
, 10 * x
[i
][XX
], 10 * x
[i
][YY
], 10 * x
[i
][ZZ
], occup
,
348 bfac
, atoms
->atom
[i
].elem
);
350 if (atoms
->pdbinfo
&& atoms
->pdbinfo
[i
].bAnisotropic
)
352 fprintf(out
, "ANISOU%5d %-4.4s%4.4s%c%4d%c %7d%7d%7d%7d%7d%7d\n", (i
+ 1) % 100000,
353 nm
.c_str(), resnm
.c_str(), ch
, resnr
, (resic
== '\0') ? ' ' : resic
,
354 atoms
->pdbinfo
[i
].uij
[0], atoms
->pdbinfo
[i
].uij
[1], atoms
->pdbinfo
[i
].uij
[2],
355 atoms
->pdbinfo
[i
].uij
[3], atoms
->pdbinfo
[i
].uij
[4], atoms
->pdbinfo
[i
].uij
[5]);
360 gmx_fprintf_pqr_atomline(out
, type
, i
+ 1, nm
.c_str(), resnm
.c_str(), ch
, resnr
,
361 10 * x
[i
][XX
], 10 * x
[i
][YY
], 10 * x
[i
][ZZ
], occup
, bfac
);
365 fprintf(out
, "TER\n");
366 fprintf(out
, "ENDMDL\n");
370 /* Write conect records */
371 for (int i
= 0; (i
< gc
->nconect
); i
++)
373 fprintf(out
, "CONECT%5d%5d\n", gc
->conect
[i
].ai
+ 1, gc
->conect
[i
].aj
+ 1);
378 void write_pdbfile(FILE* out
,
380 const t_atoms
* atoms
,
390 snew(index
, atoms
->nr
);
391 for (i
= 0; i
< atoms
->nr
; i
++)
395 write_pdbfile_indexed(out
, title
, atoms
, x
, pbcType
, box
, chainid
, model_nr
, atoms
->nr
, index
,
400 static int line2type(const char* line
)
405 for (k
= 0; (k
< 6); k
++)
411 for (k
= 0; (k
< epdbNR
); k
++)
413 if (std::strncmp(type
, pdbtp
[k
], strlen(pdbtp
[k
])) == 0)
422 static void read_anisou(char line
[], int natom
, t_atoms
* atoms
)
426 char anr
[12], anm
[12];
430 for (k
= 0; (k
< 5); k
++, j
++)
436 for (k
= 0; (k
< 4); k
++, j
++)
443 /* Strip off spaces */
446 /* Search backwards for number and name only */
447 atomnr
= std::strtol(anr
, nullptr, 10);
448 for (i
= natom
- 1; (i
>= 0); i
--)
450 if ((std::strcmp(anm
, *(atoms
->atomname
[i
])) == 0) && (atomnr
== atoms
->pdbinfo
[i
].atomnr
))
457 fprintf(stderr
, "Skipping ANISOU record (atom %s %d not found)\n", anm
, atomnr
);
461 if (sscanf(line
+ 29, "%d%d%d%d%d%d", &atoms
->pdbinfo
[i
].uij
[U11
], &atoms
->pdbinfo
[i
].uij
[U22
],
462 &atoms
->pdbinfo
[i
].uij
[U33
], &atoms
->pdbinfo
[i
].uij
[U12
],
463 &atoms
->pdbinfo
[i
].uij
[U13
], &atoms
->pdbinfo
[i
].uij
[U23
])
466 atoms
->pdbinfo
[i
].bAnisotropic
= TRUE
;
470 fprintf(stderr
, "Invalid ANISOU record for atom %d\n", i
);
471 atoms
->pdbinfo
[i
].bAnisotropic
= FALSE
;
476 void get_pdb_atomnumber(const t_atoms
* atoms
, AtomProperties
* aps
)
478 int i
, atomnumber
, len
;
480 char anm
[6], anm_copy
[6];
486 gmx_incons("Trying to deduce atomnumbers when no pdb information is present");
488 for (i
= 0; (i
< atoms
->nr
); i
++)
490 std::strcpy(anm
, atoms
->pdbinfo
[i
].atomnm
);
491 std::strcpy(anm_copy
, atoms
->pdbinfo
[i
].atomnm
);
492 bool atomNumberSet
= false;
494 if ((anm
[0] != ' ') && ((len
<= 2) || !std::isdigit(anm
[2])))
497 if (aps
->setAtomProperty(epropElement
, "???", anm_copy
, &eval
))
499 atomnumber
= gmx::roundToInt(eval
);
500 atomNumberSet
= true;
505 if (aps
->setAtomProperty(epropElement
, "???", anm_copy
, &eval
))
507 atomnumber
= gmx::roundToInt(eval
);
508 atomNumberSet
= true;
515 while ((k
< std::strlen(anm
)) && (std::isspace(anm
[k
]) || std::isdigit(anm
[k
])))
519 anm_copy
[0] = anm
[k
];
521 if (aps
->setAtomProperty(epropElement
, "???", anm_copy
, &eval
))
523 atomnumber
= gmx::roundToInt(eval
);
524 atomNumberSet
= true;
530 atoms
->atom
[i
].atomnumber
= atomnumber
;
531 buf
= aps
->elementFromAtomNumber(atomnumber
);
534 fprintf(debug
, "Atomnumber for atom '%s' is %d\n", anm
, atomnumber
);
538 std::strncpy(atoms
->atom
[i
].elem
, buf
.c_str(), 4);
542 static int read_atom(t_symtab
* symtab
, const char line
[], int type
, int natom
, t_atoms
* atoms
, rvec x
[], int chainnum
)
547 char anr
[12], anm
[12], anm_copy
[12], altloc
, resnm
[12], rnr
[12], elem
[3];
548 char xc
[12], yc
[12], zc
[12], occup
[12], bfac
[12];
551 int resnr
, atomnumber
;
553 if (natom
>= atoms
->nr
)
555 gmx_fatal(FARGS
, "\nFound more atoms (%d) in pdb file than expected (%d)", natom
+ 1, atoms
->nr
);
560 for (k
= 0; (k
< 5); k
++, j
++)
567 for (k
= 0; (k
< 4); k
++, j
++)
572 std::strcpy(anm_copy
, anm
);
578 for (k
= 0; (k
< 4); k
++, j
++)
588 for (k
= 0; (k
< 4); k
++, j
++)
594 resnr
= std::strtol(rnr
, nullptr, 10);
598 /* X,Y,Z Coordinate */
599 for (k
= 0; (k
< 8); k
++, j
++)
604 for (k
= 0; (k
< 8); k
++, j
++)
609 for (k
= 0; (k
< 8); k
++, j
++)
616 for (k
= 0; (k
< 6); k
++, j
++)
623 for (k
= 0; (k
< 7); k
++, j
++)
633 for (k
= 0; (k
< 2); k
++, j
++)
642 atomn
= &(atoms
->atom
[natom
]);
643 if ((natom
== 0) || atoms
->resinfo
[atoms
->atom
[natom
- 1].resind
].nr
!= resnr
644 || atoms
->resinfo
[atoms
->atom
[natom
- 1].resind
].ic
!= resic
645 || (strcmp(*atoms
->resinfo
[atoms
->atom
[natom
- 1].resind
].name
, resnm
) != 0))
653 atomn
->resind
= atoms
->atom
[natom
- 1].resind
+ 1;
655 atoms
->nres
= atomn
->resind
+ 1;
656 t_atoms_set_resinfo(atoms
, natom
, symtab
, resnm
, resnr
, resic
, chainnum
, chainid
);
660 atomn
->resind
= atoms
->atom
[natom
- 1].resind
;
662 atoms
->atomname
[natom
] = put_symtab(symtab
, anm
);
665 atomn
->atomnumber
= atomnumber
;
666 strncpy(atomn
->elem
, elem
, 4);
668 x
[natom
][XX
] = strtod(xc
, nullptr) * 0.1;
669 x
[natom
][YY
] = strtod(yc
, nullptr) * 0.1;
670 x
[natom
][ZZ
] = strtod(zc
, nullptr) * 0.1;
673 atoms
->pdbinfo
[natom
].type
= type
;
674 atoms
->pdbinfo
[natom
].atomnr
= strtol(anr
, nullptr, 10);
675 atoms
->pdbinfo
[natom
].altloc
= altloc
;
676 strcpy(atoms
->pdbinfo
[natom
].atomnm
, anm_copy
);
677 atoms
->pdbinfo
[natom
].bfac
= strtod(bfac
, nullptr);
678 atoms
->pdbinfo
[natom
].occup
= strtod(occup
, nullptr);
685 gmx_bool
is_hydrogen(const char* nm
)
689 std::strcpy(buf
, nm
);
692 return ((buf
[0] == 'H') || ((std::isdigit(buf
[0]) != 0) && (buf
[1] == 'H')));
695 gmx_bool
is_dummymass(const char* nm
)
699 std::strcpy(buf
, nm
);
702 return (buf
[0] == 'M') && (std::isdigit(buf
[strlen(buf
) - 1]) != 0);
705 static void gmx_conect_addline(gmx_conect_t
* con
, char* line
)
709 std::string form2
= "%*s";
710 std::string format
= form2
+ "%d";
711 if (sscanf(line
, format
.c_str(), &ai
) == 1)
716 format
= form2
+ "%d";
717 n
= sscanf(line
, format
.c_str(), &aj
);
720 gmx_conect_add(con
, ai
- 1, aj
- 1); /* to prevent duplicated records */
726 void gmx_conect_dump(FILE* fp
, gmx_conect conect
)
728 gmx_conect_t
* gc
= static_cast<gmx_conect_t
*>(conect
);
731 for (i
= 0; (i
< gc
->nconect
); i
++)
733 fprintf(fp
, "%6s%5d%5d\n", "CONECT", gc
->conect
[i
].ai
+ 1, gc
->conect
[i
].aj
+ 1);
737 gmx_conect
gmx_conect_init()
746 void gmx_conect_done(gmx_conect conect
)
748 gmx_conect_t
* gc
= conect
;
753 gmx_bool
gmx_conect_exist(gmx_conect conect
, int ai
, int aj
)
755 gmx_conect_t
* gc
= conect
;
761 for (i
= 0; (i
< gc
->nconect
); i
++)
763 if (((gc
->conect
[i
].ai
== ai
) && (gc
->conect
[i
].aj
== aj
))
764 || ((gc
->conect
[i
].aj
== ai
) && (gc
->conect
[i
].ai
== aj
)))
772 void gmx_conect_add(gmx_conect conect
, int ai
, int aj
)
774 gmx_conect_t
* gc
= static_cast<gmx_conect_t
*>(conect
);
779 if (!gmx_conect_exist(conect
, ai
, aj
))
781 srenew(gc
->conect
, ++gc
->nconect
);
782 gc
->conect
[gc
->nconect
- 1].ai
= ai
;
783 gc
->conect
[gc
->nconect
- 1].aj
= aj
;
787 int read_pdbfile(FILE* in
,
797 gmx_conect_t
* gc
= conect
;
799 gmx_bool bConnWarn
= FALSE
;
800 char line
[STRLEN
+ 1];
804 gmx_bool bStop
= FALSE
;
808 /* Only assume pbc when there is a CRYST1 entry */
809 *pbcType
= PbcType::No
;
816 atoms
->haveMass
= FALSE
;
817 atoms
->haveCharge
= FALSE
;
818 atoms
->haveType
= FALSE
;
819 atoms
->haveBState
= FALSE
;
820 atoms
->havePdbInfo
= (atoms
->pdbinfo
!= nullptr);
826 while (!bStop
&& (fgets2(line
, STRLEN
, in
) != nullptr))
828 line_type
= line2type(line
);
834 natom
= read_atom(symtab
, line
, line_type
, natom
, atoms
, x
, chainnum
);
838 if (atoms
->havePdbInfo
)
840 read_anisou(line
, natom
, atoms
);
844 case epdbCRYST1
: read_cryst1(line
, pbcType
, box
); break;
848 if (std::strlen(line
) > 6)
851 /* skip HEADER or TITLE and spaces */
860 /* truncate after title */
861 d
= std::strstr(c
, " ");
866 if (std::strlen(c
) > 0)
868 std::strcpy(title
, c
);
874 if ((!std::strstr(line
, ": ")) || (std::strstr(line
+ 6, "MOLECULE:")))
876 if (!(c
= std::strstr(line
+ 6, "MOLECULE:")))
880 /* skip 'MOLECULE:' and spaces */
889 /* truncate after title */
893 while ((d
[-1] == ';') && d
> c
)
903 std::strcat(title
, "; ");
904 std::strcat(title
, c
);
908 std::strcpy(title
, c
);
915 case epdbTER
: chainnum
++; break;
920 sscanf(line
, "%*s%d", model_nr
);
924 case epdbENDMDL
: bStop
= TRUE
; break;
928 gmx_conect_addline(gc
, line
);
932 fprintf(stderr
, "WARNING: all CONECT records are ignored\n");
944 void get_pdb_coordnum(FILE* in
, int* natoms
)
949 while (fgets2(line
, STRLEN
, in
))
951 if (std::strncmp(line
, "ENDMDL", 6) == 0)
955 if ((std::strncmp(line
, "ATOM ", 6) == 0) || (std::strncmp(line
, "HETATM", 6) == 0))
962 void gmx_pdb_read_conf(const char* infile
, t_symtab
* symtab
, char** name
, t_atoms
* atoms
, rvec x
[], PbcType
* pbcType
, matrix box
)
964 FILE* in
= gmx_fio_fopen(infile
, "r");
966 read_pdbfile(in
, title
, nullptr, atoms
, symtab
, x
, pbcType
, box
, nullptr);
969 *name
= gmx_strdup(title
);
974 gmx_conect
gmx_conect_generate(const t_topology
* top
)
979 /* Fill the conect records */
980 gc
= gmx_conect_init();
982 for (f
= 0; (f
< F_NRE
); f
++)
986 for (i
= 0; (i
< top
->idef
.il
[f
].nr
); i
+= interaction_function
[f
].nratoms
+ 1)
988 gmx_conect_add(gc
, top
->idef
.il
[f
].iatoms
[i
+ 1], top
->idef
.il
[f
].iatoms
[i
+ 2]);
995 int gmx_fprintf_pdb_atomline(FILE* fp
,
996 enum PDB_record record
,
998 const char* atom_name
,
999 char alternate_location
,
1000 const char* res_name
,
1003 char res_insertion_code
,
1009 const char* element
)
1011 char tmp_atomname
[6], tmp_resname
[6];
1012 gmx_bool start_name_in_col13
;
1015 if (record
!= epdbATOM
&& record
!= epdbHETATM
)
1017 gmx_fatal(FARGS
, "Can only print PDB atom lines as ATOM or HETATM records");
1020 /* Format atom name */
1021 if (atom_name
!= nullptr)
1023 /* If the atom name is an element name with two chars, it should start already in column 13.
1024 * Otherwise it should start in column 14, unless the name length is 4 chars.
1026 if ((element
!= nullptr) && (std::strlen(element
) >= 2)
1027 && (gmx_strncasecmp(atom_name
, element
, 2) == 0))
1029 start_name_in_col13
= TRUE
;
1033 start_name_in_col13
= (std::strlen(atom_name
) >= 4);
1035 snprintf(tmp_atomname
, sizeof(tmp_atomname
), start_name_in_col13
? "" : " ");
1036 std::strncat(tmp_atomname
, atom_name
, 4);
1037 tmp_atomname
[5] = '\0';
1041 tmp_atomname
[0] = '\0';
1044 /* Format residue name */
1045 std::strncpy(tmp_resname
, (res_name
!= nullptr) ? res_name
: "", 4);
1046 /* Make sure the string is terminated if strlen was > 4 */
1047 tmp_resname
[4] = '\0';
1048 /* String is properly terminated, so now we can use strcat. By adding a
1049 * space we can write it right-justified, and if the original name was
1050 * three characters or less there will be a space added on the right side.
1052 std::strcat(tmp_resname
, " ");
1054 /* Truncate integers so they fit */
1055 atom_seq_number
= atom_seq_number
% 100000;
1056 res_seq_number
= res_seq_number
% 10000;
1058 n
= fprintf(fp
, "%-6s%5d %-4.4s%c%4.4s%c%4d%c %8.3f%8.3f%8.3f%6.2f%6.2f %2s\n",
1059 pdbtp
[record
], atom_seq_number
, tmp_atomname
, alternate_location
, tmp_resname
,
1060 chain_id
, res_seq_number
, res_insertion_code
, x
, y
, z
, occupancy
, b_factor
,
1061 (element
!= nullptr) ? element
: "");