Add reading and writing to AWH module
authorViveca Lindahl <vivecalindahl@gmail.com>
Mon, 28 Mar 2016 12:31:42 +0000 (14:31 +0200)
committerErik Lindahl <erik.lindahl@gmail.com>
Mon, 20 Nov 2017 13:04:59 +0000 (14:04 +0100)
This change adds IO to the AWH module. AWH writes coordinate
free energies and distributions to an energy file block. The
reading is handled by a new tool gmx awh.

Change-Id: Ie30991bca376c2a648371db771fc5dfd8fca3715

24 files changed:
docs/user-guide/mdp-options.rst
src/gromacs/awh/awh.cpp
src/gromacs/awh/awh.h
src/gromacs/awh/bias.cpp
src/gromacs/awh/bias.h
src/gromacs/awh/biasparams.cpp
src/gromacs/awh/biasparams.h
src/gromacs/awh/biasstate.cpp
src/gromacs/awh/biasstate.h
src/gromacs/awh/biaswriter.cpp [new file with mode: 0644]
src/gromacs/awh/biaswriter.h [new file with mode: 0644]
src/gromacs/awh/histogramsize.h
src/gromacs/awh/read-params.cpp
src/gromacs/fileio/enxio.cpp
src/gromacs/fileio/enxio.h
src/gromacs/fileio/xvgr.cpp
src/gromacs/fileio/xvgr.h
src/gromacs/gmxana/gmx_ana.h
src/gromacs/gmxana/gmx_awh.cpp [new file with mode: 0644]
src/gromacs/mdlib/mdebin.cpp
src/gromacs/mdlib/mdebin.h
src/gromacs/mdlib/minimize.cpp
src/programs/legacymodules.cpp
src/programs/mdrun/md.cpp

index f65980377673c0e282effa965b2f31d255556b5f..8caac068b2a2038b615469fc8231b569a6c653dc 100644 (file)
@@ -2014,7 +2014,7 @@ AWH adaptive biasing
       each input file should contain the coordinate values, such that each row defines a point in
       coordinate space. Column :mdp:`awh1-ndim` + 1 should contain the PMF value for each point.
       The target distribution column can either follow the PMF (column  :mdp:`awh1-ndim` + 2) or
-      be in the same column as written by ``gmx awh``.
+      be in the same column as written by :ref:`gmx awh`.
 
 .. mdp:: awh1-share-group
 
index 02ff69114f4f2b49091aef6f5401d7a9c598be4f..ff8749f416946f092f7acf00c49aae0bf0ef4e63 100644 (file)
@@ -55,6 +55,7 @@
 
 #include <algorithm>
 
+#include "gromacs/fileio/enxio.h"
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/math/units.h"
 #include "gromacs/mdtypes/awh-history.h"
@@ -105,7 +106,7 @@ struct BiasCoupledToSystem
 
 BiasCoupledToSystem::BiasCoupledToSystem(Bias                    bias,
                                          const std::vector<int> &pullCoordIndex) :
-    bias(bias),
+    bias(std::move(bias)),
     pullCoordIndex(pullCoordIndex)
 {
     /* We already checked for this in grompp, but check again here. */
@@ -119,6 +120,7 @@ Awh::Awh(FILE              *fplog,
          const std::string &biasInitFilename,
          pull_t            *pull_work) :
     seed_(awhParams.seed),
+    nstout_(awhParams.nstOut),
     commRecord_(commRecord),
     pull_(pull_work),
     potentialOffset_(0)
@@ -186,6 +188,11 @@ Awh::Awh(FILE              *fplog,
 
 Awh::~Awh() = default;
 
+bool Awh::isOutputStep(gmx_int64_t step) const
+{
+    return (nstout_ > 0 && step % nstout_ == 0);
+}
+
 real Awh::applyBiasForcesAndUpdateBias(int                     ePBC,
                                        const t_mdatoms        &mdatoms,
                                        const matrix            box,
@@ -248,6 +255,12 @@ real Awh::applyBiasForcesAndUpdateBias(int                     ePBC,
                                             biasForce[d], &mdatoms,
                                             forceWithVirial);
         }
+
+        if (isOutputStep(step))
+        {
+            /* Ensure that we output fully updated data */
+            biasCts.bias.doSkippedUpdatesForAllPoints();
+        }
     }
 
     wallcycle_stop(wallcycle, ewcAWH);
@@ -341,4 +354,43 @@ void Awh::registerAwhWithPull(const AwhParams &awhParams,
     }
 }
 
+/* Fill the AWH data block of an energy frame with data (if there is any). */
+void Awh::writeToEnergyFrame(gmx_int64_t  step,
+                             t_enxframe  *frame) const
+{
+    GMX_ASSERT(MASTER(commRecord_), "writeToEnergyFrame should only be called on the master rank");
+    GMX_ASSERT(frame != nullptr, "Need a valid energy frame");
+
+    if (!isOutputStep(step))
+    {
+        /* This is not an AWH output step, don't write any AWH data */
+        return;
+    }
+
+    /* Get the total number of energy subblocks that AWH needs */
+    int numSubblocks  = 0;
+    for (auto &biasCoupledToSystem : biasCoupledToSystem_)
+    {
+        numSubblocks += biasCoupledToSystem.bias.numEnergySubblocksToWrite();
+    }
+    GMX_ASSERT(numSubblocks > 0, "We should always have data to write");
+
+    /* Add 1 energy block */
+    add_blocks_enxframe(frame, frame->nblock + 1);
+
+    /* Take the block that was just added and set the number of subblocks. */
+    t_enxblock *awhEnergyBlock = &(frame->block[frame->nblock - 1]);
+    add_subblocks_enxblock(awhEnergyBlock, numSubblocks);
+
+    /* Claim it as an AWH block. */
+    awhEnergyBlock->id = enxAWH;
+
+    /* Transfer AWH data blocks to energy sub blocks */
+    int energySubblockCount = 0;
+    for (auto &biasCoupledToSystem : biasCoupledToSystem_)
+    {
+        energySubblockCount += biasCoupledToSystem.bias.writeToEnergySubblocks(&(awhEnergyBlock->sub[energySubblockCount]));
+    }
+}
+
 } // namespace gmx
index f074bbbc0d5d16c7e3b1feb3456cfc989e07fc4a..1fe09c9ff14d53cd342454a492ed11cf995a2648 100644 (file)
@@ -77,6 +77,7 @@ struct pull_work_t;
 struct pull_t;
 class t_state;
 struct t_commrec;
+struct t_enxframe;
 struct t_inputrec;
 struct t_mdatoms;
 
@@ -155,7 +156,7 @@ class Awh
          * \param[in]     box              Box vectors.
          * \param[in,out] forceWithVirial  Force and virial buffers, should cover at least the local atoms.
          * \param[in]     t                Time.
-         * \param[in]     step             Time step.
+         * \param[in]     step             The current MD step.
          * \param[in,out] wallcycle        Wallcycle counter, can be nullptr.
          * \param[in,out] fplog            General output file, normally md.log, can be nullptr.
          * \returns the potential energy for the bias.
@@ -203,6 +204,14 @@ class Awh
          */
         void restoreStateFromHistory(const AwhHistory *awhHistory);
 
+        /*! \brief Fills the AWH data block of an energy frame with data at certain steps.
+         *
+         * \param[in]     step  The current MD step.
+         * \param[in,out] fr    Energy data frame.
+         */
+        void writeToEnergyFrame(gmx_int64_t  step,
+                                t_enxframe  *fr) const;
+
         /*! \brief Returns string "AWH" for registering AWH as an external potential provider with the pull module.
          */
         static const char *externalPotentialString();
@@ -221,9 +230,17 @@ class Awh
         static void registerAwhWithPull(const AwhParams &awhParams,
                                         pull_t          *pull_work);
 
+    private:
+        /*! \brief Returns whether we need to write output at the current step.
+         *
+         * \param[in]     step             The current MD step.
+         */
+        bool isOutputStep(gmx_int64_t step) const;
+
     private:
         std::vector<BiasCoupledToSystem> biasCoupledToSystem_; /**< AWH biases and definitions of their coupling to the system. */
         const gmx_int64_t                seed_;                /**< Random seed for MC jumping with umbrella type bias potential. */
+        const int                        nstout_;              /**< Interval in steps for writing to energy file. */
         const t_commrec                 *commRecord_;          /**< Pointer to the communication record. */
         pull_t                          *pull_;                /**< Pointer to the pull working data. */
         double                           potentialOffset_;     /**< The offset of the bias potential which changes due to bias updates. */
