Add DSSP v4 support to do_dssp
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_do_dssp.cpp
index 1935ae1c159e81c185d3564e7a7fcb42156ebe5c..f88a65c567f218f42a82d439a0f6dcd9c994bcc9 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) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,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.
@@ -89,8 +90,13 @@ static void printDsspResult(char* dssp, const DsspInputStrings& strings, const s
 #if HAVE_PIPES || GMX_NATIVE_WINDOWS
     sprintf(dssp, "%s -i %s %s", strings.dptr.c_str(), strings.pdbfile.c_str(), redirectionString.c_str());
 #else
-    sprintf(dssp, "%s -i %s -o %s > %s %s", strings.dptr.c_str(), strings.pdbfile.c_str(),
-            strings.tmpfile.c_str(), NULL_DEVICE, redirectionString.c_str());
+    sprintf(dssp,
+            "%s -i %s -o %s > %s %s",
+            strings.dptr.c_str(),
+            strings.pdbfile.c_str(),
+            strings.tmpfile.c_str(),
+            NULL_DEVICE,
+            redirectionString.c_str());
 #endif
 }
 
@@ -105,13 +111,13 @@ static int strip_dssp(FILE*                   tapeout,
                       int                     average_area[],
                       const gmx_output_env_t* oenv)
 {
-    static gmx_bool bFirst = TRUE;
-    static char*    ssbuf;
-    char            buf[STRLEN + 1];
-    char            SSTP;
-    int             nr, iacc, nresidues;
-    int             naccf, naccb; /* Count hydrophobic and hydrophilic residues */
-    real            iaccf, iaccb;
+    static gmx_bool    bFirst = TRUE;
+    static std::string ssbuf;
+    char               buf[STRLEN + 1];
+    char               SSTP;
+    int                nr, iacc, nresidues;
+    int                naccf, naccb; /* Count hydrophobic and hydrophilic residues */
+    real               iaccf, iaccb;
 
 
     /* Skip header */
@@ -127,7 +133,7 @@ static int strip_dssp(FILE*                   tapeout,
          * we allocate 2*nres-1, since for each chain there is a
          * separating line in the temp file. (At most each residue
          * could have been defined as a separate chain.) */
-        snew(ssbuf, 2 * nres - 1);
+        ssbuf.resize(2 * nres - 1);
     }
 
     iaccb = iaccf = 0;
@@ -175,8 +181,7 @@ static int strip_dssp(FILE*                   tapeout,
     {
         if (nullptr != acc)
         {
-            fprintf(stderr, "%d residues were classified as hydrophobic and %d as hydrophilic.\n",
-                    naccb, naccf);
+            fprintf(stderr, "%d residues were classified as hydrophobic and %d as hydrophilic.\n", naccb, naccf);
         }
 
         mat->title     = "Secondary structure";
@@ -188,12 +193,11 @@ static int strip_dssp(FILE*                   tapeout,
         mat->axis_y.resize(nr);
         std::iota(mat->axis_y.begin(), mat->axis_y.end(), 1);
         mat->axis_x.resize(0);
-        mat->matrix.resize(0, 0);
+        mat->matrix.resize(1, 1);
         bFirst = false;
     }
     mat->axis_x.push_back(t);
-    mat->matrix.resize(mat->matrix.extent(0), nr);
-    mat->nx          = mat->matrix.extent(0);
+    mat->matrix.resize(++(mat->nx), nr);
     auto columnIndex = mat->nx - 1;
     for (int i = 0; i < nr; i++)
     {
@@ -245,7 +249,9 @@ static gmx_bool* bPhobics(t_atoms* atoms)
     if (i != j)
     {
         fprintf(stderr,
-                "Not all residues were recognized (%d from %d), the result may be inaccurate!\n", j, i);
+                "Not all residues were recognized (%d from %d), the result may be inaccurate!\n",
+                j,
+                i);
     }
 
     for (i = 0; (i < n_surf); i++)
@@ -259,23 +265,14 @@ static gmx_bool* bPhobics(t_atoms* atoms)
 
 static void check_oo(t_atoms* atoms)
 {
-    char* OOO;
+    char* OOO = gmx_strdup("O");
 
-    int i;
-
-    OOO = gmx_strdup("O");
-
-    for (i = 0; (i < atoms->nr); i++)
+    for (int i = 0; (i < atoms->nr); i++)
     {
-        if (std::strcmp(*(atoms->atomname[i]), "OXT") == 0)
-        {
-            *atoms->atomname[i] = OOO;
-        }
-        else if (std::strcmp(*(atoms->atomname[i]), "O1") == 0)
-        {
-            *atoms->atomname[i] = OOO;
-        }
-        else if (std::strcmp(*(atoms->atomname[i]), "OC1") == 0)
+        if ((std::strcmp(*(atoms->atomname[i]), "OXT") == 0)
+            || (std::strcmp(*(atoms->atomname[i]), "O1") == 0)
+            || (std::strcmp(*(atoms->atomname[i]), "OC1") == 0)
+            || (std::strcmp(*(atoms->atomname[i]), "OT1") == 0))
         {
             *atoms->atomname[i] = OOO;
         }
@@ -308,8 +305,7 @@ static void norm_acc(t_atoms* atoms, int nres, const real av_area[], real norm_a
         }
         else
         {
-            fprintf(stderr, "Residue %s not found in surface database (%s)\n",
-                    *atoms->resinfo[i].name, surffn);
+            fprintf(stderr, "Residue %s not found in surface database (%s)\n", *atoms->resinfo[i].name, surffn);
         }
     }
 }
@@ -370,8 +366,22 @@ static void write_sas_mat(const char* fn, real** accr, int nframe, int nres, t_m
         }
         fp   = gmx_ffopen(fn, "w");
         nlev = static_cast<int>(hi - lo + 1);
-        write_xpm(fp, 0, "Solvent Accessible Surface", "Surface (A^2)", "Time", "Residue Index",
-                  nframe, nres, mat->axis_x.data(), mat->axis_y.data(), accr, lo, hi, rlo, rhi, &nlev);
+        write_xpm(fp,
+                  0,
+                  "Solvent Accessible Surface",
+                  "Surface (A^2)",
+                  "Time",
+                  "Residue Index",
+                  nframe,
+                  nres,
+                  mat->axis_x.data(),
+                  mat->axis_y.data(),
+                  accr,
+                  lo,
+                  hi,
+                  rlo,
+                  rhi,
+                  &nlev);
         gmx_ffclose(fp);
     }
 }
@@ -394,8 +404,8 @@ static void analyse_ss(const char* outfile, t_matrix* mat, const char* ss_string
         leg.emplace_back(m.desc);
     }
 
-    fp = xvgropen(outfile, "Secondary Structure", output_env_get_xvgr_tlabel(oenv),
-                  "Number of Residues", oenv);
+    fp = xvgropen(
+            outfile, "Secondary Structure", output_env_get_xvgr_tlabel(oenv), "Number of Residues", oenv);
     if (output_env_get_print_xvgr_codes(oenv))
     {
         fprintf(fp, "@ subtitle \"Structure = ");
@@ -471,18 +481,19 @@ int gmx_do_dssp(int argc, char* argv[])
         "reads a trajectory file and computes the secondary structure for",
         "each time frame ",
         "calling the dssp program. If you do not have the dssp program,",
-        "get it from http://swift.cmbi.ru.nl/gv/dssp. [THISMODULE] assumes ",
+        "get it from https://swift.cmbi.umcn.nl/gv/dssp. [THISMODULE] assumes ",
         "that the dssp executable is located in ",
-        "[TT]/usr/local/bin/dssp[tt]. If this is not the case, then you should",
-        "set an environment variable [TT]DSSP[tt] pointing to the dssp",
+        // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
+        "[TT]" GMX_DSSP_PROGRAM_PATH "[tt]. If this is not the case, then you should",
+        "set an environment variable [TT]DSSP[tt] pointing to the dssp ",
         "executable, e.g.: [PAR]",
         "[TT]setenv DSSP /opt/dssp/bin/dssp[tt][PAR]",
-        "Since version 2.0.0, dssp is invoked with a syntax that differs",
-        "from earlier versions. If you have an older version of dssp,",
-        "use the [TT]-ver[tt] option to direct do_dssp to use the older syntax.",
+        "The dssp program is invoked with a syntax that differs",
+        "depending on version. Version 1, 2 and 4 are supported, and the correct",
+        "invocation format can be selected using the [TT]-ver[tt] option.",
         "By default, do_dssp uses the syntax introduced with version 2.0.0.",
-        "Even newer versions (which at the time of writing are not yet released)",
-        "are assumed to have the same syntax as 2.0.0.[PAR]",
+        "Newer versions might also have executable name [TT]mkdssp[tt] instead",
+        "of [TT]dssp[tt].[PAR]",
         "The structure assignment for each residue and time is written to an",
         "[REF].xpm[ref] matrix file. This file can be visualized with for instance",
         "[TT]xv[tt] and can be converted to postscript with [TT]xpm2ps[tt].",
@@ -512,7 +523,7 @@ int gmx_do_dssp(int argc, char* argv[])
           FALSE,
           etINT,
           { &dsspVersion },
-          "DSSP major version. Syntax changed with version 2" }
+          "DSSP major version. Syntax changed with version 2 and 4." }
     };
 
     t_trxstatus*      status;
