All coordinates in the density-guided-simulation group undergo an affine transformati...
authorChristian Blau <cblau.mail@gmail.com>
Tue, 15 Sep 2020 11:21:14 +0000 (11:21 +0000)
committerChristian Blau <cblau.mail@gmail.com>
Tue, 15 Sep 2020 11:21:14 +0000 (11:21 +0000)
Resolves part of #3581.

docs/reference-manual/special/density-guided-simulation.rst
docs/release-notes/2021/major/features.rst
docs/user-guide/mdp-options.rst
src/gromacs/applied_forces/densityfitting/densityfittingforceprovider.cpp
src/gromacs/applied_forces/densityfitting/densityfittingoptions.cpp
src/gromacs/applied_forces/densityfitting/densityfittingoptions.h
src/gromacs/applied_forces/densityfitting/densityfittingparameters.h
src/programs/mdrun/tests/densityfittingmodule.cpp
src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductIdentityMatrix.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductTranslationAndTransformationMatrix.xml [new file with mode: 0644]

index cb5315bf7b17989c7b0a716cdc7e0375a7335a80..6c921e36ae8806da4abdcc9bdb59cfd045186dfd 100644 (file)
@@ -204,15 +204,15 @@ forces when similarity cannot be increased further.
 
 Aligning input structure and density
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-To align input structure and density data, a shift vector 
-:math:`v_{\mathrm{shift}}` may be defined that translates the input structure
-atom coordinates before evaluating density-guided-simulation energies and forces,
-so that 
+To align input structure and density data, a transformation matrix
+:math:`\mathbf{A}` and shift vector :math:`\mathbf{v_{\mathrm{shift}}}` may be
+defined that transform the input structure atom coordinates before evaluating
+density-guided-simulation energies and forces, so that 
 
-.. math:: U = U_{\mathrm{forcefield}}(\mathbf{r}) - k S[\rho^{\mathrm{ref}},\rho^{\mathrm{sim}}\!(\mathbf{r+v_{\mathrm{shift}}})]\,\mathrm{.}
+.. math:: U = U_{\mathrm{forcefield}}(\mathbf{r}) - k S[\rho^{\mathrm{ref}},\rho^{\mathrm{sim}}\!(\mathbf{r+v_{\mathrm{shift}}})]\,\mathrm{.}
           :label:eqndensnine
 
-.. math:: \mathbf{F}_{\mathrm{density}} = k \nabla_{\mathbf{r}} S[\rho^{\mathrm{ref}},\rho^{\mathrm{sim}}\!(\mathbf{r+v_{\mathrm{shift}}})]\,\mathrm{.}
+.. math:: \mathbf{F}_{\mathrm{density}} = k \nabla_{\mathbf{r}} S[\rho^{\mathrm{ref}},\rho^{\mathrm{sim}}\!(\mathbf{r+v_{\mathrm{shift}}})]\,\mathrm{.}
           :label:eqndensten
 
 Future developments
index 27a93ec0434aaaad790932ca924db04a4d4ea3a7..7d289862f82a208cf9798b63959f54ea5d8ada19 100644 (file)
@@ -13,16 +13,20 @@ Virtual site with single constructing atom
 Added a virtual site that is constructed on top if its single constructing
 atom. This can be useful for free-energy calculations.
 
-Density-guided simulations can apply shift vector to structures
-"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+Density-guided simulations can apply matrix multiplication and shift vector to structures
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
 The new mdp option "density-guided-simulation-shift-vector" defines a
 shift vector that shifts the density-guided simulation group before the 
 density forces are evaluated. With a known shift vector that aligns structure
 and input density, this feature enables structure refinement to non-aligned
 densities without the need to manipulate the input density data or structure.
+The mdp option "density-guided-simulation-transformation-matrix" allows to 
+define a matrix with which to multiply the structure coordinates, before the shift
+vector is applied. This allows arbitrary rotation, skewing and scaling of input
+structures with respect to the input densities.
 A typical use case are membrane-embedded proteins which cannot easily be