index 36f3af0e42807a5de75e6e57d79718c8869bbcd8..1803063d5a99e777f1a12bf64c6991d6afa93b7c 100644 (file)
@@ -93,7 +93,10 @@ void Bias::warnForHistogramAnomalies(double t, gmx_int64_t step, FILE *fplog)
 
 void Bias::doSkippedUpdatesForAllPoints()
 {
-    state_.doSkippedUpdatesForAllPoints(params_);
+    if (params_.skipUpdates())
+    {
+        state_.doSkippedUpdatesForAllPoints(params_);
+    }
 }
 
 void Bias::calcForceAndUpdateBias(const awh_dvec        coordValue,
@@ -250,6 +253,27 @@ Bias::Bias(int                             biasIndexInCollection,
     updateList_.reserve(grid_.numPoints());
 
     state_.initGridPointState(awhBiasParams, dimParams_, grid_, params_, biasInitFilename, awhParams.numBias);
+
+    if (thisRankDoesIO_)
+    {
+        writer_ = std::unique_ptr<BiasWriter>(new BiasWriter(*this));
+    }
+}
+
+/* Return the number of data blocks that have been prepared for writing. */
+int Bias::numEnergySubblocksToWrite() const
+{
+    GMX_RELEASE_ASSERT(writer_ != nullptr, "Should only request data from an initialized writer");
+
+    return writer_->numBlocks();
+}
+
+/* Write bias data blocks to energy subblocks. */
+int Bias::writeToEnergySubblocks(t_enxsubblock *subblock) const
+{
+    GMX_RELEASE_ASSERT(writer_ != nullptr, "Should only request data from an initialized writer");
+
+    return writer_->writeToEnergySubblocks(*this, subblock);
 }
 
 } // namespace gmx
index 95db7d4c61425939a17b61dae2a696876512a10c..841f08098525ae75522881db6d2161b1de4569df 100644 (file)
 
 #include "biasparams.h"
 #include "biasstate.h"
+#include "biaswriter.h"
 #include "dimparams.h"
 #include "grid.h"
 
 struct gmx_multisim_t;
 struct t_commrec;