@@ -521,7 +532,7 @@ int gmx_do_dssp(int argc, char* argv[])
     const char *      fnSCount, *fnArea, *fnTArea, *fnAArea;
     const char*       leg[] = { "Phobic", "Phylic" };
     t_topology        top;
-    int               ePBC;
+    PbcType           pbcType;
     t_atoms*          atoms;
     t_matrix          mat;
     int               nres, nr0, naccr, nres_plus_separators;
@@ -550,8 +561,18 @@ int gmx_do_dssp(int argc, char* argv[])
     };
 #define NFILE asize(fnm)
 
-    if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW | PCA_TIME_UNIT, NFILE, fnm,
-                           asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
+    if (!parse_common_args(&argc,
+                           argv,
+                           PCA_CAN_TIME | PCA_CAN_VIEW | PCA_TIME_UNIT,
+                           NFILE,
+                           fnm,
+                           asize(pa),
+                           pa,
+                           asize(desc),
+                           desc,
+                           0,
+                           nullptr,
+                           &oenv))
     {
         return 0;
     }
@@ -561,7 +582,7 @@ int gmx_do_dssp(int argc, char* argv[])
     fnAArea    = opt2fn_null("-aa", NFILE, fnm);
     bDoAccSurf = ((fnArea != nullptr) || (fnTArea != nullptr) || (fnAArea != nullptr));
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xp, nullptr, box, FALSE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xp, nullptr, box, FALSE);
     atoms = &(top.atoms);
     check_oo(atoms);
     bPhbres = bPhobics(atoms);
