Add preprocessing and integration test for transformation coordinate
[alexxy/gromacs.git] / src / gromacs / gmxpreprocess / tests / readir.cpp
index 73768107ee8a46ad7e9757ae4eedb55fb58d53f3..586c95f99ccd5991d23124007678a93494c83769 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018, by the GROMACS development team, led by
+ * Copyright (c) 2017,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.
@@ -52,7 +52,7 @@
 #include <gtest/gtest.h>
 
 #include "gromacs/fileio/warninp.h"
-#include "gromacs/mdrunutility/mdmodules.h"
+#include "gromacs/mdrun/mdmodules.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/stringutil.h"
@@ -61,8 +61,8 @@
 #include "gromacs/utility/unique_cptr.h"
 
 #include "testutils/refdata.h"
+#include "testutils/testasserts.h"
 #include "testutils/testfilemanager.h"
-
 namespace gmx
 {
 namespace test
@@ -70,125 +70,286 @@ namespace test
 
 class GetIrTest : public ::testing::Test
 {
-    public:
-        GetIrTest() : fileManager_(), data_(), checker_(data_.rootChecker()),
-                      ir_(), mdModules_(), opts_(),
-                      wi_(init_warning(FALSE, 0)), wiGuard_(wi_)
+public:
+    GetIrTest() : wi_(init_warning(FALSE, 0)), wiGuard_(wi_)
+    {
+        snew(opts_.include, STRLEN);
+        snew(opts_.define, STRLEN);
+    }
+    ~GetIrTest() override
+    {
+        done_inputrec_strings();
+        sfree(opts_.include);
+        sfree(opts_.define);
+    }
 
+    //! Tells whether warnings and/or errors are expected from inputrec parsing and checking, and whether we should compare the output
+    enum class TestBehavior
+    {
+        NoErrorAndCompareOutput,      //!< Expect no warnings/error and compare output
+        NoErrorAndDoNotCompareOutput, //!< Expect no warnings/error and do not compare output
+        ErrorAndCompareOutput,        //!< Expect at least one warning/error and compare output
+        ErrorAndDoNotCompareOutput //!< Expect at least one warning/error and do not compare output
+    };
+
+    /*! \brief Test mdp reading and writing
+     *
+     * \todo Modernize read_inp and write_inp to use streams,
+     * which will make these tests run faster, because they don't
+     * use disk files. */
+    void runTest(const std::string& inputMdpFileContents,
+                 const TestBehavior testBehavior = TestBehavior::NoErrorAndCompareOutput)
+    {
+        const bool expectError = testBehavior == TestBehavior::ErrorAndCompareOutput
+                                 || testBehavior == TestBehavior::ErrorAndDoNotCompareOutput;
+        const bool compareOutput = testBehavior == TestBehavior::ErrorAndCompareOutput
+                                   || testBehavior == TestBehavior::NoErrorAndCompareOutput;
+
+        std::string inputMdpFilename = fileManager_.getTemporaryFilePath("input.mdp");
+        std::string outputMdpFilename;
+        if (compareOutput)
         {
-            snew(opts_.include, STRLEN);
-            snew(opts_.define, STRLEN);
-        }
-        ~GetIrTest()
-        {
-            done_inputrec_strings();
-            sfree(opts_.include);
-            sfree(opts_.define);
+            outputMdpFilename = fileManager_.getTemporaryFilePath("output.mdp");
         }
-        /*! \brief Test mdp reading and writing
-         *
-         * \todo Modernize read_inp and write_inp to use streams,
-         * which will make these tests run faster, because they don't
-         * use disk files. */
-        void runTest(const std::string &inputMdpFileContents)
-        {
-            auto inputMdpFilename  = fileManager_.getTemporaryFilePath("input.mdp");
-            auto outputMdpFilename = fileManager_.getTemporaryFilePath("output.mdp");
+        TextWriter::writeFileFromString(inputMdpFilename, inputMdpFileContents);
 
-            TextWriter::writeFileFromString(inputMdpFilename, inputMdpFileContents);
+        get_ir(inputMdpFilename.c_str(),
+               outputMdpFilename.empty() ? nullptr : outputMdpFilename.c_str(),
+               &mdModules_,
+               &ir_,
+               &opts_,
+               WriteMdpHeader::no,
+               wi_);
 
-            get_ir(inputMdpFilename.c_str(), outputMdpFilename.c_str(),
-                   &mdModules_, &ir_, &opts_, WriteMdpHeader::no, wi_);
-            bool failure = warning_errors_exist(wi_);
-            checker_.checkBoolean(failure, "Error parsing mdp file");
+        check_ir(inputMdpFilename.c_str(), mdModules_.notifiers(), &ir_, &opts_, wi_);
+        // Now check
+        bool failure = warning_errors_exist(wi_);
+        EXPECT_EQ(failure, expectError);
+
+        if (compareOutput)
+        {
+            TestReferenceData    data;
+            TestReferenceChecker checker(data.rootChecker());
+            checker.checkBoolean(failure, "Error parsing mdp file");
             warning_reset(wi_);
 
             auto outputMdpContents = TextReader::readFileToString(outputMdpFilename);
-            checker_.checkString(outputMdpContents, "OutputMdpFile");
+            checker.checkString(outputMdpContents, "OutputMdpFile");
         }
+    }
 
-        TestFileManager                    fileManager_;
-        TestReferenceData                  data_;
-        TestReferenceChecker               checker_;
-        t_inputrec                         ir_;
-        MDModules                          mdModules_;
-        t_gromppopts                       opts_;
-        warninp_t                          wi_;
-        unique_cptr<warninp, free_warning> wiGuard_;
+    TestFileManager                    fileManager_;
+    t_inputrec                         ir_;
+    MDModules                          mdModules_;
+    t_gromppopts                       opts_;
+    warninp_t                          wi_;
+    unique_cptr<warninp, free_warning> wiGuard_;
 };
 
 TEST_F(GetIrTest, HandlesDifferentKindsOfMdpLines)
 {
-    const char *inputMdpFile[] = {
-        "; File to run my simulation",
-        "title = simulation",
-        ";",
-        "xtc_grps = System ; was Protein",
-        "include = -I/home/me/stuff",
-        "",
-        "tau-t = 0.1 0.3",
-        "tinit = 0.3",
-        "init_step = 0",
-        "nstcomm = 100",
-        "integrator = steep"
-    };
+    const char* inputMdpFile[] = { "; File to run my simulation",
+                                   "title = simulation",
+                                   "define = -DBOOLVAR -DVAR=VALUE",
+                                   ";",
+                                   "xtc_grps = System ; was Protein",
+                                   "include = -I/home/me/stuff",
+                                   "",
+                                   "tau-t = 0.1 0.3",
+                                   "ref-t = ;290 290",
+                                   "tinit = 0.3",
+                                   "init_step = 0",
+                                   "nstcomm = 100",
+                                   "integrator = steep" };
     runTest(joinStrings(inputMdpFile, "\n"));
 }
 
-// This case is used often by SimulationRunner::useEmptyMdpFile (see
-// comments there for explanation). When we remove the group scheme,
-// that usage will have to disappear, and so can this test.
-TEST_F(GetIrTest, HandlesOnlyCutoffScheme)
+TEST_F(GetIrTest, RejectsNonCommentLineWithNoEquals)
+{
+    const char* inputMdpFile = "title simulation";
+    GMX_EXPECT_DEATH_IF_SUPPORTED(runTest(inputMdpFile), "No '=' to separate");
+}
+
+TEST_F(GetIrTest, AcceptsKeyWithoutValue)
 {
-    const char *inputMdpFile = "cutoff-scheme = Group\n";
+    // Users are probably using lines like this
+    const char* inputMdpFile = "xtc_grps = ";
     runTest(inputMdpFile);
 }
 
-// TODO Stop accepting any of these
-TEST_F(GetIrTest, UserErrorsSilentlyTolerated)
+TEST_F(GetIrTest, RejectsValueWithoutKey)
 {
-    const char *inputMdpFile[] = {
-        "title simulation",
-        "xtc_grps = ",
-        "= -I/home/me/stuff",
-        "="
-    };
-    runTest(joinStrings(inputMdpFile, "\n"));
+    const char* inputMdpFile = "= -I/home/me/stuff";
+    GMX_EXPECT_DEATH_IF_SUPPORTED(runTest(inputMdpFile), "No .mdp parameter name was found");
+}
+
+TEST_F(GetIrTest, RejectsEmptyKeyAndEmptyValue)
+{
+    const char* inputMdpFile = " = ";
+    GMX_EXPECT_DEATH_IF_SUPPORTED(runTest(inputMdpFile),
+                                  "No .mdp parameter name or value was found");
 }
 
-TEST_F(GetIrTest, DefineHandlesAssignmentOnRhs)
+TEST_F(GetIrTest, AcceptsDefineParametersWithValuesIncludingAssignment)
 {
-    const char *inputMdpFile[] = {
+    const charinputMdpFile[] = {
         "define = -DBOOL -DVAR=VALUE",
     };
     runTest(joinStrings(inputMdpFile, "\n"));
 }
 
-TEST_F(GetIrTest, EmptyInputWorks)
+TEST_F(GetIrTest, AcceptsEmptyLines)
 {
-    const char *inputMdpFile = "";
+    const charinputMdpFile = "";
     runTest(inputMdpFile);
 }
 
+TEST_F(GetIrTest, MtsCheckNstcalcenergy)
+{
+    const char* inputMdpFile[] = {
+        "mts = yes", "mts-levels = 2", "mts-level2-factor = 2", "nstcalcenergy = 5"
+    };
+    runTest(joinStrings(inputMdpFile, "\n"), TestBehavior::ErrorAndDoNotCompareOutput);
+}
+
+TEST_F(GetIrTest, MtsCheckNstenergy)
+{
+    const char* inputMdpFile[] = {
+        "mts = yes", "mts-levels = 2", "mts-level2-factor = 2", "nstenergy = 5"
+    };
+    runTest(joinStrings(inputMdpFile, "\n"), TestBehavior::ErrorAndDoNotCompareOutput);
+}
+
+TEST_F(GetIrTest, MtsCheckNstpcouple)
+{
+    const char* inputMdpFile[] = { "mts = yes",
+                                   "mts-levels = 2",
+                                   "mts-level2-factor = 2",
+                                   "pcoupl = Berendsen",
+                                   "nstpcouple = 5" };
+    runTest(joinStrings(inputMdpFile, "\n"), TestBehavior::ErrorAndDoNotCompareOutput);
+}
+
+TEST_F(GetIrTest, MtsCheckNstdhdl)
+{
+    const char* inputMdpFile[] = {
+        "mts = yes", "mts-level2-factor = 2", "free-energy = yes", "nstdhdl = 5"
+    };
+    runTest(joinStrings(inputMdpFile, "\n"), TestBehavior::ErrorAndDoNotCompareOutput);
+}
+
 // These tests observe how the electric-field keys behave, since they
 // are currently the only ones using the new Options-style handling.
-TEST_F(GetIrTest, ProducesOutputFromElectricField)
+TEST_F(GetIrTest, AcceptsElectricField)
+{
+    const char* inputMdpFile = "electric-field-x = 1.2 0 0 0";
+    runTest(inputMdpFile);
+}
+
+TEST_F(GetIrTest, AcceptsElectricFieldPulsed)
 {
-    const char *inputMdpFile = "electric-field-x = 1.2 0 0 0";
+    const char* inputMdpFile = "electric-field-y = 3.7 2.0 6.5 1.0";
     runTest(inputMdpFile);
 }
 
-TEST_F(GetIrTest, ProducesOutputFromElectricFieldPulsed)
+TEST_F(GetIrTest, AcceptsElectricFieldOscillating)
 {
-    const char *inputMdpFile = "electric-field-y = 3.7 2.0 6.5 1.0";
+    const char* inputMdpFile = "electric-field-z = 3.7 7.5 0 0";
     runTest(inputMdpFile);
 }
 
-TEST_F(GetIrTest, ProducesOutputFromElectricFieldOscillating)
+TEST_F(GetIrTest, RejectsDuplicateOldAndNewKeys)
 {
-    const char *inputMdpFile = "electric-field-z = 3.7 7.5 0 0";
+    const char* inputMdpFile[] = { "verlet-buffer-drift = 1.3", "verlet-buffer-tolerance = 2.7" };
+    GMX_EXPECT_DEATH_IF_SUPPORTED(runTest(joinStrings(inputMdpFile, "\n")),
+                                  "A parameter is present with both");
+}
+
+TEST_F(GetIrTest, AcceptsImplicitSolventNo)
+{
+    const char* inputMdpFile = "implicit-solvent = no";
     runTest(inputMdpFile);
 }
 
-} // namespace
-} // namespace
+TEST_F(GetIrTest, RejectsImplicitSolventYes)
+{
+    const char* inputMdpFile = "implicit-solvent = yes";
+    GMX_EXPECT_DEATH_IF_SUPPORTED(runTest(inputMdpFile), "Invalid enum");
+}
+
+TEST_F(GetIrTest, AcceptsMimic)
+{
+    const char* inputMdpFile[] = { "integrator = mimic", "QMMM-grps = QMatoms" };
+    runTest(joinStrings(inputMdpFile, "\n"));
+}
+
+#if HAVE_MUPARSER
+
+TEST_F(GetIrTest, AcceptsTransformationCoord)
+{
+    const char* inputMdpFile[] = {
+        "pull = yes",
+        "pull-ngroups = 2",
+        "pull-ncoords = 2",
+        "pull-coord1-geometry = distance",
+        "pull-coord1-groups = 1 2",
+        "pull-coord1-k = 1",
+        "pull-coord2-geometry = transformation",
+        "pull-coord2-expression = 1/(1+x1)",
+        "pull-coord2-k = 10",
+    };
+    runTest(joinStrings(inputMdpFile, "\n"), TestBehavior::NoErrorAndDoNotCompareOutput);
+}
+
+TEST_F(GetIrTest, InvalidTransformationCoordWithConstraint)
+{
+    const char* inputMdpFile[] = {
+        "pull = yes",
+        "pull-ncoords = 1",
+        "pull-coord1-geometry = transformation",
+        "pull-coord1-type = constraint", // INVALID
+        "pull-coord1-expression = 10",
+    };
+    runTest(joinStrings(inputMdpFile, "\n"), TestBehavior::ErrorAndDoNotCompareOutput);
+}
+
+TEST_F(GetIrTest, InvalidPullCoordWithConstraintInTransformationExpression)
+{
+    const char* inputMdpFile[] = {
+        "pull = yes",
+        "pull-ngroups = 2",
+        "pull-ncoords = 2",
+        "pull-coord1-geometry = distance",
+        "pull-coord1-type = constraint", // INVALID
+        "pull-coord1-groups = 1 2",
+        "pull-coord2-geometry = transformation",
+        "pull-coord2-expression = x1",
+    };
+    runTest(joinStrings(inputMdpFile, "\n"), TestBehavior::ErrorAndDoNotCompareOutput);
+}
+
+TEST_F(GetIrTest, InvalidTransformationCoordDxValue)
+{
+    const char* inputMdpFile[] = {
+        "pull = yes",
+        "pull-ncoords = 1",
+        "pull-coord1-geometry = transformation",
+        "pull-coord1-expression = 10",
+        "pull-coord1-dx = 0", // INVALID
+    };
+    runTest(joinStrings(inputMdpFile, "\n"), TestBehavior::ErrorAndDoNotCompareOutput);
+}
+
+TEST_F(GetIrTest, MissingTransformationCoordExpression)
+{
+    const char* inputMdpFile[] = {
+        "pull = yes",
+        "pull-ncoords = 1",
+        "pull-coord1-geometry = transformation",
+    };
+    runTest(joinStrings(inputMdpFile, "\n"), TestBehavior::ErrorAndDoNotCompareOutput);
+}
+#endif // HAVE_MUPARSER
+
+} // namespace test
+} // namespace gmx