Note that adaptive force scaling does not conserve energy and will ultimately lead to very high
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
+
+.. 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{.}
+ :label:eqndensten
+
Future developments
^^^^^^^^^^^^^^^^^^^
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
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+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.
+A typical use case are membrane-embedded proteins which cannot easily be
+shifted.
+
Lower energy drift due to SETTLE
""""""""""""""""""""""""""""""""
(4) [ps] Couple force constant to increase in similarity with reference density
with this time constant. Larger times result in looser coupling.
+.. mdp:: density-guided-simulation-shift-vector
+
+ (0,0,0) [nm] Add this vector to all atoms 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 shift of the input density in the opposite
+ direction by (-1) * density-guided-simulation-shift-vector.
+
User defined thingies
^^^^^^^^^^^^^^^^^^^^^
#include "gromacs/mdtypes/enerdata.h"
#include "gromacs/mdtypes/forceoutput.h"
#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/utility/strconvert.h"
#include "densityfittingamplitudelookup.h"
#include "densityfittingparameters.h"
//! Optionally scale the force according to a moving average of the similarity
std::optional<ExponentialMovingAverage> expAverageSimilarity_;
+
+ //! Optionally translate the structure
+ std::optional<TranslateAndScale> translate_;
};
DensityFittingForceProvider::Impl::~Impl() = default;
/ (simulationTimeStep * parameters_.calculationIntervalInSteps_),
state.exponentialMovingAverageState_));
}
+
+ // 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)
+ {
+ translate_.emplace(RVec(1, 1, 1), RVec((*translationParametersAsArray)[XX],
+ (*translationParametersAsArray)[YY],
+ (*translationParametersAsArray)[ZZ]));
+ }
+
referenceDensityCenter_ = { real(referenceDensity.extent(XX)) / 2,
real(referenceDensity.extent(YY)) / 2,
real(referenceDensity.extent(ZZ)) / 2 };
std::begin(transformedCoordinates_),
[&forceProviderInput](int index) { return forceProviderInput.x_[index]; });
+ // optionally apply additional structure transformations
+ if (translate_)
+ {
+ (*translate_)(transformedCoordinates_);
+ }
+
// pick periodic image that is closest to the center of the reference density
{
t_pbc pbc;
densityfittingMdpTransformFromString<bool>(rules, &fromStdString<bool>, c_adaptiveForceScalingTag_);
densityfittingMdpTransformFromString<real>(rules, &fromStdString<real>,
c_adaptiveForceScalingTimeConstantTag_);
+ const auto& stringRVecToStringRVecWithCheck = [](const std::string& str) {
+ return stringIdentityTransformWithArrayCheck<real, 3>(
+ str, "Reading three real values as vector while parsing the .mdp input failed in "
+ + DensityFittingModuleInfo::name_ + ".");
+ };
+ densityfittingMdpTransformFromString<std::string>(rules, stringRVecToStringRVecWithCheck,
+ c_translationTag_);
}
//! Name the methods that may be used to evaluate similarity between densities
BooleanOption(c_adaptiveForceScalingTag_.c_str()).store(¶meters_.adaptiveForceScaling_));
section.addOption(RealOption(c_adaptiveForceScalingTimeConstantTag_.c_str())
.store(¶meters_.adaptiveForceScalingTimeConstant_));
+ section.addOption(StringOption(c_translationTag_.c_str()).store(¶meters_.translationString_));
}
bool DensityFittingOptions::active() const
const std::string c_adaptiveForceScalingTimeConstantTag_ =
"adaptive-force-scaling-time-constant";
+ const std::string c_translationTag_ = "shift-vector";
DensityFittingParameters parameters_;
};
bool adaptiveForceScaling_ = false;
//! The time constant for the adaptive force scaling in ps
real adaptiveForceScalingTimeConstant_ = 4;
+ //! Translation of the structure, so that the coordinates that are fitted are x+translation
+ std::string translationString_ = "";
};
/*!\brief Check if two structs holding density fitting parameters are equal.
const std::string mdpSkipDensityfittingEveryOtherStep_ = formatString(
"nstenergy = 2\n"
"density-guided-simulation-nst = 2\n");
+ const std::string mdpTranslationSet_ =
+ formatString("density-guided-simulation-shift-vector = 0.1 -0.2 0.3\n");
+ const std::string mdpTranslationSetWrongValues_ =
+ formatString("density-guided-simulation-shift-vector = 0.1 -0.2\n");
//! The command line to call mdrun
CommandLine commandLineForMdrun_;
};
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, EnergyMinimizationEnergyCorrectInnerProductTranslation)
+{
+ runner_.useStringAsMdpFile(mdpEminDensfitYesUnsetValues + mdpTranslationSet_);
+
+ 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, EnergyMinimizationEnergyTranslationParametersOff)
+{
+ runner_.useStringAsMdpFile(mdpEminDensfitYesUnsetValues + mdpTranslationSetWrongValues_);
+
+ GMX_EXPECT_DEATH_IF_SUPPORTED(runner_.callGrompp(), ".*Reading three real values.*");
+}
+
/* Like above, but with as many parameters reversed as possible
*/
TEST_F(DensityFittingTest, EnergyMinimizationEnergyCorrectForRelativeEntropy)
--- /dev/null
+<?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">-8991.123</Real>
+ <Real Name="Time 1.000000 Step 1 in frame 1">-9367.3584</Real>
+ <Real Name="Time 2.000000 Step 2 in frame 2">-9820.8447</Real>
+ </Energy>
+ <Energy Name="Density fitting">
+ <Real Name="Time 0.000000 Step 0 in frame 0">-8990.1055</Real>
+ <Real Name="Time 1.000000 Step 1 in frame 1">-9366.3193</Real>
+ <Real Name="Time 2.000000 Step 2 in frame 2">-9819.8525</Real>
+ </Energy>
+</ReferenceData>