@@ -605,9 +626,10 @@ int gmx_do_dssp(int argc, char* argv[])
     }
     fclose(tmpf);
 
+    const std::string defpathenv = GMX_DSSP_PROGRAM_PATH;
     if ((dptr = getenv("DSSP")) == nullptr)
     {
-        dptr = "/usr/local/bin/dssp";
+        dptr = defpathenv.c_str();
     }
     if (!gmx_fexist(dptr))
     {
@@ -621,14 +643,30 @@ int gmx_do_dssp(int argc, char* argv[])
     dsspStrings.tmpfile = tmpfile;
     if (dsspVersion >= 2)
     {
-        if (dsspVersion > 2)
+        if (dsspVersion == 4)
+        {
+            std::string mkdsspCommandLine = dsspStrings.dptr;
+            mkdsspCommandLine += " --output-format dssp ";
+            mkdsspCommandLine += dsspStrings.pdbfile;
+
+#if not HAVE_PIPES && not GMX_NATIVE_WINDOWS
+            // Without pipe/popen, rely on temporary file for output
+            mkdsspCommandLine += " " + dsspStrings.tmpfile;
+#endif
+
+            GMX_RELEASE_ASSERT(mkdsspCommandLine.size() < 255, "DSSP v4 command line too long");
+            strcpy(dssp, mkdsspCommandLine.c_str());
+        }
+        else if (dsspVersion == 2)
+        {
+            printDsspResult(dssp, dsspStrings, redirectionString);
+        }
+        else
         {
             printf("\nWARNING: You use DSSP version %d, which is not explicitly\nsupported by "
                    "do_dssp. Assuming version 2 syntax.\n\n",
                    dsspVersion);
         }
-
-        printDsspResult(dssp, dsspStrings, redirectionString);
     }
     else
     {
@@ -646,8 +684,11 @@ int gmx_do_dssp(int argc, char* argv[])
 
     if (fnTArea)
     {
-        fTArea = xvgropen(fnTArea, "Solvent Accessible Surface Area",
-                          output_env_get_xvgr_tlabel(oenv), "Area (nm\\S2\\N)", oenv);
+        fTArea = xvgropen(fnTArea,
+                          "Solvent Accessible Surface Area",
+                          output_env_get_xvgr_tlabel(oenv),
+                          "Area (nm\\S2\\N)",
+                          oenv);
         xvgr_legend(fTArea, 2, leg, oenv);
     }
     else
@@ -673,7 +714,7 @@ int gmx_do_dssp(int argc, char* argv[])
     accr  = nullptr;
     naccr = 0;
 
-    gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top.idef, pbcType, natoms);
     do
     {
         t = output_env_conv_time(oenv, t);
@@ -688,7 +729,8 @@ int gmx_do_dssp(int argc, char* argv[])
         }
         gmx_rmpbc(gpbc, natoms, box, x);
         tapein = gmx_ffopen(pdbfile, "w");
-        write_pdbfile_indexed(tapein, nullptr, atoms, x, ePBC, box, ' ', -1, gnx, index, nullptr, FALSE);
+        write_pdbfile_indexed(
+                tapein, nullptr, atoms, x, pbcType, box, 'A', -1, gnx, index, nullptr, FALSE, true);
         gmx_ffclose(tapein);
         /* strip_dssp returns the number of lines found in the dssp file, i.e.
          * the number of residues plus the separator lines */