-shifted.
+shifted and rotated within membranes.
 
 Lower energy drift due to SETTLE
 """"""""""""""""""""""""""""""""
index 2ea402530c4848f1e13d8e4a47b5b54969e476e1..4fa3b4a4cda831ef9d7a7bfac44cc72c1a4fc35c 100644 (file)
@@ -3240,6 +3240,14 @@ electron-microscopy experiments. (See the `reference manual`_ for details)
    and energies. Corresponds to a shift of the input density in the opposite
    direction by (-1) * density-guided-simulation-shift-vector.
 
+.. mdp:: density-guided-simulation-transformation-matrix
+
+   (1,0,0,0,1,0,0,0,1) Multiply all atoms with this matrix in the 
+   density-guided-simulation-group before calculating forces and energies for
+   density-guided-simulations. Affects only the density-guided-simulation forces
+   and energies. Corresponds to a transformation of the input density in the
+   opposite by the inverse of this matrix. The matrix is given in row-major order.
+
 User defined thingies
 ^^^^^^^^^^^^^^^^^^^^^
 
index b2963f6e03b58d8e786c8f1c516d48f277030392..c0dab954369b5faa312a254d21b821e5588a7e9e 100644 (file)
@@ -182,7 +182,7 @@ private:
     std::optional<ExponentialMovingAverage> expAverageSimilarity_;
 
     //! Optionally translate the structure
-    std::optional<TranslateAndScale> translate_;
+    std::optional<AffineTransformation> affineTransformation_;
 };
 
 DensityFittingForceProvider::Impl::~Impl() = default;
@@ -222,11 +222,20 @@ DensityFittingForceProvider::Impl::Impl(const DensityFittingParameters&
     // set up optional coordinate translation if the translation string contains a vector
     const std::optional<std::array<real, 3>> translationParametersAsArray =
             parsedArrayFromInputString<real, 3>(parameters_.translationString_);
-    if (translationParametersAsArray)
+    // set up optional coordinate transformation if the transformation string contains data
+    const std::optional<std::array<real, 9>> transformationMatrixParametersAsArray =
+            parsedArrayFromInputString<real, 9>(parameters_.transformationMatrixString_);
+    if (translationParametersAsArray || transformationMatrixParametersAsArray)
     {
-        translate_.emplace(RVec(1, 1, 1), RVec((*translationParametersAsArray)[XX],
-                                               (*translationParametersAsArray)[YY],
-                                               (*translationParametersAsArray)[ZZ]));
+        Matrix3x3 translationMatrix = transformationMatrixParametersAsArray.has_value()
+                                              ? *transformationMatrixParametersAsArray
+                                              : identityMatrix<real, 3>();
+        RVec translationVector = translationParametersAsArray.has_value()
+                                         ? RVec((*translationParametersAsArray)[XX],
+                                                (*translationParametersAsArray)[YY],
+                                                (*translationParametersAsArray)[ZZ])
+                                         : RVec(0, 0, 0);
+        affineTransformation_.emplace(translationMatrix.asConstView(), translationVector);
     }
 
     referenceDensityCenter_ = { real(referenceDensity.extent(XX)) / 2,
@@ -261,10 +270,10 @@ void DensityFittingForceProvider::Impl::calculateForces(const ForceProviderInput
                    std::begin(transformedCoordinates_),
                    [&forceProviderInput](int index) { return forceProviderInput.x_[index]; });
 
-    // optionally apply additional structure transformations
-    if (translate_)
+    // apply additional structure transformations
+    if (affineTransformation_)
     {
-        (*translate_)(transformedCoordinates_);
+        (*affineTransformation_)(transformedCoordinates_);
     }
 
     // pick periodic image that is closest to the center of the reference density
index 298d6913239bd4b636e8851996c4649e77ba339c..a3cb45794be772bde3c13ef916b7c9f60d2238cc 100644 (file)
@@ -153,6 +153,14 @@ void DensityFittingOptions::initMdpTransform(IKeyValueTreeTransformRules* rules)
     };
     densityfittingMdpTransformFromString<std::string>(rules, stringRVecToStringRVecWithCheck,
                                                       c_translationTag_);
+
+    const auto& stringMatrixToStringMatrixWithCheck = [](const std::string& str) {
+        return stringIdentityTransformWithArrayCheck<real, 9>(
+                str, "Reading nine real values as vector while parsing the .mdp input failed in "
+                             + DensityFittingModuleInfo::name_ + ".");
+    };
+    densityfittingMdpTransformFromString<std::string>(rules, stringMatrixToStringMatrixWithCheck,
+                                                      c_transformationMatrixTag_);
 }
 
 //! Name the methods that may be used to evaluate similarity between densities