+struct t_enxsubblock;
 
 namespace gmx
 {
@@ -206,7 +208,7 @@ class Bias
          * sense as a relative, to other coordinate values, measure of the bias.
          *
          * \param[in] coordValue  The coordinate value.
-         * \returns the convolved bias >= -GMX_DOUBLE_MAX.
+         * \returns the convolved bias >= -GMX_FLOAT_MAX.
          */
         double calcConvolvedBias(const awh_dvec &coordValue) const
         {
@@ -277,6 +279,17 @@ class Bias
             return params_.biasIndex;
         }
 
+        /*! \brief Return the coordinate value for a grid point.
+         *
+         * \param[in] gridPointIndex  The index of the grid point.
+         */
+        inline const awh_dvec &getGridCoordValue(size_t gridPointIndex) const
+        {
+            GMX_ASSERT(gridPointIndex < grid_.numPoints(), "gridPointIndex should be in the range of the grid");
+
+            return grid_.point(gridPointIndex).coordValue;
+        }
+
     private:
         /*! \brief
          * Performs statistical checks on the collected histograms and warns if issues are detected.
@@ -289,6 +302,18 @@ class Bias
                                        gmx_int64_t  step,
                                        FILE        *fplog);
 
+    public:
+        /*! \brief Return the number of data blocks that have been prepared for writing.
+         */
+        int numEnergySubblocksToWrite() const;
+
+        /*! \brief Write bias data blocks to energy subblocks.
+         *
+         * \param[in,out] subblock  Energy subblocks to write to.
+         * \returns the number of subblocks written.
+         */
+        int writeToEnergySubblocks(t_enxsubblock *subblock) const;
+
         /* Data members. */
     private:
         const std::vector<DimParams> dimParams_;         /**< Parameters for each dimension. */
@@ -301,6 +326,9 @@ class Bias
 
         const bool                   thisRankDoesIO_;    /**< Tells whether this MPI rank will do I/O (checkpointing, AWH output) */
 
+        /* I/O */
+        std::unique_ptr<BiasWriter>  writer_;      /**< Takes care of AWH data output. */
+
         /* Temporary working vector used during the update.
          * This only here to avoid allocation at every MD step.
          */
index 13347147b7b9c6975979fa5d92f8c77163736594..d34cb9550877ae5f8664662392f8cb05caeeb6d0 100644 (file)
@@ -289,6 +289,7 @@ BiasParams::BiasParams(const AwhParams              &awhParams,
     numSharedUpdate(getNumSharedUpdate(awhBiasParams, numSharingSimulations)),
     updateWeight(numSamplesUpdateFreeEnergy_*numSharedUpdate),
     localWeightScaling(eTarget == eawhtargetLOCALBOLTZMANN ? temperatureScaleFactor : 1),
+    initialErrorInKT(beta*awhBiasParams.errorInitial),
     initialHistogramSize(getInitialHistogramSizeEstimate(dimParams, awhBiasParams, gridAxis, beta, numStepsSampleCoord_*mdTimeStep)),
     convolveForce(awhParams.ePotential == eawhpotentialCONVOLVED),
     biasIndex(biasIndex),
index ff2938f36dd0db4e4de92e73a990c475595e6406..3455fe54b745d7b21d04368d16b19d3f7dc1d4dd 100644 (file)
@@ -202,6 +202,7 @@ class BiasParams
         const int         numSharedUpdate;             /**< The number of (multi-)simulations sharing the bias update */
         const double      updateWeight;                /**< The probability weight accumulated for each update. */
         const double      localWeightScaling;          /**< Scaling factor applied to a sample before adding it to the reference weight histogram (= 1, usually). */
+        const double      initialErrorInKT;            /**< Estimated initial free energy error in kT. */
         const double      initialHistogramSize;        /**< Initial reference weight histogram size. */
     private:
         awh_ivec          coverRadius_;                /**< The radius (in points) that needs to be sampled around a point before it is considered covered. */
index a57efb3c60d1607c0696deea14d915226562f4e2..fea6c83490f9c4db8fc43c950179a96a50d43939 100644 (file)
 namespace gmx
 {
 
-void BiasState::getPmf(std::vector<float> *pmf) const
+void BiasState::getPmf(gmx::ArrayRef<float> pmf) const
 {
+    GMX_ASSERT(pmf.size() == points_.size(), "pmf should have the size of the bias grid");
+
     /* The PMF is just the negative of the log of the sampled PMF histogram.
      * Points with zero target weight are ignored, they will mostly contain noise.
      */
-
-    pmf->resize(points_.size());
-
     for (size_t i = 0; i < points_.size(); i++)
     {
-        (*pmf)[i] = points_[i].inTargetRegion() ? -points_[i].logPmfSum() : GMX_FLOAT_MAX;
+        pmf[i] = points_[i].inTargetRegion() ? -points_[i].logPmfSum() : GMX_FLOAT_MAX;
     }
 }
 
@@ -140,7 +139,7 @@ void sumPmf(gmx::ArrayRef<PointState>  pointState,
  */
 double freeEnergyMinimumValue(gmx::ArrayRef<const PointState> pointState)
 {
-    double fMin = GMX_DOUBLE_MAX;
+    double fMin = GMX_FLOAT_MAX;
 
     for (auto const &ps : pointState)
     {
@@ -204,8 +203,8 @@ void BiasState::calcConvolvedPmf(const std::vector<DimParams>  &dimParams,
     convolvedPmf->resize(numPoints);
 
     /* Get the PMF to convolve. */
-    std::vector<float> pmf;
-    getPmf(&pmf);
+    std::vector<float> pmf(numPoints);
+    getPmf(pmf);
 
     for (size_t m = 0; m < numPoints; m++)
     {
@@ -303,7 +302,7 @@ int BiasState::warnForHistogramAnomalies(const Grid  &grid,
                                          FILE        *fplog,
                                          int          maxNumWarnings) const
 {
-    const double maxHistogramRatio = 0.5; /* Tolerance for printing a warning about the histogram ratios */
+    const double maxHistogramRatio = 0.5;  /* Tolerance for printing a warning about the histogram ratios */
 
     /* Sum up the histograms and get their normalization */
     double sumVisits  = 0;
@@ -446,10 +445,10 @@ double BiasState::moveUmbrella(const std::vector<DimParams> &dimParams,
     for (size_t d = 0; d < dimParams.size(); d++)
     {
         /* clang thinks newForce[d] can be garbage */
-#ifndef __clang_analyzer__
+ #ifndef __clang_analyzer__
         /* Average of the current and new force */
         biasForce[d] = 0.5*(biasForce[d] + newForce[d]);
-#endif
+ #endif
     }
 
     return newPotential;
@@ -863,7 +862,7 @@ bool BiasState::isSamplingRegionCovered(const BiasParams             &params,
        of the target region are ignored. */
 
     /* Set the free energy cutoff */
-    double maxFreeEnergy = GMX_DOUBLE_MAX;
+    double maxFreeEnergy = GMX_FLOAT_MAX;
 
     if (params.eTarget == eawhtargetCUTOFF)
     {
@@ -1170,8 +1169,8 @@ double BiasState::calcConvolvedBias(const std::vector<DimParams>  &dimParams,
         weightSum       += std::exp(logWeight);
     }
 
-    /* Returns -GMX_DOUBLE_MAX if no neighboring points were in the target region. */
-    return (weightSum > 0) ? std::log(weightSum) : -GMX_DOUBLE_MAX;
+    /* Returns -GMX_FLOAT_MAX if no neighboring points were in the target region. */
+    return (weightSum > 0) ? std::log(weightSum) : -GMX_FLOAT_MAX;
 }
 
 void BiasState::sampleProbabilityWeights(const Grid                  &grid,
index 1a541c606cbd8ef3901cadeb568d035ad7ea71e0..87c532c455592cbf2c1b6f057333914b7be19edb 100644 (file)
@@ -469,22 +469,22 @@ class BiasState
          * \param[in] dimParams   The bias dimensions parameters
          * \param[in] grid        The grid.
          * \param[in] coordValue  Coordinate value.
-         * \returns the convolved bias >= -GMX_DOUBLE_MAX.
+         * \returns the convolved bias >= -GMX_FLOAT_MAX.
          */
         double calcConvolvedBias(const std::vector<DimParams>  &dimParams,
                                  const Grid                    &grid,
                                  const awh_dvec                &coordValue) const;
 
         /*! \brief
-         * Fills the given array with PMF values, resizes if necessary.
+         * Fills the given array with PMF values.
          *
          * Points outside of the biasing target region will get PMF = GMX_FLOAT_MAX.
          * \note: The PMF is in single precision, because it is a statistical
          *        quantity and therefore never reaches full float precision.
          *
-         * \param[in,out] pmf        Array returned will be of the same length as the AWH grid to store the PMF in.
+         * \param[out] pmf  Array(ref) to be filled with the PMF values, should have the same size as the bias grid.
          */
-        void getPmf(std::vector<float> *pmf) const;
+        void getPmf(gmx::ArrayRef<float>) const;
 
         /*! \brief Returns the current coordinate state.
          */
@@ -507,6 +507,13 @@ class BiasState
             return histogramSize_.inInitialStage();
         };
 
+        /*! \brief Returns the current histogram size.
+         */
+        inline HistogramSize histogramSize() const
+        {
+            return histogramSize_;
+        };
+
         /* Data members */
     private:
         CoordState             coordState_; /**< The Current coordinate state */
diff --git a/src/gromacs/awh/biaswriter.cpp b/src/gromacs/awh/biaswriter.cpp
new file mode 100644 (file)
index 0000000..1e9ab20
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#include "gmxpre.h"
+
+#include "biaswriter.h"
+
+#include <assert.h>
+
+#include <cmath>
+
+#include "gromacs/awh/awh.h"
+#include "gromacs/fileio/enxio.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/mdtypes/commrec.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/smalloc.h"
+
+#include "bias.h"
+#include "grid.h"
+#include "pointstate.h"
+
+namespace gmx
+{
+
+namespace
+{
+
+/*! \brief
+ * Map the output entry type to a normalization type.
+ *
+ * The data is written to energy file blocks in the order given by
+ * the iterator of this map, which is based on the enum value
+ * (and matches the order of the lines below).
+ */
+static const std::map<AwhOutputEntryType, Normalization> outputTypeToNormalization =
+{
+    { AwhOutputEntryType::MetaData,   Normalization::None },
+    { AwhOutputEntryType::CoordValue, Normalization::Coordinate },
+    { AwhOutputEntryType::Pmf,        Normalization::FreeEnergy },
+    { AwhOutputEntryType::Bias,       Normalization::FreeEnergy },
+    { AwhOutputEntryType::Visits,     Normalization::Distribution },
+    { AwhOutputEntryType::Weights,    Normalization::Distribution },
+    { AwhOutputEntryType::Target,     Normalization::Distribution }
+};
+
+/*! \brief
+ * Gets the coordinate normalization value for the given dimension.
+ *
+ * \param[in] bias      The AWH bias.
+ * \param[in] dimIndex  Dimensional index.
+ * \returns the coordinate normalization value.
+ */
+float getCoordNormalizationValue(const Bias &bias,
+                                 int         dimIndex)
+{
+    /* AWH may use different units internally but here we convert to user units */
+    return bias.dimParams()[dimIndex].scaleInternalToUserInput(1);
+}
+
+/*! \brief
+ * Gets the normalization value for the given output entry type.
+ *
+ * \param[in] outputType  Output entry type.
+ * \param[in] bias        The AWH bias.
+ * \param[in] numBlocks   The number of blocks for this output type.
+ * \returns the normalization value.
+ */
+float getNormalizationValue(AwhOutputEntryType  outputType,
+                            const Bias         &bias,
+                            int                 numBlocks)
+{
+    float normalizationValue = 0;
+
+    switch (outputType)
+    {
+        case AwhOutputEntryType::CoordValue:
+            normalizationValue = getCoordNormalizationValue(bias, numBlocks);
+            break;
+        case AwhOutputEntryType::Visits:
+        case AwhOutputEntryType::Weights:
+        case AwhOutputEntryType::Target:
+            normalizationValue = static_cast<float>(bias.state().points().size());
+            break;
+        default:
+            break;
+    }
+
+    return normalizationValue;
+}
+
+}   // namespace
+
+AwhEnergyBlock::AwhEnergyBlock(int            numPoints,
+                               Normalization  normalizationType,
+                               float          normalizationValue) :
+    normalizationType(normalizationType),
+    normalizationValue(normalizationValue),
+    data_(numPoints)
+{
+}
+
+BiasWriter::BiasWriter(const Bias &bias)
+{
+    std::map<AwhOutputEntryType, int> outputTypeNumBlock; /* Number of blocks per output type */
+
+    /* Different output variable types need different number of blocks.
+     * We keep track of the starting block for each variable.
+     */
+    int blockCount = 0;
+    for (const auto &pair : outputTypeToNormalization)
+    {
+        const AwhOutputEntryType outputType = pair.first;
+        {
+            outputTypeToBlock_[outputType] = blockCount;
+
+            if (outputType == AwhOutputEntryType::CoordValue)
+            {
+                outputTypeNumBlock[outputType] = bias.ndim();
+            }
+            else
+            {
+                /* Most output variable types need one block */
+                outputTypeNumBlock[outputType] = 1;
+            }
+        }
+        blockCount += outputTypeNumBlock[outputType];
+    }
+
+    /* Initialize the data blocks for each variable */
+    for (const auto &pair : outputTypeToNormalization)
+    {
+        const AwhOutputEntryType outputType = pair.first;
+        int                      numPoints;
+        if (outputType == AwhOutputEntryType::MetaData)
+        {
+            numPoints = static_cast<int>(AwhOutputMetaData::Count);
+        }
+        else
+        {
+            numPoints = bias.state().points().size();
+        }
+        for (int b = 0; b < outputTypeNumBlock[outputType]; b++)
+        {
+            block_.push_back(AwhEnergyBlock(numPoints,
+                                            pair.second,
+                                            getNormalizationValue(outputType, bias, b)));
+        }
+    }
+}
+
+/*! \brief
+ * Normalizes block data for output.
+ *
+ * \param[in,out] block  The block to normalize.
+ * \param[in]     bias   The AWH bias.
+ */
+static void normalizeBlock(AwhEnergyBlock *block, const Bias &bias)
+{
+    gmx::ArrayRef<float> data = block->data();
+
+    /* Here we operate on float data (which is accurate enough, since it
+     * is statistical data that will never reach full float precision).
+     * But since we can have very many data points, we sum into a double.
+     */
+    double sum       = 0;
+    float  minValue  = GMX_FLOAT_MAX;
+    float  recipNorm = 0;
+
+    switch (block->normalizationType)
+    {
+        case Normalization::None:
+            break;
+        case Normalization::Coordinate:
+            /* Normalize coordinate values by a scale factor */
+            for (float &point : data)
+            {
+                point *= block->normalizationValue;
+            }
+            break;
+        case Normalization::FreeEnergy:
+            /* Normalize free energy values by subtracting the minimum value */
+            for (size_t index = 0; index < data.size(); index++)
+            {
+                if (bias.state().points()[index].inTargetRegion() && data[index] < minValue)
+                {
+                    minValue = data[index];
+                }
+            }
+            for (size_t index = 0; index < data.size(); index++)
+            {
+                if (bias.state().points()[index].inTargetRegion())
+                {
+                    data[index] -= minValue;
+                }
+            }
+
+            break;
+        case Normalization::Distribution:
+            /* Normalize distribution values by normalizing their sum */
+            for (float &point : data)
+            {
+                sum += point;
+            }
+            if (sum > 0)
+            {
+                recipNorm = block->normalizationValue/static_cast<float>(sum);
+            }
+            for (float &point : data)
+            {
+                point *= recipNorm;
+            }
+            break;
+        default:
+            GMX_RELEASE_ASSERT(false, "Unknown AWH normalization type");
+            break;
+    }
+}
+
+void BiasWriter::transferMetaDataToWriter(size_t             metaDataIndex,
+                                          AwhOutputMetaData  metaDataType,
+                                          const Bias        &bias)
+{
+    gmx::ArrayRef<float> data = block_[getVarStartBlock(AwhOutputEntryType::MetaData)].data();
+    GMX_ASSERT(metaDataIndex < data.size(), "Attempt to transfer AWH meta data to block for index out of range");
+
+    /* Transfer the point data of this variable to the right block(s) */
+    switch (metaDataType)
+    {
+        case AwhOutputMetaData::NumBlock:
+            /* The number of subblocks per awh (needed by gmx_energy) */
+            data[metaDataIndex] = static_cast<double>(block_.size());
+            /* Note: a single subblock takes only a single type and we need doubles. */
+            break;
+        case AwhOutputMetaData::TargetError:
+            /* The theoretical target error */
+            data[metaDataIndex] = bias.params().initialErrorInKT*std::sqrt(bias.params().initialHistogramSize/bias.state().histogramSize().histogramSize());
+            break;
+        case AwhOutputMetaData::ScaledSampleWeight:
+            /* The logarithm of the sample weight relative to a sample weight of 1 at the initial time.
+               In the normal case: this will increase in the initial stage and then stay at a constant value. */
+            data[metaDataIndex] = bias.state().histogramSize().logScaledSampleWeight();
+            break;
+        case AwhOutputMetaData::Count:
+            break;
+    }
+}
+
+void
+BiasWriter::transferPointDataToWriter(AwhOutputEntryType          outputType,
+                                      int                         pointIndex,
+                                      const Bias                 &bias,
+                                      gmx::ArrayRef<const float>  pmf)
+{
+    /* The starting block index of this output type.
+     * Note that some variables need several (contiguous) blocks.
+     */
+    int blockStart = getVarStartBlock(outputType);
+    GMX_ASSERT(pointIndex < static_cast<int>(block_[blockStart].data().size()), "Attempt to transfer AWH data to block for point index out of range");
+
+    /* Transfer the point data of this variable to the right block(s) */
+    int b = blockStart;
+    switch (outputType)
+    {
+        case AwhOutputEntryType::MetaData:
+            GMX_RELEASE_ASSERT(false, "MetaData is handled by a different function");
+            break;
+        case AwhOutputEntryType::CoordValue:
+        {
+            const awh_dvec &coordValue = bias.getGridCoordValue(pointIndex);
+            for (int d = 0; d < bias.ndim(); d++)
+            {
+                block_[b].data()[pointIndex] = coordValue[d];
+                b++;
+            }
+        }
+        break;
+        case AwhOutputEntryType::Pmf:
+            block_[b].data()[pointIndex] = bias.state().points()[pointIndex].inTargetRegion() ? pmf[pointIndex] : 0;
+            break;
+        case AwhOutputEntryType::Bias:
+        {
+            const awh_dvec &coordValue = bias.getGridCoordValue(pointIndex);
+            block_[b].data()[pointIndex] = bias.state().points()[pointIndex].inTargetRegion() ? bias.calcConvolvedBias(coordValue) : 0;
+        }
+        break;
+        case AwhOutputEntryType::Visits:
+            block_[b].data()[pointIndex] = bias.state().points()[pointIndex].numVisitsTot();
+            break;
+        case AwhOutputEntryType::Weights:
+            block_[b].data()[pointIndex] = bias.state().points()[pointIndex].weightSumTot();
+            break;
+        case AwhOutputEntryType::Target:
+            block_[b].data()[pointIndex] = bias.state().points()[pointIndex].target();
+            break;
+        default:
+            GMX_RELEASE_ASSERT(false, "Unknown AWH output variable");
+            break;
+    }
+}
+
+void BiasWriter::prepareBiasOutput(const Bias &bias)
+{
+    /* Pack the AWH data into the writer data. */
+
+    /* Evaluate the PMF for all points */
+    gmx::ArrayRef<float> pmf = block_[getVarStartBlock(AwhOutputEntryType::Pmf)].data();
+    bias.state().getPmf(pmf);
+
+    /* Pack the the data point by point.
+     * Unfortunately we can not loop over a class enum, so we cast to int.
+     * \todo Use strings instead of enum when we port the output to TNG.
+     */
+    for (int i = 0; i < static_cast<int>(AwhOutputMetaData::Count); i++)
+    {
+        transferMetaDataToWriter(i, static_cast<AwhOutputMetaData>(i), bias);
+    }
+    for (const auto &pair : outputTypeToNormalization)
+    {
+        const AwhOutputEntryType outputType = pair.first;
+        /* Skip metadata (transfered above) and unused blocks */
+        if (outputType == AwhOutputEntryType::MetaData || !hasVarBlock(outputType))
+        {
+            continue;
+        }
+        for (size_t m = 0; m < bias.state().points().size(); m++)
+        {
+            transferPointDataToWriter(outputType, m, bias, pmf);
+        }
+    }
+
+    /* For looks of the output, normalize it */
+    for (AwhEnergyBlock &block : block_)
+    {
+        normalizeBlock(&block, bias);
+    }
+}
+
+int BiasWriter::writeToEnergySubblocks(const Bias    &bias,
+                                       t_enxsubblock *sub)
+{
+    prepareBiasOutput(bias);
+
+    for (size_t b = 0; b < block_.size(); b++)
+    {
+        sub[b].type = xdr_datatype_float;
+        sub[b].nr   = block_[b].data().size();
+        sub[b].fval = block_[b].data().data();
+    }
+
+    return block_.size();
+}
+
+} // namepace gmx
diff --git a/src/gromacs/awh/biaswriter.h b/src/gromacs/awh/biaswriter.h
new file mode 100644 (file)
index 0000000..137b4b3
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * This file contains the BiasWriter class that prepares and writes data of a Bias to an energy file.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_BIASWRITER_H
+#define GMX_AWH_BIASWRITER_H
+
+#include <map>
+#include <vector>
+
+#include "gromacs/fileio/enxio.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_multisim_t;
+struct t_enxsubblock;
+
+namespace gmx
+{
+class Bias;
+
+/* TODO: the post-simulations AWH reader and this AWH writer are totally
+ * disconnected although they read/write the same data. I'm not sure how
+ * to handle that or if it should be left as it is until the writing is done
+ * in a differen format (i.e. TNG) than the current energy file.
+ */
+
+//! Enum with the AWH variables to write.
+enum class AwhOutputEntryType
+{
+    MetaData,           //!< Meta data.
+    CoordValue,         //!< Coordinate value.
+    Pmf,                //!< The pmf.
+    Bias,               //!< The bias.
+    Visits,             //!< The number of visits.
+    Weights,            //!< The weights.
+    Target              //!< The target distribition.
+};
+
+//! Enum with the types of metadata to write.
+enum class AwhOutputMetaData
+{
+    NumBlock,           //!< The number of blocks.
+    TargetError,        //!< The target error.
+    ScaledSampleWeight, //!< The logarithm of the sample weight relative to a sample weight of 1 at the initial time.
+    Count               //!< The number of enum values, not including Count.
+};
+
+//! Enum with different ways of normalizing the output.
+enum class Normalization
+{
+    None,         //!< No normalization.
+    Coordinate,   //!< Scale using the internal/user input coordinate scaling factor.
+    FreeEnergy,   //!< Normalize free energy values by subtracting the minimum value.
+    Distribution  //!< Normalize the distribution to 1.
+};
+
+/*! \internal \brief AWH output data block that can be written to an energy file block.
+ */
+class AwhEnergyBlock
+{
+    public:
+        /*! \brief Constructor
+         *
+         * \param[in] numPoints           Number of points in block.
+         * \param[in] normalizationType   Value for normalization type enum.
+         * \param[in] normalizationValue  Normalization value.
+         */
+        AwhEnergyBlock(int            numPoints,
+                       Normalization  normalizationType,
+                       float          normalizationValue);
+
+        /*! \brief Returns an ArrarRef to the data in the block.
+         */
+        gmx::ArrayRef<float> data()
+        {
+            return data_;
+        }
+
+        const Normalization normalizationType;  /**< How to normalize the output data */
+        const float         normalizationValue; /**< The normalization value */
+    private:
+        std::vector<float>  data_;              /**< The data, always float which is enough since this is statistical data */
+};
+
+/*! \internal \brief Class organizing the output data storing and writing of an AWH bias.
+ */
+class BiasWriter
+{
+    private:
+        /*! \brief Query if the writer has a block for the given variable.
+         *
+         * \param[in] outputType  Output entry type.
+         */
+        bool hasVarBlock(AwhOutputEntryType outputType) const
+        {
+            return outputTypeToBlock_.find(outputType)->second >= 0;
+        }
+
+        /*! \brief* Find the first block containing the given variable.
+         *
+         * \param[in] outputType  Output entry type.
+         * \returns the first block index for the variable, or -1 there is no block.
+         */
+        int getVarStartBlock(AwhOutputEntryType outputType) const
+        {
+            return outputTypeToBlock_.find(outputType)->second;
+        }
+
+        /*! \brief Transfer AWH point data to writer data blocks.
+         *
+         * \param[in] metaDataIndex  Meta data block index.
+         * \param[in] metaDataType   The type of meta data to write.
+         * \param[in] bias           The AWH Bias.
+         */
+        void transferMetaDataToWriter(size_t             metaDataIndex,
+                                      AwhOutputMetaData  metaDataType,
+                                      const Bias        &bias);
+
+        /*! \brief Transfer AWH point data to writer data blocks.
+         *
+         * \param[in] outputType  Output entry type.
+         * \param[in] pointIndex  The point index.
+         * \param[in] bias        The AWH Bias.
+         * \param[in] pmf         PMF values.
+         */
+        void transferPointDataToWriter(AwhOutputEntryType          outputType,
+                                       int                         pointIndex,
+                                       const Bias                 &bias,
+                                       gmx::ArrayRef<const float>  pmf);
+
+    public:
+        /*! \brief Constructor.
+         *
+         * \param[in] bias  The AWH bias.
+         */
+        BiasWriter(const Bias &bias);
+
+        /*! \brief Returns the number of data blocks.
+         *
+         * \returns the number of data blocks.
+         */
+        int numBlocks() const
+        {
+            return block_.size();
+        }
+
+    private:
+        /*! \brief
+         * Prepare the bias output data.
+         *
+         * \param[in] bias  The AWH Bias.
+         */
+        void prepareBiasOutput(const Bias &bias);
+
+    public:
+        /*! \brief Collect AWH bias data in blocks and write to energy subblocks.
+         *
+         * \param[in]     bias      The AWH Bias.
+         * \param[in,out] subblock  Energy subblocks to write to.
+         * \returns the number of blocks written.
+         */
+        int writeToEnergySubblocks(const Bias &bias, t_enxsubblock *subblock);
+
+    private:
+        std::vector<AwhEnergyBlock>       block_;             /**< The data blocks */
+        std::map<AwhOutputEntryType, int> outputTypeToBlock_; /**< Start block index for each variable, -1 when variable should not be written */
+};
+
+}       // namespace gmx
+
+#endif  /* GMX_AWH_BIASWRITER_H */
index 0adf36ca5904bc2868f03965afc894b291424ee1..8145e5408275616b69eb8dd7ae3385551f867160 100644 (file)
@@ -172,11 +172,18 @@ class HistogramSize
 
         /*! \brief Returns true if we are in the initial stage of the AWH method.
          */
-        inline bool inInitialStage() const
+        bool inInitialStage() const
         {
             return inInitialStage_;
         };
 
+        /*! \brief Returns The log of the current sample weight, scaled because of the histogram rescaling.
+         */
+        double logScaledSampleWeight() const
+        {
+            return logScaledSampleWeight_;
+        };
+
     private:
         gmx_int64_t numUpdates_; /**< The number of updates performed since the start of the simulation. */
 
index 59f85f7543d90b8ffc3bd15daca2310ec38a279b..eef1774df0783308ad20e23805b6e9f21d7ab764 100644 (file)
@@ -522,6 +522,14 @@ AwhParams *readAndCheckAwhParams(int *ninp_p, t_inpfile **inp_p, const t_inputre
                 opt, awhParams->nstOut);
         warning_error(wi, buf);
     }
+    /* This restriction can be removed by changing a flag of print_ebin() */
+    if (ir->nstenergy == 0 || awhParams->nstOut % ir->nstenergy != 0)
+    {
+        char buf[STRLEN];
+        sprintf(buf, "%s (%d) should be a multiple of nstenergy (%d)",
+                opt, awhParams->nstOut, ir->nstenergy);
+        warning_error(wi, buf);
+    }
 
     CTYPE("Coordinate sampling interval in number of steps");
     sprintf(opt, "%s-nstsample", prefix);
index feedd8a99fbd00672abfc22996b351a8bd1d926c..ec1938a510c7d6912faad0dbcd185089323d3d4e 100644 (file)
@@ -72,7 +72,8 @@ const char      *enx_block_id_name[] = {
     "Distance restraints",
     "Free energy data",
     "BAR histogram",
-    "Delta H raw data"
+    "Delta H raw data",
+    "AWH data"
 };
 
 
index 00e9083ec96b2f1aa89e8272519032048785aa7b..db4d20a24475715488f30cf831bd4c89f45db6eb 100644 (file)
@@ -81,6 +81,8 @@ enum {
     enxDHHIST, /* BAR histogram                                              */
     enxDH,     /* BAR raw delta H data                                       */
 
+    enxAWH,    /* AWH data */
+
     enxNR      /* Total number of extra blocks in the current code,
                 * note that the enxio code can read files written by
                 * future code which contain more blocks.
@@ -93,7 +95,7 @@ extern const char *enx_block_id_name[];
 
 /* the subblocks that are contained in energy file blocks. Each of these
    has a number of values of a single data type in a .edr file. */
-typedef struct
+struct t_enxsubblock
 {
     int          nr;        /* number of items in subblock */
     xdr_datatype type;      /* the block type */
@@ -114,7 +116,7 @@ typedef struct
     int lval_alloc;
     int cval_alloc;
     int sval_alloc;
-} t_enxsubblock;
+};
 
 
 /* the energy file blocks. Each block contains a number of sub-blocks
@@ -128,7 +130,7 @@ typedef struct t_enxblock{
 
 
 /* The frames that are read/written */
-typedef struct {
+struct t_enxframe {
     double          t;            /* Timestamp of this frame                        */
     gmx_int64_t     step;         /* MD step                                */
     gmx_int64_t     nsteps;       /* The number of steps between frames            */
@@ -141,7 +143,7 @@ typedef struct {
     int             nblock;       /* Number of following energy blocks             */
     t_enxblock     *block;        /* The blocks                                    */
     int             nblock_alloc; /* The number of blocks allocated                */
-} t_enxframe;
+};
 
 /* file handle */
 typedef struct ener_file *ener_file_t;
index 2dd05b986e733d0d591ceef8bfdb2b4785fabb91..e2fdf8c703d2a9ff4c3b0a5910dfe1b8e5c158d4 100644 (file)
@@ -336,8 +336,19 @@ void xvgr_world(FILE *out, real xmin, real ymin, real xmax, real ymax,
     }
 }
 
-void xvgr_legend(FILE *out, int nsets, const char** setname,
-                 const gmx_output_env_t *oenv)
+static bool stringIsEmpty(const std::string s)
+{
+    return s.empty();
+}
+
+static bool stringIsEmpty(const char *s)
+{
+    return (s == nullptr || s[0] == '\0');
+}
+
+template <typename T>
+static void xvgr_legend(FILE *out, int nsets, const T * setname,
+                        const gmx_output_env_t *oenv)
 {
     int  i;
     char buf[STRLEN];
@@ -352,7 +363,7 @@ void xvgr_legend(FILE *out, int nsets, const char** setname,
         fprintf(out, "@ legend length %d\n", 2);
         for (i = 0; (i < nsets); i++)
         {
-            if (setname[i])
+            if (!stringIsEmpty(setname[i]))
             {
                 if (output_env_get_xvg_format(oenv) == exvgXMGR)
                 {
@@ -369,6 +380,18 @@ void xvgr_legend(FILE *out, int nsets, const char** setname,
     }
 }
 
+void xvgrLegend(FILE                           *out,
+                const std::vector<std::string> &setNames,
+                const struct gmx_output_env_t  *oenv)
+{
+    xvgr_legend(out, setNames.size(), setNames.data(), oenv);
+}
+void xvgr_legend(FILE *out, int nsets, const char** setnames,
+                 const struct gmx_output_env_t *oenv)
+{
+    xvgr_legend<char const *>(out, nsets, setnames, oenv);
+}
+
 void xvgr_new_dataset(FILE *out, int nr_first, int nsets,
                       const char **setname,
                       const gmx_output_env_t *oenv)
index 166300ff1fb6e3c6b67479f35f082e58efcfbbc3..1c5104d367e9b2243850e587bdc9199fc28aa6cb 100644 (file)
@@ -40,6 +40,7 @@
 #include <stdio.h>
 
 #include <string>
+#include <vector>
 
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
@@ -131,11 +132,15 @@ void xvgr_world(FILE *out, real xmin, real ymin, real xmax, real ymax,
                 const struct gmx_output_env_t *oenv);
 /* Set the world in xvgr */
 
+void xvgrLegend(FILE                           *out,
+                const std::vector<std::string> &setNames,
+                const struct gmx_output_env_t  *oenv);
+/* Make a legend box, and also modifies the view to make room for the legend */
+
 void xvgr_legend(FILE *out, int nsets, const char** setnames,
                  const struct gmx_output_env_t *oenv);
 /* Make a legend box, and also modifies the view to make room for the legend */
 
-
 void xvgr_new_dataset(FILE *out,
                       int nr_first, int nsets, const char **setnames,
                       const struct gmx_output_env_t *oenv);
index 67efbea450ff7e8ec20e85c0b1cf57e9c219d374..a8e83dd95e1b207fb5e9e1ac1c0719eb300240aa 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017, 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.
@@ -51,6 +51,9 @@ gmx_analyze(int argc, char *argv[]);
 int
 gmx_anaeig(int argc, char *argv[]);
 
+int
+gmx_awh(int argc, char *argv[]);
+
 int
 gmx_g_angle(int argc, char *argv[]);
 
diff --git a/src/gromacs/gmxana/gmx_awh.cpp b/src/gromacs/gmxana/gmx_awh.cpp
new file mode 100644 (file)
index 0000000..a016f39
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2016,2017, 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.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief
+ *  Tool for extracting AWH (Accelerated Weight Histogram) data from energy files.
+ *
+ *  \author Viveca Lindahl
+ *  \author Berk Hess
+ */
+
+#include "gmxpre.h"
+
+#include <cstdlib>
+#include <cstring>
+
+#include <algorithm>
+#include <array>
+#include <memory>
+#include <string>
+
+#include "gromacs/commandline/pargs.h"
+#include "gromacs/fileio/enxio.h"
+#include "gromacs/fileio/gmxfio.h"
+#include "gromacs/fileio/oenv.h"
+#include "gromacs/fileio/tpxio.h"
+#include "gromacs/fileio/trxio.h"
+#include "gromacs/fileio/xvgr.h"
+#include "gromacs/gmxana/gmx_ana.h"
+#include "gromacs/math/units.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/topology/mtop_util.h"
+#include "gromacs/topology/topology.h"
+#include "gromacs/utility/arraysize.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/futil.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/smalloc.h"
+#include "gromacs/utility/stringutil.h"
+
+using gmx::AwhParams;
+using gmx::AwhBiasParams;
+
+namespace
+{
+
+//! Enum for choosing AWH graph output
+enum class AwhGraphSelection
+{
+    Pmf, //!< Only the PMF
+    All  //!< All possible AWH graphs
+};
+
+//! Energy unit options
+enum class EnergyUnit
+{
+    KJPerMol, //!< kJ/mol
+    KT        //!< kT
+};
+
+} // namespace
+
+/*! \brief All meta-data that is shared for one output file type for one bias */
+class OutputFile
+{
+    public:
+        /*! \brief Constructor, Set the output base file name and title.
+         *
+         * Result is a valid object, but will produce empty output files.
+         */
+        OutputFile(const std::string  filename,
+                   const std::string  baseTitle,
+                   int                numBias,
+                   int                biasIndex);
+
+        /*! \brief Initializes the output file setup for the AWH output.
+         */
+        void initializeAwhOutputFile(int                  subblockStart,
+                                     int                  numSubblocks,
+                                     const AwhBiasParams *awhBiasParams,
+                                     AwhGraphSelection    graphSelection,
+                                     EnergyUnit           energyUnit,
+                                     real                 kTValue);
+
+        /*! \brief Opens a single output file for a bias, prints title and legends.
+         *
+         * \param[in] time  The time for of frame to be written.
+         * \param[in] oenv  The output environment.
+         * \returns the file pointer.
+         */
+        FILE * openBiasOutputFile(double                  time,
+                                  const gmx_output_env_t *oenv) const;
+
+        /*! \brief Writes data selected from \p block to file.
+         *
+         * \param[in] block          The energy block with the data to write.
+         * \param[in] subBlockStart  The sub-block start index.
+         * \param[in] fp             The file pointer.
+         */
+        void writeData(const t_enxblock &block,
+                       int               subBlockStart,
+                       FILE             *fp) const;
+
+    private:
+        std::string              baseFilename_;       /**< Base of the output file name. */
+        std::string              title_;              /**< Title for the graph. */
+        int                      numDim_;             /**< Number of dimensions. */
+        int                      firstGraphSubblock_; /**< Index in the energy sub-blocks for the first graph. */
+        int                      numGraph_;           /**< Number of actual graphs. */
+        bool                     useKTForEnergy_;     /**< Whether to use kT instead of kJ/mol for energy. */
+        std::vector<real>        scaleFactor_;        /**< Scale factors from energy file data to output for each of the numGraph quantities. */
+
+        std::vector<std::string> legend_;             /**< Legends for the output */
+        std::string              xLabel_;             /**< Label for the x-axis. */
+        std::string              yLabel_;             /**< Label for the y-axis. */
+};
+
+/*! \brief All meta-data that is shared for all output files for one bias */
+class BiasReader
+{
+    public:
+        //! Constructor.
+        BiasReader(int                         subblockStart,
+                   int                         numSubblocks,
+                   std::unique_ptr<OutputFile> awhOutputFile) :
+            subblockStart_(subblockStart),
+            numSubblocks_(numSubblocks),
+            awhOutputFile_(std::move(awhOutputFile))
+        {
+        }
+
+        //! Return the AWH output file data.
+        const OutputFile &awhOutputFile() const
+        {
+            return *awhOutputFile_.get();
+        }
+
+        //! Return the starting subblock.
+        int subblockStart() const
+        {
+            return subblockStart_;
+        }
+
+    private:
+        const int                   subblockStart_; /**< The start index of the subblocks to read. */
+        const int                   numSubblocks_;  /**< Number of subblocks to read. */
+        std::unique_ptr<OutputFile> awhOutputFile_; /**< The standard AWH output file data. */
+        /* NOTE: A second OutputFile will be added soon, this will also make numSubblocks_ useful. */
+};
+
+/*! \brief All options and meta-data needed for the AWH output */
+class AwhReader
+{
+    public:
+        //! Constructor
+        AwhReader(const AwhParams  *awhParams,
+                  int               numFileOptions,
+                  const t_filenm   *filenames,
+                  AwhGraphSelection awhGraphSelection,
+                  EnergyUnit        energyUnit,
+                  real              kT,
+                  const t_enxblock *block);
+
+        //! Extract and print AWH data for an AWH block of one energy frame
+        void processAwhFrame(const t_enxblock       &block,
+                             double                  time,
+                             const gmx_output_env_t *oenv) const;
+
+    private:
+        std::vector<BiasReader> biasReader_; /**< The readers, one for each AWH bias. */
+    public:
+        const real              kT_;         /**< kB*T in kJ/mol. */
+};
+
+namespace
+{
+
+/* NOTE: A second value will be added soon. */
+enum {
+    awhGraphTypeAwh
+};
+
+/*! \brief The maximum number of observables per subblock, not including the full friction tensor.
+ *
+ * This number, as well as the legends in make_legend() should match
+ * the output that mdrun writes. It would be better to define these
+ * values in a single location.
+ */
+static constexpr int maxAwhGraphs = 5;
+
+/*! \brief Constructs a legend for a standard awh output file */
+std::vector<std::string>makeLegend(const AwhBiasParams *awhBiasParams,
+                                   int                  awhGraphType,
+                                   size_t               numLegend)
+{
+    const std::array<std::string, maxAwhGraphs> legendBase =
+    {
+        { "PMF",
+          "Coord bias",
+          "Coord distr",
+          "Ref value distr",
+          "Target ref value distr" }
+    };
+
+    std::vector<std::string>                    legend;
+    /* Give legends to dimensions higher than the first */
+    for (int d = 1; d < awhBiasParams->ndim; d++)
+    {
+        legend.push_back(gmx::formatString("dim%d", d));
+    }
+
+    switch (awhGraphType)
+    {
+        case awhGraphTypeAwh:
+        {
+            /* Add as many legends as possible from the "base" legend list */
+            size_t legendBaseIndex = 0;
+            while (legend.size() < numLegend && legendBaseIndex < legendBase.size())
+            {
+                legend.push_back(legendBase[legendBaseIndex]);
+                legendBaseIndex++;
+            }
+        }
+        break;
+    }
+
+    GMX_RELEASE_ASSERT(legend.size() == numLegend, "The number of legends requested for printing and the number generated should be the same");
+
+    return legend;
+}
+
+} // namespace
+
+OutputFile::OutputFile(const std::string  filename,
+                       const std::string  baseTitle,
+                       int                numBias,
+                       int                biasIndex) :
+    numDim_(0),
+    firstGraphSubblock_(0),
+    numGraph_(0),
+    useKTForEnergy_(0),
+    scaleFactor_(),
+    legend_(),
+    xLabel_(),
+    yLabel_()
+{
+    // cppcheck-suppress useInitializationList
+    baseFilename_ = filename.substr(0, filename.find('.'));
+    title_        = baseTitle;
+    if (numBias > 1)
+    {
+        baseFilename_ += gmx::formatString("%d", biasIndex + 1);
+        title_        += gmx::formatString(" %d", biasIndex + 1);
+    }
+}
+
+void OutputFile::initializeAwhOutputFile(int                  subblockStart,
+                                         int                  numSubblocks,
+                                         const AwhBiasParams *awhBiasParams,
+                                         AwhGraphSelection    graphSelection,
+                                         EnergyUnit           energyUnit,
+                                         real                 kTValue)
+{
+    /* The first subblock with actual graph y-values is index 1 + ndim */
+    numDim_             = awhBiasParams->ndim;
+    firstGraphSubblock_ = subblockStart + 1 + numDim_;
+    if (graphSelection == AwhGraphSelection::All)
+    {
+        /* There are one metadata and ndim coordinate blocks */
+        numGraph_       = std::min(numSubblocks - 1 - numDim_,
+                                   maxAwhGraphs);
+    }
+    else
+    {
+        numGraph_       = 1;
+    }
+    useKTForEnergy_     = (energyUnit == EnergyUnit::KT);
+    scaleFactor_.resize(numGraph_, 1);
+    if (!useKTForEnergy_)
+    {
+        /* The first two graphs are in units of energy, multiply by kT */
+        std::fill(scaleFactor_.begin(), scaleFactor_.begin() + std::min(2, numGraph_), kTValue);
+    }
+    int numLegend = numDim_ - 1 + numGraph_;
+    legend_       = makeLegend(awhBiasParams, awhGraphTypeAwh, numLegend);
+    /* We could have both length and angle coordinates in a single bias */
+    xLabel_       = "(nm or deg)";
+    yLabel_       = useKTForEnergy_ ? "(k\\sB\\NT)" : "(kJ/mol)";
+    if (graphSelection == AwhGraphSelection::All)
+    {
+        yLabel_ += gmx::formatString(", (nm\\S-%d\\N or rad\\S-%d\\N), (-)",
+                                     awhBiasParams->ndim, awhBiasParams->ndim);
+    }
+}
+
+AwhReader::AwhReader(const AwhParams  *awhParams,
+                     int               numFileOptions,
+                     const t_filenm   *filenames,
+                     AwhGraphSelection awhGraphSelection,
+                     EnergyUnit        energyUnit,
+                     real              kT,
+                     const t_enxblock *block) :
+    kT_(kT)
+{
+    GMX_RELEASE_ASSERT(block != nullptr, "NULL pointer passed to initAwhReader");
+
+    /* The first subblock of each AWH block has metadata about
+     * the number of subblocks belonging to that AWH block.
+     * (block->nsub gives only the total number of subblocks and not how
+     * they are distributed between the AWH biases).
+     */
+
+    /* Keep track of the first subblock of this AWH */
+    int subblockStart = 0;
+    for (int k = 0; k < awhParams->numBias; k++)
+    {
+        AwhBiasParams              *awhBiasParams = &awhParams->awhBiasParams[k];
+
+        int                         numSubblocks  = (int)block->sub[subblockStart].fval[0];
+
+        std::unique_ptr<OutputFile> outputFileAwh(new OutputFile(opt2fn("-o", numFileOptions, filenames), "AWH", awhParams->numBias, k));
+
+        outputFileAwh->initializeAwhOutputFile(subblockStart, numSubblocks,
+                                               awhBiasParams, awhGraphSelection,
+                                               energyUnit, kT);
+
+        biasReader_.emplace_back(BiasReader(subblockStart, numSubblocks, std::move(outputFileAwh)));
+
+        subblockStart += numSubblocks;
+    }
+}
+
+FILE * OutputFile::openBiasOutputFile(double                  time,
+                                      const gmx_output_env_t *oenv) const
+{
+    std::string filename = baseFilename_ + gmx::formatString("_t%g.xvg", time);
+
+    FILE       *fp = xvgropen(filename.c_str(), title_.c_str(), xLabel_, yLabel_, oenv);
+    xvgrLegend(fp, legend_, oenv);
+
+    return fp;
+}
+
+/*! \brief Prints data selected by \p outputFile from \p block to \p fp */
+void OutputFile::writeData(const t_enxblock &block,
+                           int               subBlockStart,
+                           FILE             *fp) const
+{
+    int numPoints = block.sub[subBlockStart + 1].nr;
+    for (int j = 0; j < numPoints; j++)
+    {
+        /* Print the coordinates for numDim dimensions */
+        for (int d = 0; d < numDim_; d++)
+        {
+            fprintf(fp, "  %8.4f", block.sub[subBlockStart + 1 + d].fval[j]);
+        }
+
+        /* Print numGraph observables */
+        for (int i = 0; i < numGraph_; i++)
+        {
+            fprintf(fp, "  %g", block.sub[firstGraphSubblock_ + i].fval[j]*scaleFactor_[i]);
+        }
+
+        fprintf(fp, "\n");
+    }
+}
+
+void AwhReader::processAwhFrame(const t_enxblock       &block,
+                                double                  time,
+                                const gmx_output_env_t *oenv) const
+{
+    /* We look for AWH data every energy frame and count the no of AWH frames found. We only extract every 'skip' AWH frame. */
+
+    for (auto &biasReader : biasReader_)
+    {
+        /* Each frame and AWH instance extracted generates one xvg file. */
+        {
+            const OutputFile &awhOutputFile = biasReader.awhOutputFile();
+
+            const int         subStart = biasReader.subblockStart();
+
+            FILE             *fpAwh = awhOutputFile.openBiasOutputFile(time, oenv);
+
+            /* Now do the actual printing. Metadata in first subblock is treated separately. */
+            fprintf(fpAwh, "# AWH metadata: target error = %.2f kT = %.2f kJ/mol\n",
+                    block.sub[subStart].fval[1],
+                    block.sub[subStart].fval[1]*kT_);
+
+            fprintf(fpAwh, "# AWH metadata: log sample weight = %4.2f\n",
+                    block.sub[subStart].fval[2]);
+
+            awhOutputFile.writeData(block, subStart, fpAwh);
+
+            gmx_ffclose(fpAwh);
+        }
+    }
+}
+
+/*! \brief The main function for the AWH tool */
+int gmx_awh(int argc, char *argv[])
+{
+    const char         *desc[] = {
+        "[THISMODULE] extracts AWH data from an energy file.",
+        "One file is written per AWH bias per time frame.",
+        "The bias index, if more than one, is appended to the file, as well as",
+        "the time of the frame. By default only the PMF is printed.",
+        "With [TT]-more[tt] the bias, target and coordinate distributions",
+        "are also printed.",
+    };
+    static gmx_bool     moreGraphs = FALSE;
+    static int          skip       = 0;
+    static gmx_bool     kTUnit     = FALSE;
+    t_pargs             pa[]       = {
+        { "-skip", FALSE, etINT,  {&skip},
+          "Skip number of frames between data points" },
+        { "-more", FALSE, etBOOL, {&moreGraphs},
+          "Print more output" },
+        { "-kt", FALSE, etBOOL, {&kTUnit},
+          "Print free energy output in units of kT instead of kJ/mol" }
+    };
+
+    ener_file_t         fp;
+    t_inputrec          ir;
+    gmx_enxnm_t        *enm = nullptr;
+    t_enxframe         *frame;
+    int                 nre;
+    gmx_output_env_t   *oenv;
+
+    t_filenm            fnm[] = {
+        { efEDR, "-f", nullptr,           ffREAD  },
+        { efTPR, "-s", nullptr,           ffREAD  },
+        { efXVG, "-o",    "awh",          ffWRITE }
+    };
+    const int           nfile  = asize(fnm);
+    if (!parse_common_args(&argc, argv,
+                           PCA_CAN_VIEW | PCA_CAN_BEGIN | PCA_CAN_END,
+                           nfile, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
+    {
+        return 0;
+    }
+
+    snew(frame, 1);
+    fp = open_enx(ftp2fn(efEDR, nfile, fnm), "r");
+    do_enxnms(fp, &nre, &enm);
+
+    /* We just need the AWH parameters from inputrec. These are used to initialize
+       the AWH reader when we have a frame to read later on. */
+    gmx_mtop_t mtop;
+    int        natoms;
+    matrix     box;
+    read_tpx(ftp2fn(efTPR, nfile, fnm), &ir, box, &natoms, nullptr, nullptr, &mtop);
+
+    if (!ir.bDoAwh)
+    {
+        gmx_fatal(FARGS, "No AWH data in %s\n", opt2fn("-f", nfile, fnm));
+    }
+
+    std::unique_ptr<AwhReader> awhReader;
+
+    /* Initiate counters */
+    gmx_bool   haveFrame;
+    int        awhFrameCounter = 0;
+    int        timeCheck       = 0;
+    do
+    {
+        haveFrame = do_enx(fp, frame);
+
+        bool        useFrame = false;
+
+        t_enxblock *block    = nullptr;
+
+        if (haveFrame)
+        {
+            timeCheck = check_times(frame->t);
+
+            if (timeCheck == 0)
+            {
+                block = find_block_id_enxframe(frame, enxAWH, nullptr);
+
+                if (block != nullptr)
+                {
+                    if ((skip == 0) || (awhFrameCounter % skip == 0))
+                    {
+                        useFrame = true;
+                    }
+                    awhFrameCounter++;
+                }
+            }
+        }
+
+        if (useFrame)
+        {
+            /* We read a valid frame with an AWH block, so we can use it */
+
+            /* Currently we have the number of subblocks per AWH stored
+             * in the first subblock (we cannot get this directly from the tpr),
+             * so we have to read an AWH block before making the legends.
+             */
+            if (awhReader == nullptr)
+            {
+                AwhGraphSelection awhGraphSelection = (moreGraphs ? AwhGraphSelection::All : AwhGraphSelection::Pmf);
+                EnergyUnit        energyUnit        = (kTUnit ? EnergyUnit::KT : EnergyUnit::KJPerMol);
+                awhReader =
+                    std::unique_ptr<AwhReader>(new AwhReader(ir.awhParams,
+                                                             nfile, fnm,
+                                                             awhGraphSelection,
+                                                             energyUnit, BOLTZ*ir.opts.ref_t[0],
+                                                             block));
+            }
+
+            awhReader->processAwhFrame(*block, frame->t, oenv);
+        }
+    }
+    while (haveFrame && (timeCheck <= 0));
+
+    fprintf(stderr, "\n");
+
+    close_enx(fp);
+
+    return 0;
+}
index 12c0d21ca2ba865445479f3da74353f01815c012..49361183ff28f63d1784259d58bf7bede083590d 100644 (file)
@@ -42,6 +42,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "gromacs/awh/awh.h"
 #include "gromacs/fileio/enxio.h"
 #include "gromacs/fileio/gmxfio.h"
 #include "gromacs/fileio/xvgr.h"
@@ -1245,7 +1246,8 @@ void print_ebin(ener_file_t fp_ene, gmx_bool bEne, gmx_bool bDR, gmx_bool bOR,
                 gmx_int64_t step, double time,
                 int mode,
                 t_mdebin *md, t_fcdata *fcd,
-                gmx_groups_t *groups, t_grpopts *opts)
+                gmx_groups_t *groups, t_grpopts *opts,
+                gmx::Awh *awh)
 {
     /*static char **grpnms=NULL;*/
     char         buf[246];
@@ -1360,6 +1362,12 @@ void print_ebin(ener_file_t fp_ene, gmx_bool bEne, gmx_bool bDR, gmx_bool bOR,
                     mde_delta_h_coll_reset(md->dhc);
                 }
 
+                /* AWH bias blocks. */
+                if (awh != nullptr)  // TODO: add boolean in t_mdebin. See in mdebin.h.
+                {
+                    awh->writeToEnergyFrame(step, &fr);
+                }
+
                 /* do the actual I/O */
                 do_enx(fp_ene, &fr);
                 if (fr.nre)
index b79eb1607c3f93557304a7c80e986933eace9e23..37a10a2f01e99b8a1fe4a37bb776d6e3632527b5 100644 (file)
@@ -54,6 +54,11 @@ struct t_grpopts;
 struct t_lambda;
 class t_state;
 
+namespace gmx
+{
+class Awh;
+}
+
 /* The functions & data structures here determine the content for outputting
    the .edr file; the file format and actual writing is done with functions
    defined in enxio.h */
@@ -154,7 +159,8 @@ void print_ebin(ener_file_t fp_ene, gmx_bool bEne, gmx_bool bDR, gmx_bool bOR,
                 gmx_int64_t step, double time,
                 int mode,
                 t_mdebin *md, t_fcdata *fcd,
-                gmx_groups_t *groups, t_grpopts *opts);
+                gmx_groups_t *groups, t_grpopts *opts,
+                gmx::Awh *awh);
 
 
 
index 16c8b2c8659aedb0725174b46c9d7e3571bb0d89..dbed5d27e77a44f02822434c915a42e2c96a5cb2 100644 (file)
@@ -359,7 +359,7 @@ static void init_em(FILE *fplog, const char *title,
 
     if (ir->eI == eiNM)
     {
-        GMX_ASSERT(shellfc != NULL, "With NM we always support shells");
+        GMX_ASSERT(shellfc != nullptr, "With NM we always support shells");
 
         *shellfc = init_shell_flexcon(stdout,
                                       top_global,
@@ -1084,7 +1084,7 @@ double do_cg(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlog,
 
         print_ebin_header(fplog, step, step);
         print_ebin(mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step, step, eprNORMAL,
-                   mdebin, fcd, &(top_global->groups), &(inputrec->opts));
+                   mdebin, fcd, &(top_global->groups), &(inputrec->opts), nullptr);
     }
     where();
 
@@ -1525,7 +1525,7 @@ double do_cg(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlog,
             }
             print_ebin(mdoutf_get_fp_ene(outf), do_ene, FALSE, FALSE,
                        do_log ? fplog : nullptr, step, step, eprNORMAL,
-                       mdebin, fcd, &(top_global->groups), &(inputrec->opts));
+                       mdebin, fcd, &(top_global->groups), &(inputrec->opts), nullptr);
         }
 
         /* Send energies and positions to the IMD client if bIMD is TRUE. */
@@ -1574,7 +1574,7 @@ double do_cg(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlog,
             /* Write final energy file entries */
             print_ebin(mdoutf_get_fp_ene(outf), !do_ene, FALSE, FALSE,
                        !do_log ? fplog : nullptr, step, step, eprNORMAL,
-                       mdebin, fcd, &(top_global->groups), &(inputrec->opts));
+                       mdebin, fcd, &(top_global->groups), &(inputrec->opts), nullptr);
         }
     }
 
@@ -1790,7 +1790,7 @@ double do_lbfgs(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlo
 
         print_ebin_header(fplog, step, step);
         print_ebin(mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step, step, eprNORMAL,
-                   mdebin, fcd, &(top_global->groups), &(inputrec->opts));
+                   mdebin, fcd, &(top_global->groups), &(inputrec->opts), nullptr);
     }
     where();
 
@@ -2296,7 +2296,7 @@ double do_lbfgs(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlo
             }
             print_ebin(mdoutf_get_fp_ene(outf), do_ene, FALSE, FALSE,
                        do_log ? fplog : nullptr, step, step, eprNORMAL,
-                       mdebin, fcd, &(top_global->groups), &(inputrec->opts));
+                       mdebin, fcd, &(top_global->groups), &(inputrec->opts), nullptr);
         }
 
         /* Send x and E to IMD client, if bIMD is TRUE. */
@@ -2344,7 +2344,7 @@ double do_lbfgs(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlo
     {
         print_ebin(mdoutf_get_fp_ene(outf), !do_ene, FALSE, FALSE,
                    !do_log ? fplog : nullptr, step, step, eprNORMAL,
-                   mdebin, fcd, &(top_global->groups), &(inputrec->opts));
+                   mdebin, fcd, &(top_global->groups), &(inputrec->opts), nullptr);
     }
 
     /* Print some stuff... */
@@ -2542,7 +2542,7 @@ double do_steep(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlo
                            do_per_step(steps_accepted, inputrec->nstdisreout),
                            do_per_step(steps_accepted, inputrec->nstorireout),
                            fplog, count, count, eprNORMAL,
-                           mdebin, fcd, &(top_global->groups), &(inputrec->opts));
+                           mdebin, fcd, &(top_global->groups), &(inputrec->opts), nullptr);
                 fflush(fplog);
             }
         }
index 0a159de02b4cb90e2b0afc60b1748c9c69956000..6da0d77694c1a9aa4471e3e279e07bfde0df2208 100644 (file)
@@ -230,6 +230,8 @@ void registerLegacyModules(gmx::CommandLineModuleManager *manager)
                    "Analyze data sets");
     registerModule(manager, &gmx_g_angle, "angle",
                    "Calculate distributions and correlations for angles and dihedrals");
+    registerModule(manager, &gmx_awh, "awh",
+                   "Extract data from an accelerated weight histogram (AWH) run");
     registerModule(manager, &gmx_bar, "bar",
                    "Calculate free energy difference estimates through Bennett's acceptance ratio");
     registerObsoleteTool(manager, "bond");
@@ -406,6 +408,7 @@ void registerLegacyModules(gmx::CommandLineModuleManager *manager)
         gmx::CommandLineModuleGroup group =
             manager->addModuleGroup("Tools");
         group.addModule("analyze");
+        group.addModule("awh");
         group.addModule("dyndom");
         group.addModule("filter");
         group.addModule("lie");
index e90983ed78599b40b9a79ac120cccecde2dbe9c0..c14284008ec2fcf10c57fdc07acb05bcaf2fdfd1 100644 (file)
@@ -1775,7 +1775,7 @@ double gmx::do_md(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog,
 
             print_ebin(mdoutf_get_fp_ene(outf), do_ene, do_dr, do_or, do_log ? fplog : nullptr,
                        step, t,
-                       eprNORMAL, mdebin, fcd, groups, &(ir->opts));
+                       eprNORMAL, mdebin, fcd, groups, &(ir->opts), ir->awh);
 
             if (ir->bPull)
             {
@@ -1966,7 +1966,7 @@ double gmx::do_md(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog,
         if (ir->nstcalcenergy > 0 && !bRerunMD)
         {
             print_ebin(mdoutf_get_fp_ene(outf), FALSE, FALSE, FALSE, fplog, step, t,
-                       eprAVER, mdebin, fcd, groups, &(ir->opts));
+                       eprAVER, mdebin, fcd, groups, &(ir->opts), ir->awh);
         }
     }