Add DSSP v4 support to do_dssp
[alexxy/gromacs.git] / src / gromacs / fileio / pdbio.cpp
index aa070856c64c43f172127cfd4e5bb1b77e6518ee..c3c3995039774070b923690aa6330de820617a77 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -36,8 +37,9 @@
  */
 #include "gmxpre.h"
 
-#include "pdbio.h"
+#include "gromacs/fileio/pdbio.h"
 
+#include <algorithm>
 #include <cctype>
 #include <cmath>
 #include <cstdlib>
 
 #include "gromacs/fileio/gmxfio.h"
 #include "gromacs/math/units.h"
+#include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/atomprop.h"
 #include "gromacs/topology/ifunc.h"
-#include "gromacs/topology/residuetypes.h"
 #include "gromacs/topology/symtab.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/utility/coolstuff.h"
 #include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/futil.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/snprintf.h"
+#include "gromacs/utility/stringtoenumvalueconverter.h"
 
-typedef struct {
+typedef struct
+{
     int ai, aj;
 } gmx_conection_t;
 
-typedef struct gmx_conect_t {
+typedef struct gmx_conect_t
+{
     int              nconect;
     gmx_bool         bSorted;
-    gmx_conection_t *conect;
+    gmx_conection_tconect;
 } gmx_conect_t;
 
-static const char *pdbtp[epdbNR] = {
-    "ATOM  ", "HETATM", "ANISOU", "CRYST1",
-    "COMPND", "MODEL", "ENDMDL", "TER", "HEADER", "TITLE", "REMARK",
-    "CONECT"
-};
-
-
-#define REMARK_SIM_BOX "REMARK    THIS IS A SIMULATION BOX"
-
-static void xlate_atomname_pdb2gmx(char *name)
+const char* enumValueToString(PdbRecordType enumValue)
 {
-    int  i, length;
-    char temp;
-
-    length = std::strlen(name);
-    if (length > 3 && std::isdigit(name[0]))
-    {
-        temp = name[0];
-        for (i = 1; i < length; i++)
-        {
-            name[i-1] = name[i];
-        }
-        name[length-1] = temp;
-    }
+    static constexpr gmx::EnumerationArray<PdbRecordType, const char*> pdbRecordTypeName = {
+        "ATOM  ", "HETATM", "ANISOU", "CRYST1", "COMPND", "MODEL",
+        "ENDMDL", "TER",    "HEADER", "TITLE",  "REMARK", "CONECT"
+    };
+    return pdbRecordTypeName[enumValue];
 }
 
-static void xlate_atomname_gmx2pdb(char *name)
-{
-    int  i, length;
-    char temp;
-
-    length = std::strlen(name);
-    if (length > 3 && std::isdigit(name[length-1]))
-    {
-        temp = name[length-1];
-        for (i = length-1; i > 0; --i)
-        {
-            name[i] = name[i-1];
-        }
-        name[0] = temp;
-    }
-}
-
-
-void gmx_write_pdb_box(FILE *out, int ePBC, const matrix box)
+void gmx_write_pdb_box(FILE* out, PbcType pbcType, const matrix box)
 {
     real alpha, beta, gamma;
 
-    if (ePBC == -1)
+    if (pbcType == PbcType::Unset)
     {
-        ePBC = guess_ePBC(box);
+        pbcType = guessPbcType(box);
     }
 
-    if (ePBC == epbcNONE)
+    if (pbcType == PbcType::No)
     {
         return;
     }
 
-    if (norm2(box[YY])*norm2(box[ZZ]) != 0)
+    if (norm2(box[YY]) * norm2(box[ZZ]) != 0)
     {
-        alpha = RAD2DEG*gmx_angle(box[YY], box[ZZ]);
+        alpha = gmx::c_rad2Deg * gmx_angle(box[YY], box[ZZ]);
     }
     else
     {
         alpha = 90;
     }
-    if (norm2(box[XX])*norm2(box[ZZ]) != 0)
+    if (norm2(box[XX]) * norm2(box[ZZ]) != 0)
     {
-        beta  = RAD2DEG*gmx_angle(box[XX], box[ZZ]);
+        beta = gmx::c_rad2Deg * gmx_angle(box[XX], box[ZZ]);
     }
     else
     {
-        beta  = 90;
+        beta = 90;
     }
-    if (norm2(box[XX])*norm2(box[YY]) != 0)
+    if (norm2(box[XX]) * norm2(box[YY]) != 0)
     {
-        gamma = RAD2DEG*gmx_angle(box[XX], box[YY]);
+        gamma = gmx::c_rad2Deg * gmx_angle(box[XX], box[YY]);
     }
     else
     {
         gamma = 90;
     }
     fprintf(out, "REMARK    THIS IS A SIMULATION BOX\n");
-    if (ePBC != epbcSCREW)
+    if (pbcType != PbcType::Screw)
     {
-        fprintf(out, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n",
-                10*norm(box[XX]), 10*norm(box[YY]), 10*norm(box[ZZ]),
-                alpha, beta, gamma, "P 1", 1);
+        fprintf(out,
+                "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n",
+                10 * norm(box[XX]),
+                10 * norm(box[YY]),
+                10 * norm(box[ZZ]),
+                alpha,
+                beta,
+                gamma,
+                "P 1",
+                1);
     }
     else
     {
         /* Double the a-vector length and write the correct space group */
-        fprintf(out, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n",
-                20*norm(box[XX]), 10*norm(box[YY]), 10*norm(box[ZZ]),
-                alpha, beta, gamma, "P 21 1 1", 1);
-
+        fprintf(out,
+                "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n",
+                20 * norm(box[XX]),
+                10 * norm(box[YY]),
+                10 * norm(box[ZZ]),
+                alpha,
+                beta,
+                gamma,
+                "P 21 1 1",
+                1);
     }
 }
 
-static void read_cryst1(char *line, int *ePBC, matrix box)
+static void read_cryst1(char* line, PbcType* pbcType, matrix box)
 {
 #define SG_SIZE 11
-    char   sa[12], sb[12], sc[12], sg[SG_SIZE+1], ident;
-    double fa, fb, fc, alpha, beta, gamma, cosa, cosb, cosg, sing;
-    int    syma, symb, symc;
-    int    ePBC_file;
+    char    sa[12], sb[12], sc[12], sg[SG_SIZE + 1], ident;
+    double  fa, fb, fc, alpha, beta, gamma, cosa, cosb, cosg, sing;
+    int     syma, symb, symc;
+    PbcType pbcTypeFile;
 
     sscanf(line, "%*s%s%s%s%lf%lf%lf", sa, sb, sc, &alpha, &beta, &gamma);
 
-    ePBC_file = -1;
+    pbcTypeFile = PbcType::Unset;
     if (strlen(line) >= 55)
     {
-        strncpy(sg, line+55, SG_SIZE);
+        strncpy(sg, line + 55, SG_SIZE);
         sg[SG_SIZE] = '\0';
         ident       = ' ';
         syma        = 0;
         symb        = 0;
         symc        = 0;
         sscanf(sg, "%c %d %d %d", &ident, &syma, &symb, &symc);
-        if (ident == 'P' && syma ==  1 && symb <= 1 && symc <= 1)
+        if (ident == 'P' && syma == 1 && symb <= 1 && symc <= 1)
         {
-            fc        = strtod(sc, nullptr)*0.1;
-            ePBC_file = (fc > 0 ? epbcXYZ : epbcXY);
+            fc          = strtod(sc, nullptr) * 0.1;
+            pbcTypeFile = (fc > 0 ? PbcType::Xyz : PbcType::XY);
         }
         if (ident == 'P' && syma == 21 && symb == 1 && symc == 1)
         {
-            ePBC_file = epbcSCREW;
+            pbcTypeFile = PbcType::Screw;
         }
     }
-    if (ePBC)
+    if (pbcType)
     {
-        *ePBC = ePBC_file;
+        *pbcType = pbcTypeFile;
     }
 
     if (box)
     {
-        fa = strtod(sa, nullptr)*0.1;
-        fb = strtod(sb, nullptr)*0.1;
-        fc = strtod(sc, nullptr)*0.1;
-        if (ePBC_file == epbcSCREW)
+        fa = strtod(sa, nullptr) * 0.1;
+        fb = strtod(sb, nullptr) * 0.1;
+        fc = strtod(sc, nullptr) * 0.1;
+        if (pbcTypeFile == PbcType::Screw)
         {
             fa *= 0.5;
         }
@@ -220,7 +204,7 @@ static void read_cryst1(char *line, int *ePBC, matrix box)
         {
             if (alpha != 90.0)
             {
-                cosa = std::cos(alpha*DEG2RAD);
+                cosa = std::cos(alpha * gmx::c_deg2Rad);
             }
             else
             {
@@ -228,7 +212,7 @@ static void read_cryst1(char *line, int *ePBC, matrix box)
             }
             if (beta != 90.0)
             {
-                cosb = std::cos(beta*DEG2RAD);
+                cosb = std::cos(beta * gmx::c_deg2Rad);
             }
             else
             {
@@ -236,20 +220,19 @@ static void read_cryst1(char *line, int *ePBC, matrix box)
             }
             if (gamma != 90.0)
             {
-                cosg = std::cos(gamma*DEG2RAD);
-                sing = std::sin(gamma*DEG2RAD);
+                cosg = std::cos(gamma * gmx::c_deg2Rad);
+                sing = std::sin(gamma * gmx::c_deg2Rad);
             }
             else
             {
                 cosg = 0;
                 sing = 1;
             }
-            box[YY][XX] = fb*cosg;
-            box[YY][YY] = fb*sing;
-            box[ZZ][XX] = fc*cosb;
-            box[ZZ][YY] = fc*(cosa - cosb*cosg)/sing;
-            box[ZZ][ZZ] = std::sqrt(fc*fc
-                                    - box[ZZ][XX]*box[ZZ][XX] - box[ZZ][YY]*box[ZZ][YY]);
+            box[YY][XX] = fb * cosg;
+            box[YY][YY] = fb * sing;
+            box[ZZ][XX] = fc * cosb;
+            box[ZZ][YY] = fc * (cosa - cosb * cosg) / sing;
+            box[ZZ][ZZ] = std::sqrt(fc * fc - box[ZZ][XX] * box[ZZ][XX] - box[ZZ][YY] * box[ZZ][YY]);
         }
         else
         {
@@ -259,77 +242,93 @@ static void read_cryst1(char *line, int *ePBC, matrix box)
     }
 }
 
-static int
-gmx_fprintf_pqr_atomline(FILE *            fp,
-                         enum PDB_record   record,
-                         int               atom_seq_number,
-                         const char *      atom_name,
-                         const char *      res_name,
-                         char              chain_id,
-                         int               res_seq_number,
-                         real              x,
-                         real              y,
-                         real              z,
-                         real              occupancy,
-                         real              b_factor)
+static int gmx_fprintf_pqr_atomline(FILE*         fp,
+                                    PdbRecordType record,
+                                    int           atom_seq_number,
+                                    const char*   atom_name,
+                                    const char*   res_name,
+                                    char          chain_id,
+                                    int           res_seq_number,
+                                    real          x,
+                                    real          y,
+                                    real          z,
+                                    real          occupancy,
+                                    real          b_factor)
 {
-    GMX_RELEASE_ASSERT(record == epdbATOM || record == epdbHETATM,
+    GMX_RELEASE_ASSERT(record == PdbRecordType::Atom || record == PdbRecordType::Hetatm,
                        "Can only print PQR atom lines as ATOM or HETATM records");
 
     /* Check atom name */
-    GMX_RELEASE_ASSERT(atom_name != nullptr,
-                       "Need atom information to print pqr");
+    GMX_RELEASE_ASSERT(atom_name != nullptr, "Need atom information to print pqr");
 
     /* Check residue name */
-    GMX_RELEASE_ASSERT(res_name != nullptr,
-                       "Need residue information to print pqr");
+    GMX_RELEASE_ASSERT(res_name != nullptr, "Need residue information to print pqr");
 
     /* Truncate integers so they fit */
     atom_seq_number = atom_seq_number % 100000;
     res_seq_number  = res_seq_number % 10000;
 
     int n = fprintf(fp,
-                    "%s %d %s %s %c %d %8.3f %8.3f %8.3f %6.2f %6.2f\n",
-                    pdbtp[record],
+                    "%-6s%5d %-4.4s%4.4s%c%4d %8.3f %8.3f %8.3f %6.2f %6.2f\n",
+                    enumValueToString(record),
                     atom_seq_number,
                     atom_name,
                     res_name,
                     chain_id,
                     res_seq_number,
-                    x, y, z,
+                    x,
+                    y,
+                    z,
                     occupancy,
                     b_factor);
 
     return n;
 }
 
-void write_pdbfile_indexed(FILE *out, const char *title,
-                           const t_atoms *atoms, const rvec x[],
-                           int ePBC, const matrix box, char chainid,
-                           int model_nr, int nindex, const int index[],
-                           gmx_conect conect, gmx_bool bTerSepChains,
-                           bool usePqrFormat)
+void write_pdbfile_indexed(FILE*          out,
+                           const char*    title,
+                           const t_atoms* atoms,
+                           const rvec     x[],
+                           PbcType        pbcType,
+                           const matrix   box,
+                           char           chainid,
+                           int            model_nr,
+                           int            nindex,
+                           const int      index[],
+                           gmx_conect     conect,
+                           bool           usePqrFormat,
+                           bool           standardCompliantMode)
 {
-    gmx_conect_t     *gc = static_cast<gmx_conect_t *>(conect);
-    char              resnm[6], nm[6];
-    int               i, ii;
-    int               resind, resnr;
-    enum PDB_record   type;
-    unsigned char     resic, ch;
-    char              altloc;
-    real              occup, bfac;
-    gmx_bool          bOccup;
-    int               chainnum, lastchainnum;
-    gmx_residuetype_t*rt;
-    const char       *p_restype;
-    const char       *p_lastrestype;
-
-    gmx_residuetype_init(&rt);
-
-    fprintf(out, "TITLE     %s\n", (title && title[0]) ? title : gmx::bromacs().c_str());
-    if (box && ( (norm2(box[XX]) != 0.0f) || (norm2(box[YY]) != 0.0f) || (norm2(box[ZZ]) != 0.0f) ) )
+    gmx_conect_t* gc = static_cast<gmx_conect_t*>(conect);
+    PdbRecordType type;
+    char          altloc;
+    real          occup, bfac;
+    gmx_bool      bOccup;
+
+    if (standardCompliantMode)
+    {
+        fprintf(out, "HEADER    GROMACS SIMULATION BOX                  01-JAN-00   0000\n");
+        fprintf(out,
+                "TITLE     Gromacs simulation box\n"
+                "COMPND    MOL_ID:  1;                                                           \n"
+                "COMPND   2 MOLECULE:  GROMACS SIMULATION BOX;                                   \n"
+                "COMPND   3 CHAIN: A;  \n"
+                "SOURCE    MOL_ID: 1;\n"
+                "SOURCE   2 SYNTHETIC\n"
+                "KEYWDS    GROMACS\n"
+                "EXPDTA    PURE PRODUCT OF COMPUTER SIMULATION\n"
+                "AUTHOR    GROMACS\n"
+                "REVDAT   1   01-JAN-00 0000    0\n");
+    }
+    else
+    {
+        fprintf(out, "TITLE     %s\n", (title && title[0]) ? title : gmx::bromacs().c_str());
+    }
+
+
+    if (box && ((norm2(box[XX]) != 0.0F) || (norm2(box[YY]) != 0.0F) || (norm2(box[ZZ]) != 0.0F)))
     {
-        gmx_write_pdb_box(out, ePBC, box);
+        gmx_write_pdb_box(out, pbcType, box);
     }
     if (atoms->havePdbInfo)
     {
@@ -337,9 +336,9 @@ void write_pdbfile_indexed(FILE *out, const char *title,
          * otherwise set them all to one
          */
         bOccup = TRUE;
-        for (ii = 0; (ii < nindex) && bOccup; ii++)
+        for (int ii = 0; (ii < nindex) && bOccup; ii++)
         {
-            i      = index[ii];
+            int i  = index[ii];
             bOccup = bOccup && (atoms->pdbinfo[i].occup == 0.0);
         }
     }
@@ -350,37 +349,21 @@ void write_pdbfile_indexed(FILE *out, const char *title,
 
     fprintf(out, "MODEL %8d\n", model_nr > 0 ? model_nr : 1);
 
-    lastchainnum      = -1;
-    p_restype         = nullptr;
+    // Collect last printed values for TER record
+    int         lastAtomNumber = 0;
+    std::string lastResName;
+    int         lastResNr = 0;
 
-    for (ii = 0; ii < nindex; ii++)
+    for (int ii = 0; ii < nindex; ii++)
     {
-        i             = index[ii];
-        resind        = atoms->atom[i].resind;
-        chainnum      = atoms->resinfo[resind].chainnum;
-        p_lastrestype = p_restype;
-        gmx_residuetype_get_type(rt, *atoms->resinfo[resind].name, &p_restype);
-
-        /* Add a TER record if we changed chain, and if either the previous or this chain is protein/DNA/RNA. */
-        if (bTerSepChains && ii > 0 && chainnum != lastchainnum)
-        {
-            /* Only add TER if the previous chain contained protein/DNA/RNA. */
-            if (gmx_residuetype_is_protein(rt, p_lastrestype) || gmx_residuetype_is_dna(rt, p_lastrestype) || gmx_residuetype_is_rna(rt, p_lastrestype))
-            {
-                fprintf(out, "TER\n");
-            }
-            lastchainnum    = chainnum;
-        }
-
-        strncpy(resnm, *atoms->resinfo[resind].name, sizeof(resnm)-1);
-        resnm[sizeof(resnm)-1] = 0;
-        strncpy(nm, *atoms->atomname[i], sizeof(nm)-1);
-        nm[sizeof(nm)-1] = 0;
-
-        /* rename HG12 to 2HG1, etc. */
-        xlate_atomname_gmx2pdb(nm);
-        resnr = atoms->resinfo[resind].nr;
-        resic = atoms->resinfo[resind].ic;
+        int         i      = index[ii];
+        int         resind = atoms->atom[i].resind;
+        std::string resnm  = *atoms->resinfo[resind].name;
+        std::string nm     = *atoms->atomname[i];
+
+        int           resnr = atoms->resinfo[resind].nr;
+        unsigned char resic = atoms->resinfo[resind].ic;
+        unsigned char ch;
         if (chainid != ' ')
         {
             ch = chainid;
@@ -407,7 +390,7 @@ void write_pdbfile_indexed(FILE *out, const char *title,
         {
             gmx_pdbinfo_init_default(&pdbinfo);
         }
-        type   = static_cast<enum PDB_record>(pdbinfo.type);
+        type   = pdbinfo.type;
         altloc = pdbinfo.altloc;
         if (!isalnum(altloc))
         {
@@ -419,60 +402,89 @@ void write_pdbfile_indexed(FILE *out, const char *title,
         {
             gmx_fprintf_pdb_atomline(out,
                                      type,
-                                     i+1,
-                                     nm,
+                                     i + 1,
+                                     nm.c_str(),
                                      altloc,
-                                     resnm,
+                                     resnm.c_str(),
                                      ch,
                                      resnr,
                                      resic,
-                                     10*x[i][XX], 10*x[i][YY], 10*x[i][ZZ],
+                                     10 * x[i][XX],
+                                     10 * x[i][YY],
+                                     10 * x[i][ZZ],
                                      occup,
                                      bfac,
                                      atoms->atom[i].elem);
 
+            lastAtomNumber = i + 1;
+            lastResName    = resnm;
+            lastResNr      = resnr;
+
             if (atoms->pdbinfo && atoms->pdbinfo[i].bAnisotropic)
             {
-                fprintf(out, "ANISOU%5d  %-4.4s%4.4s%c%4d%c %7d%7d%7d%7d%7d%7d\n",
-                        (i+1)%100000, nm, resnm, ch, resnr,
+                fprintf(out,
+                        "ANISOU%5d  %-4.4s%4.4s%c%4d%c %7d%7d%7d%7d%7d%7d\n",
+                        (i + 1) % 100000,
+                        nm.c_str(),
+                        resnm.c_str(),
+                        ch,
+                        resnr,
                         (resic == '\0') ? ' ' : resic,
-                        atoms->pdbinfo[i].uij[0], atoms->pdbinfo[i].uij[1],
-                        atoms->pdbinfo[i].uij[2], atoms->pdbinfo[i].uij[3],
-                        atoms->pdbinfo[i].uij[4], atoms->pdbinfo[i].uij[5]);
+                        atoms->pdbinfo[i].uij[0],
+                        atoms->pdbinfo[i].uij[1],
+                        atoms->pdbinfo[i].uij[2],
+                        atoms->pdbinfo[i].uij[3],
+                        atoms->pdbinfo[i].uij[4],
+                        atoms->pdbinfo[i].uij[5]);
             }
         }
         else
         {
             gmx_fprintf_pqr_atomline(out,
                                      type,
-                                     i+1,
-                                     nm,
-                                     resnm,
+                                     i + 1,
+                                     nm.c_str(),
+                                     resnm.c_str(),
                                      ch,
                                      resnr,
-                                     10*x[i][XX], 10*x[i][YY], 10*x[i][ZZ],
+                                     10 * x[i][XX],
+                                     10 * x[i][YY],
+                                     10 * x[i][ZZ],
                                      occup,
                                      bfac);
         }
     }
 
-    fprintf(out, "TER\n");
+    if (standardCompliantMode)
+    {
+        fprintf(out, "TER   %5d      %4.4s%c%4d\n", lastAtomNumber, lastResName.c_str(), chainid, lastResNr);
+    }
+    else
+    {
+        fprintf(out, "TER\n");
+    }
+
     fprintf(out, "ENDMDL\n");
 
     if (nullptr != gc)
     {
         /* Write conect records */
-        for (i = 0; (i < gc->nconect); i++)
+        for (int i = 0; (i < gc->nconect); i++)
         {
-            fprintf(out, "CONECT%5d%5d\n", gc->conect[i].ai+1, gc->conect[i].aj+1);
+            fprintf(out, "CONECT%5d%5d\n", gc->conect[i].ai + 1, gc->conect[i].aj + 1);
         }
     }
-
-    gmx_residuetype_destroy(rt);
 }
 
-void write_pdbfile(FILE *out, const char *title, const t_atoms *atoms, const rvec x[],
-                   int ePBC, const matrix box, char chainid, int model_nr, gmx_conect conect, gmx_bool bTerSepChains)
+void write_pdbfile(FILE*          out,
+                   const char*    title,
+                   const t_atoms* atoms,
+                   const rvec     x[],
+                   PbcType        pbcType,
+                   const matrix   box,
+                   char           chainid,
+                   int            model_nr,
+                   gmx_conect     conect)
 {
     int i, *index;
 
@@ -481,34 +493,12 @@ void write_pdbfile(FILE *out, const char *title, const t_atoms *atoms, const rve
     {
         index[i] = i;
     }
-    write_pdbfile_indexed(out, title, atoms, x, ePBC, box, chainid, model_nr,
-                          atoms->nr, index, conect, bTerSepChains, false);
+    write_pdbfile_indexed(
+            out, title, atoms, x, pbcType, box, chainid, model_nr, atoms->nr, index, conect, false);
     sfree(index);
 }
 
-static int line2type(const char *line)
-{
-    int  k;
-    char type[8];
-
-    for (k = 0; (k < 6); k++)
-    {
-        type[k] = line[k];
-    }
-    type[k] = '\0';
-
-    for (k = 0; (k < epdbNR); k++)
-    {
-        if (std::strncmp(type, pdbtp[k], strlen(pdbtp[k])) == 0)
-        {
-            break;
-        }
-    }
-
-    return k;
-}
-
-static void read_anisou(char line[], int natom, t_atoms *atoms)
+static void read_anisou(char line[], int natom, t_atoms* atoms)
 {
     int  i, j, k, atomnr;
     char nc = '\0';
@@ -534,25 +524,27 @@ static void read_anisou(char line[], int natom, t_atoms *atoms)
 
     /* Search backwards for number and name only */
     atomnr = std::strtol(anr, nullptr, 10);
-    for (i = natom-1; (i >= 0); i--)
+    for (i = natom - 1; (i >= 0); i--)
     {
-        if ((std::strcmp(anm, *(atoms->atomname[i])) == 0) &&
-            (atomnr == atoms->pdbinfo[i].atomnr))
+        if ((std::strcmp(anm, *(atoms->atomname[i])) == 0) && (atomnr == atoms->pdbinfo[i].atomnr))
         {
             break;
         }
     }
     if (i < 0)
     {
-        fprintf(stderr, "Skipping ANISOU record (atom %s %d not found)\n",
-                anm, atomnr);
+        fprintf(stderr, "Skipping ANISOU record (atom %s %d not found)\n", anm, atomnr);
     }
     else
     {
-        if (sscanf(line+29, "%d%d%d%d%d%d",
-                   &atoms->pdbinfo[i].uij[U11], &atoms->pdbinfo[i].uij[U22],
-                   &atoms->pdbinfo[i].uij[U33], &atoms->pdbinfo[i].uij[U12],
-                   &atoms->pdbinfo[i].uij[U13], &atoms->pdbinfo[i].uij[U23])
+        if (sscanf(line + 29,
+                   "%d%d%d%d%d%d",
+                   &atoms->pdbinfo[i].uij[U11],
+                   &atoms->pdbinfo[i].uij[U22],
+                   &atoms->pdbinfo[i].uij[U33],
+                   &atoms->pdbinfo[i].uij[U12],
+                   &atoms->pdbinfo[i].uij[U13],
+                   &atoms->pdbinfo[i].uij[U23])
             == 6)
         {
             atoms->pdbinfo[i].bAnisotropic = TRUE;
@@ -565,11 +557,11 @@ static void read_anisou(char line[], int natom, t_atoms *atoms)
     }
 }
 
-void get_pdb_atomnumber(const t_atoms *atoms, gmx_atomprop_t aps)
+void get_pdb_atomnumber(const t_atoms* atoms, AtomProperties* aps)
 {
     int    i, atomnumber, len;
     size_t k;
-    char   anm[6], anm_copy[6], *ptr;
+    char   anm[6], anm_copy[6];
     char   nc = '\0';
     real   eval;
 
@@ -582,21 +574,21 @@ void get_pdb_atomnumber(const t_atoms *atoms, gmx_atomprop_t aps)
         std::strcpy(anm, atoms->pdbinfo[i].atomnm);
         std::strcpy(anm_copy, atoms->pdbinfo[i].atomnm);
         bool atomNumberSet = false;
-        len        = strlen(anm);
+        len                = strlen(anm);
         if ((anm[0] != ' ') && ((len <= 2) || !std::isdigit(anm[2])))
         {
             anm_copy[2] = nc;
-            if (gmx_atomprop_query(aps, epropElement, "???", anm_copy, &eval))
+            if (aps->setAtomProperty(epropElement, "???", anm_copy, &eval))
             {
-                atomnumber    = std::round(eval);
+                atomnumber    = gmx::roundToInt(eval);
                 atomNumberSet = true;
             }
             else
             {
                 anm_copy[1] = nc;
-                if (gmx_atomprop_query(aps, epropElement, "???", anm_copy, &eval))
+                if (aps->setAtomProperty(epropElement, "???", anm_copy, &eval))
                 {
-                    atomnumber    = std::round(eval);
+                    atomnumber    = gmx::roundToInt(eval);
                     atomNumberSet = true;
                 }
             }
@@ -610,35 +602,33 @@ void get_pdb_atomnumber(const t_atoms *atoms, gmx_atomprop_t aps)
             }
             anm_copy[0] = anm[k];
             anm_copy[1] = nc;
-            if (gmx_atomprop_query(aps, epropElement, "???", anm_copy, &eval))
+            if (aps->setAtomProperty(epropElement, "???", anm_copy, &eval))
             {
-                atomnumber    = std::round(eval);
+                atomnumber    = gmx::roundToInt(eval);
                 atomNumberSet = true;
             }
         }
+        static constexpr size_t sc_maxElementNameLength = 3;
+        static_assert(sizeof(atoms->atom[i].elem) >= sc_maxElementNameLength + 1);
+        std::string element;
         if (atomNumberSet)
         {
             atoms->atom[i].atomnumber = atomnumber;
-            ptr = gmx_atomprop_element(aps, atomnumber);
+            element                   = aps->elementFromAtomNumber(atomnumber);
             if (debug)
             {
-                fprintf(debug, "Atomnumber for atom '%s' is %d\n",
-                        anm, atomnumber);
+                fprintf(debug, "Atomnumber for atom '%s' is %d\n", anm, atomnumber);
             }
         }
-        else
-        {
-            ptr = nullptr;
-        }
-        std::strncpy(atoms->atom[i].elem, ptr == nullptr ? "" : ptr, 4);
+        element.resize(sc_maxElementNameLength);
+        std::strcpy(atoms->atom[i].elem, element.c_str());
     }
 }
 
-static int read_atom(t_symtab *symtab,
-                     const char line[], int type, int natom,
-                     t_atoms *atoms, rvec x[], int chainnum, gmx_bool bChange)
+static int
+read_atom(t_symtab* symtab, const char line[], PdbRecordType type, int natom, t_atoms* atoms, rvec x[], int chainnum)
 {
-    t_atom       *atomn;
+    t_atom*       atomn;
     int           j, k;
     char          nc = '\0';
     char          anr[12], anm[12], anm_copy[12], altloc, resnm[12], rnr[12], elem[3];
@@ -649,8 +639,7 @@ static int read_atom(t_symtab *symtab,
 
     if (natom >= atoms->nr)
     {
-        gmx_fatal(FARGS, "\nFound more atoms (%d) in pdb file than expected (%d)",
-                  natom+1, atoms->nr);
+        gmx_fatal(FARGS, "\nFound more atoms (%d) in pdb file than expected (%d)", natom + 1, atoms->nr);
     }
 
     /* Skip over type */
@@ -691,7 +680,7 @@ static int read_atom(t_symtab *symtab,
     trim(rnr);
     resnr = std::strtol(rnr, nullptr, 10);
     resic = line[j];
-    j    += 4;
+    j += 4;
 
     /* X,Y,Z Coordinate */
     for (k = 0; (k < 8); k++, j++)
@@ -738,10 +727,9 @@ static int read_atom(t_symtab *symtab,
     if (atoms->atom)
     {
         atomn = &(atoms->atom[natom]);
-        if ((natom == 0) ||
-            atoms->resinfo[atoms->atom[natom-1].resind].nr != resnr ||
-            atoms->resinfo[atoms->atom[natom-1].resind].ic != resic ||
-            (strcmp(*atoms->resinfo[atoms->atom[natom-1].resind].name, resnm) != 0))
+        if ((natom == 0) || atoms->resinfo[atoms->atom[natom - 1].resind].nr != resnr
+            || atoms->resinfo[atoms->atom[natom - 1].resind].ic != resic
+            || (strcmp(*atoms->resinfo[atoms->atom[natom - 1].resind].name, resnm) != 0))
         {
             if (natom == 0)
             {
@@ -749,18 +737,14 @@ static int read_atom(t_symtab *symtab,
             }
             else
             {
-                atomn->resind = atoms->atom[natom-1].resind + 1;
+                atomn->resind = atoms->atom[natom - 1].resind + 1;
             }
             atoms->nres = atomn->resind + 1;
             t_atoms_set_resinfo(atoms, natom, symtab, resnm, resnr, resic, chainnum, chainid);
         }
         else
         {
-            atomn->resind = atoms->atom[natom-1].resind;
-        }
-        if (bChange)
-        {
-            xlate_atomname_pdb2gmx(anm);
+            atomn->resind = atoms->atom[natom - 1].resind;
         }
         atoms->atomname[natom] = put_symtab(symtab, anm);
         atomn->m               = 0.0;
@@ -768,9 +752,9 @@ static int read_atom(t_symtab *symtab,
         atomn->atomnumber      = atomnumber;
         strncpy(atomn->elem, elem, 4);
     }
-    x[natom][XX] = strtod(xc, nullptr)*0.1;
-    x[natom][YY] = strtod(yc, nullptr)*0.1;
-    x[natom][ZZ] = strtod(zc, nullptr)*0.1;
+    x[natom][XX] = strtod(xc, nullptr) * 0.1;
+    x[natom][YY] = strtod(yc, nullptr) * 0.1;
+    x[natom][ZZ] = strtod(zc, nullptr) * 0.1;
     if (atoms->pdbinfo)
     {
         atoms->pdbinfo[natom].type   = type;
@@ -785,74 +769,61 @@ static int read_atom(t_symtab *symtab,
     return natom;
 }
 
-gmx_bool is_hydrogen(const char *nm)
+gmx_bool is_hydrogen(const charnm)
 {
     char buf[30];
 
     std::strcpy(buf, nm);
     trim(buf);
 
-    if (buf[0] == 'H')
-    {
-        return TRUE;
-    }
-    else if ((std::isdigit(buf[0])) && (buf[1] == 'H'))
-    {
-        return TRUE;
-    }
-    return FALSE;
+    return ((buf[0] == 'H') || ((std::isdigit(buf[0]) != 0) && (buf[1] == 'H')));
 }
 
-gmx_bool is_dummymass(const char *nm)
+gmx_bool is_dummymass(const charnm)
 {
     char buf[30];
 
     std::strcpy(buf, nm);
     trim(buf);
 
-    return (buf[0] == 'M') && (std::isdigit(buf[strlen(buf)-1]) != 0);
+    return (buf[0] == 'M') && (std::isdigit(buf[strlen(buf) - 1]) != 0);
 }
 
-static void gmx_conect_addline(gmx_conect_t *con, char *line)
+static void gmx_conect_addline(gmx_conect_t* con, char* line)
 {
-    int  n, ai, aj;
-    char format[32], form2[32];
+    int n, ai, aj;
 
-    sprintf(form2, "%%*s");
-    sprintf(format, "%s%%d", form2);
-    if (sscanf(line, format, &ai) == 1)
+    std::string form2  = "%*s";
+    std::string format = form2 + "%d";
+    if (sscanf(line, format.c_str(), &ai) == 1)
     {
         do
         {
-            std::strcat(form2, "%*s");
-            sprintf(format, "%s%%d", form2);
-            n = sscanf(line, format, &aj);
+            form2 += "%*s";
+            format = form2 + "%d";
+            n      = sscanf(line, format.c_str(), &aj);
             if (n == 1)
             {
-                srenew(con->conect, ++con->nconect);
-                con->conect[con->nconect-1].ai = ai-1;
-                con->conect[con->nconect-1].aj = aj-1;
+                gmx_conect_add(con, ai - 1, aj - 1); /* to prevent duplicated records */
             }
-        }
-        while (n == 1);
+        } while (n == 1);
     }
 }
 
-void gmx_conect_dump(FILE *fp, gmx_conect conect)
+void gmx_conect_dump(FILEfp, gmx_conect conect)
 {
-    gmx_conect_t *gc = static_cast<gmx_conect_t *>(conect);
+    gmx_conect_t* gc = static_cast<gmx_conect_t*>(conect);
     int           i;
 
     for (i = 0; (i < gc->nconect); i++)
     {
-        fprintf(fp, "%6s%5d%5d\n", "CONECT",
-                gc->conect[i].ai+1, gc->conect[i].aj+1);
+        fprintf(fp, "%6s%5d%5d\n", "CONECT", gc->conect[i].ai + 1, gc->conect[i].aj + 1);
     }
 }
 
 gmx_conect gmx_conect_init()
 {
-    gmx_conect_t *gc;
+    gmx_conect_tgc;
 
     snew(gc, 1);
 
@@ -861,14 +832,14 @@ gmx_conect gmx_conect_init()
 
 void gmx_conect_done(gmx_conect conect)
 {
-    gmx_conect_t *gc = conect;
+    gmx_conect_tgc = conect;
 
     sfree(gc->conect);
 }
 
 gmx_bool gmx_conect_exist(gmx_conect conect, int ai, int aj)
 {
-    gmx_conect_t *gc = conect;
+    gmx_conect_tgc = conect;
     int           i;
 
     /* if (!gc->bSorted)
@@ -876,10 +847,8 @@ gmx_bool gmx_conect_exist(gmx_conect conect, int ai, int aj)
 
     for (i = 0; (i < gc->nconect); i++)
     {
-        if (((gc->conect[i].ai == ai) &&
-             (gc->conect[i].aj == aj)) ||
-            ((gc->conect[i].aj == ai) &&
-             (gc->conect[i].ai == aj)))
+        if (((gc->conect[i].ai == ai) && (gc->conect[i].aj == aj))
+            || ((gc->conect[i].aj == ai) && (gc->conect[i].ai == aj)))
         {
             return TRUE;
         }
@@ -889,7 +858,7 @@ gmx_bool gmx_conect_exist(gmx_conect conect, int ai, int aj)
 
 void gmx_conect_add(gmx_conect conect, int ai, int aj)
 {
-    gmx_conect_t *gc = static_cast<gmx_conect_t *>(conect);
+    gmx_conect_t* gc = static_cast<gmx_conect_t*>(conect);
 
     /* if (!gc->bSorted)
        sort_conect(gc);*/
@@ -897,28 +866,33 @@ void gmx_conect_add(gmx_conect conect, int ai, int aj)
     if (!gmx_conect_exist(conect, ai, aj))
     {
         srenew(gc->conect, ++gc->nconect);
-        gc->conect[gc->nconect-1].ai = ai;
-        gc->conect[gc->nconect-1].aj = aj;
+        gc->conect[gc->nconect - 1].ai = ai;
+        gc->conect[gc->nconect - 1].aj = aj;
     }
 }
 
-int read_pdbfile(FILE *in, char *title, int *model_nr,
-                 t_atoms *atoms, t_symtab *symtab, rvec x[], int *ePBC,
-                 matrix box, gmx_bool bChange, gmx_conect conect)
+int read_pdbfile(FILE*      in,
+                 char*      title,
+                 int*       model_nr,
+                 t_atoms*   atoms,
+                 t_symtab*  symtab,
+                 rvec       x[],
+                 PbcType*   pbcType,
+                 matrix     box,
+                 gmx_conect conect)
 {
-    gmx_conect_t *gc = conect;
+    gmx_conect_tgc = conect;
     gmx_bool      bCOMPND;
     gmx_bool      bConnWarn = FALSE;
-    char          line[STRLEN+1];
-    int           line_type;
-    char         *c, *d;
+    char          line[STRLEN + 1];
+    char *        c, *d;
     int           natom, chainnum;
-    gmx_bool      bStop   = FALSE;
+    gmx_bool      bStop = FALSE;
 
-    if (ePBC)
+    if (pbcType)
     {
         /* Only assume pbc when there is a CRYST1 entry */
-        *ePBC = epbcNONE;
+        *pbcType = PbcType::No;
     }
     if (box != nullptr)
     {
@@ -935,33 +909,42 @@ int read_pdbfile(FILE *in, char *title, int *model_nr,
     title[0] = '\0';
     natom    = 0;
     chainnum = 0;
+    static const gmx::StringToEnumValueConverter<PdbRecordType, enumValueToString, gmx::StringCompareType::Exact, gmx::StripStrings::Yes>
+            sc_pdbRecordTypeIdentifier;
     while (!bStop && (fgets2(line, STRLEN, in) != nullptr))
     {
-        line_type = line2type(line);
+        // Convert line to a null-terminated string, then take a substring of at most 6 chars
+        std::string                  lineAsString(line);
+        std::optional<PdbRecordType> line_type =
+                sc_pdbRecordTypeIdentifier.valueFrom(lineAsString.substr(0, 6));
+        if (!line_type)
+        {
+            // Skip this line because it does not start with a
+            // recognized leading string describing a PDB record type.
+            continue;
+        }
 
-        switch (line_type)
+        switch (line_type.value())
         {
-            case epdbATOM:
-            case epdbHETATM:
-                natom = read_atom(symtab, line, line_type, natom, atoms, x, chainnum, bChange);
+            case PdbRecordType::Atom:
+            case PdbRecordType::Hetatm:
+                natom = read_atom(symtab, line, line_type.value(), natom, atoms, x, chainnum);
                 break;
 
-            case epdbANISOU:
+            case PdbRecordType::Anisou:
                 if (atoms->havePdbInfo)
                 {
                     read_anisou(line, natom, atoms);
                 }
                 break;
 
-            case epdbCRYST1:
-                read_cryst1(line, ePBC, box);
-                break;
+            case PdbRecordType::Cryst1: read_cryst1(line, pbcType, box); break;
 
-            case epdbTITLE:
-            case epdbHEADER:
+            case PdbRecordType::Title:
+            case PdbRecordType::Header:
                 if (std::strlen(line) > 6)
                 {
-                    c = line+6;
+                    c = line + 6;
                     /* skip HEADER or TITLE and spaces */
                     while (c[0] != ' ')
                     {
@@ -984,10 +967,10 @@ int read_pdbfile(FILE *in, char *title, int *model_nr,
                 }
                 break;
 
-            case epdbCOMPND:
-                if ((!std::strstr(line, ": ")) || (std::strstr(line+6, "MOLECULE:")))
+            case PdbRecordType::Compound:
+                if ((!std::strstr(line, ": ")) || (std::strstr(line + 6, "MOLECULE:")))
                 {
-                    if (!(c = std::strstr(line+6, "MOLECULE:")) )
+                    if (!(c = std::strstr(line + 6, "MOLECULE:")))
                     {
                         c = line;
                     }
@@ -1004,7 +987,7 @@ int read_pdbfile(FILE *in, char *title, int *model_nr,
                     d = strstr(c, "   ");
                     if (d)
                     {
-                        while ( (d[-1] == ';') && d > c)
+                        while ((d[-1] == ';') && d > c)
                         {
                             d--;
                         }
@@ -1026,21 +1009,17 @@ int read_pdbfile(FILE *in, char *title, int *model_nr,
                 }
                 break;
 
-            case epdbTER:
-                chainnum++;
-                break;
+            case PdbRecordType::Ter: chainnum++; break;
 
-            case epdbMODEL:
+            case PdbRecordType::Model:
                 if (model_nr)
                 {
                     sscanf(line, "%*s%d", model_nr);
                 }
                 break;
 
-            case epdbENDMDL:
-                bStop = TRUE;
-                break;
-            case epdbCONECT:
+            case PdbRecordType::EndModel: bStop = TRUE; break;
+            case PdbRecordType::Conect:
                 if (gc)
                 {
                     gmx_conect_addline(gc, line);
@@ -1052,15 +1031,14 @@ int read_pdbfile(FILE *in, char *title, int *model_nr,
                 }
                 break;
 
-            default:
-                break;
+            default: break;
         }
     }
 
     return natom;
 }
 
-void get_pdb_coordnum(FILE *in, int *natoms)
+void get_pdb_coordnum(FILE* in, int* natoms)
 {
     char line[STRLEN];
 
@@ -1078,13 +1056,11 @@ void get_pdb_coordnum(FILE *in, int *natoms)
     }
 }
 
-void gmx_pdb_read_conf(const char *infile,
-                       t_symtab *symtab, char **name, t_atoms *atoms,
-                       rvec x[], int *ePBC, matrix box)
+void gmx_pdb_read_conf(const char* infile, t_symtab* symtab, char** name, t_atoms* atoms, rvec x[], PbcType* pbcType, matrix box)
 {
-    FILE *in = gmx_fio_fopen(infile, "r");
+    FILEin = gmx_fio_fopen(infile, "r");
     char  title[STRLEN];
-    read_pdbfile(in, title, nullptr, atoms, symtab, x, ePBC, box, TRUE, nullptr);
+    read_pdbfile(in, title, nullptr, atoms, symtab, x, pbcType, box, nullptr);
     if (name != nullptr)
     {
         *name = gmx_strdup(title);
@@ -1092,50 +1068,48 @@ void gmx_pdb_read_conf(const char *infile,
     gmx_fio_fclose(in);
 }
 
-gmx_conect gmx_conect_generate(const t_topology *top)
+gmx_conect gmx_conect_generate(const t_topologytop)
 {
     int        f, i;
     gmx_conect gc;
 
     /* Fill the conect records */
-    gc  = gmx_conect_init();
+    gc = gmx_conect_init();
 
     for (f = 0; (f < F_NRE); f++)
     {
         if (IS_CHEMBOND(f))
         {
-            for (i = 0; (i < top->idef.il[f].nr); i += interaction_function[f].nratoms+1)
+            for (i = 0; (i < top->idef.il[f].nr); i += interaction_function[f].nratoms + 1)
             {
-                gmx_conect_add(gc, top->idef.il[f].iatoms[i+1],
-                               top->idef.il[f].iatoms[i+2]);
+                gmx_conect_add(gc, top->idef.il[f].iatoms[i + 1], top->idef.il[f].iatoms[i + 2]);
             }
         }
     }
     return gc;
 }
 
-int
-gmx_fprintf_pdb_atomline(FILE *            fp,
-                         enum PDB_record   record,
-                         int               atom_seq_number,
-                         const char *      atom_name,
-                         char              alternate_location,
-                         const char *      res_name,
-                         char              chain_id,
-                         int               res_seq_number,
-                         char              res_insertion_code,
-                         real              x,
-                         real              y,
-                         real              z,
-                         real              occupancy,
-                         real              b_factor,
-                         const char *      element)
+int gmx_fprintf_pdb_atomline(FILE*         fp,
+                             PdbRecordType record,
+                             int           atom_seq_number,
+                             const char*   atom_name,
+                             char          alternate_location,
+                             const char*   res_name,
+                             char          chain_id,
+                             int           res_seq_number,
+                             char          res_insertion_code,
+                             real          x,
+                             real          y,
+                             real          z,
+                             real          occupancy,
+                             real          b_factor,
+                             const char*   element)
 {
     char     tmp_atomname[6], tmp_resname[6];
     gmx_bool start_name_in_col13;
     int      n;
 
-    if (record != epdbATOM && record != epdbHETATM)
+    if (record != PdbRecordType::Atom && record != PdbRecordType::Hetatm)
     {
         gmx_fatal(FARGS, "Can only print PDB atom lines as ATOM or HETATM records");
     }
@@ -1146,7 +1120,8 @@ gmx_fprintf_pdb_atomline(FILE *            fp,
         /* If the atom name is an element name with two chars, it should start already in column 13.
          * Otherwise it should start in column 14, unless the name length is 4 chars.
          */
-        if ( (element != nullptr) && (std::strlen(element) >= 2) && (gmx_strncasecmp(atom_name, element, 2) == 0) )
+        if ((element != nullptr) && (std::strlen(element) >= 2)
+            && (gmx_strncasecmp(atom_name, element, 2) == 0))
         {
             start_name_in_col13 = TRUE;
         }
@@ -1179,7 +1154,7 @@ gmx_fprintf_pdb_atomline(FILE *            fp,
 
     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",
-                pdbtp[record],
+                enumValueToString(record),
                 atom_seq_number,
                 tmp_atomname,
                 alternate_location,
@@ -1187,7 +1162,9 @@ gmx_fprintf_pdb_atomline(FILE *            fp,
                 chain_id,
                 res_seq_number,
                 res_insertion_code,
-                x, y, z,
+                x,
+                y,
+                z,
                 occupancy,
                 b_factor,
                 (element != nullptr) ? element : "");