@@ -245,6 +253,8 @@ void DensityFittingOptions::initMdpOptions(IOptionsContainerWithSections* option
     section.addOption(RealOption(c_adaptiveForceScalingTimeConstantTag_.c_str())
                               .store(&parameters_.adaptiveForceScalingTimeConstant_));
     section.addOption(StringOption(c_translationTag_.c_str()).store(&parameters_.translationString_));
+    section.addOption(
+            StringOption(c_transformationMatrixTag_.c_str()).store(&parameters_.transformationMatrixString_));
 }
 
 bool DensityFittingOptions::active() const
index b361eea5118debfd6de7859a3f3df8fe59170b7d..bbdf3176157d8a9403ec59427f6138ff68ea1374 100644 (file)
@@ -138,6 +138,8 @@ private:
 
     const std::string c_translationTag_ = "shift-vector";
 
+    const std::string c_transformationMatrixTag_ = "transformation-matrix";
+
     DensityFittingParameters parameters_;
 };
 
index 024c71f312a23e455cd4032d954a0f3ca9927009..cfafc1f6f5de9f13343a5d9ae9b9c15e80e43a5d 100644 (file)
@@ -84,6 +84,8 @@ struct DensityFittingParameters
     real adaptiveForceScalingTimeConstant_ = 4;
     //! Translation of the structure, so that the coordinates that are fitted are x+translation
     std::string translationString_ = "";
+    //! Linear transformat of the structure, so that the coordinates that are fitted are Matrix * x
+    std::string transformationMatrixString_ = "";
 };
 
 /*!\brief Check if two structs holding density fitting parameters are equal.
index 91c74f264816957578701f10cc620c3d80856c03..15501b455a0019e52362d6fd883975cec0c4683f 100644 (file)
@@ -131,10 +131,25 @@ public:
     const std::string mdpSkipDensityfittingEveryOtherStep_ = formatString(
             "nstenergy = 2\n"
             "density-guided-simulation-nst = 2\n");
+    //! A properly set shift vector
     const std::string mdpTranslationSet_ =
             formatString("density-guided-simulation-shift-vector = 0.1 -0.2 0.3\n");
+    //! A shift vector that is lacking an entry
     const std::string mdpTranslationSetWrongValues_ =
             formatString("density-guided-simulation-shift-vector = 0.1 -0.2\n");
+    //! A 45 degree rotation around the y axis expressed as matrix transformation
+    const std::string mdpTransformationMatrix1degAroundY_ = formatString(
+            "density-guided-simulation-transformation-matrix = 0.9998477 0.0000000 0.0174524 "
+            "0.0000000 1.0000000 0.0000000 -0.0174524 0.0000000 0.9998477 \n");
+    //! The identity matrix as transformation matrix
+    const std::string mdpTransformationMatrixIdentity_ = formatString(
+            "density-guided-simulation-transformation-matrix = 1 0 0 "
+            "0 1 0 0 0 1 \n");
+    //! A transformation matrix string where only eight values are given
+    const std::string mdpTransformationMatrixWrongValues_ = formatString(
+            "density-guided-simulation-transformation-matrix = 0.7071068 0.0000000 0.7071068 "
+            "0.0000000 0.0000000 -0.7071068 0.0000000 0.7071068 \n");
+
     //! The command line to call mdrun
     CommandLine commandLineForMdrun_;
 };
@@ -183,6 +198,51 @@ TEST_F(DensityFittingTest, EnergyMinimizationEnergyTranslationParametersOff)
     GMX_EXPECT_DEATH_IF_SUPPORTED(runner_.callGrompp(), ".*Reading three real values.*");
 }
 
+/* Fit a subset of three of twelve argon atoms into a reference density
+ * that are rotated around the simulation box origin by a matrix multiplication.
+ *
+ * All density fitting mdp parameters are set to defaults
+ */
+TEST_F(DensityFittingTest, EnergyMinimizationEnergyCorrectInnerProductTranslationAndTransformationMatrix)
+{
+    runner_.useStringAsMdpFile(mdpEminDensfitYesUnsetValues + mdpTranslationSet_
+                               + mdpTransformationMatrix1degAroundY_);
+
+    ASSERT_EQ(0, runner_.callGrompp());
+    ASSERT_EQ(0, runner_.callMdrun(commandLineForMdrun_));
+
+    const real expectedEnergyTermMagnitude = -8991;
+    checkMdrun(expectedEnergyTermMagnitude);
+}
+
+/* Fit a subset of three of twelve argon atoms into a reference density
+ * whose origin is offset from the simulation box origin.
+ *
+ * All density fitting mdp parameters are set to defaults
+ */
+TEST_F(DensityFittingTest, EnergyMinimizationEnergyMatrixTransfromationOff)
+{
+    runner_.useStringAsMdpFile(mdpEminDensfitYesUnsetValues + mdpTransformationMatrixWrongValues_);
+
+    GMX_EXPECT_DEATH_IF_SUPPORTED(runner_.callGrompp(), ".*Reading nine real values.*");
+}
+
+/* Fit a subset of three of twelve argon atoms into a reference density
+ * where the given matrix transformation is the identity transformation.
+ *
+ * All density fitting mdp parameters are set to defaults
+ */
+TEST_F(DensityFittingTest, EnergyMinimizationEnergyCorrectInnerProductIdentityMatrix)
+{
+    runner_.useStringAsMdpFile(mdpEminDensfitYesUnsetValues + mdpTransformationMatrixIdentity_);
+
+    ASSERT_EQ(0, runner_.callGrompp());
+    ASSERT_EQ(0, runner_.callMdrun(commandLineForMdrun_));
+
+    const real expectedEnergyTermMagnitude = -8991;
+    checkMdrun(expectedEnergyTermMagnitude);
+}
+
 /* Like above, but with as many parameters reversed as possible
  */
 TEST_F(DensityFittingTest, EnergyMinimizationEnergyCorrectForRelativeEntropy)
diff --git a/src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductIdentityMatrix.xml b/src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductIdentityMatrix.xml
new file mode 100644 (file)
index 0000000..5659fa1
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-3379.9202</Real>
+    <Real Name="Time 1.000000 Step 1 in frame 1">-3591.7776</Real>
+    <Real Name="Time 2.000000 Step 2 in frame 2">-3856.54</Real>
+  </Energy>
+  <Energy Name="Density fitting">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-3378.9026</Real>
+    <Real Name="Time 1.000000 Step 1 in frame 1">-3590.7383</Real>
+    <Real Name="Time 2.000000 Step 2 in frame 2">-3855.5586</Real>
+  </Energy>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductTranslationAndTransformationMatrix.xml b/src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductTranslationAndTransformationMatrix.xml
new file mode 100644 (file)
index 0000000..f272948
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-8933.2021</Real>
+    <Real Name="Time 1.000000 Step 1 in frame 1">-9308.2793</Real>
+    <Real Name="Time 2.000000 Step 2 in frame 2">-9760.4385</Real>
+  </Energy>
+  <Energy Name="Density fitting">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-8932.1846</Real>
+    <Real Name="Time 1.000000 Step 1 in frame 1">-9307.2402</Real>
+    <Real Name="Time 2.000000 Step 2 in frame 2">-9759.457</Real>
+  </Energy>
+</ReferenceData>