New analysis tool to compute the free volume and total volume.
authorDavid van der Spoel <spoel@xray.bmc.uu.se>
Thu, 28 Mar 2013 16:25:38 +0000 (17:25 +0100)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Fri, 14 Jun 2013 11:40:06 +0000 (13:40 +0200)
Works with selections, such that the freevolume in a box containing
a subset of all atoms can be computed.
Added excessive comments so that others can use this as a reference
for implementing analysis tools as well.
Added a test system as well, but this is somewhat tricky due to
real precision and the Monte Carlo algorithm.
literature reference.
valgrind.

Change-Id: I2acb48b829c9495e399c60e9dc927662e2958d69

13 files changed:
src/gromacs/gmxlib/rmpbc.c
src/gromacs/gmxlib/tpxio.c
src/gromacs/gmxlib/typedefs.c
src/gromacs/legacyheaders/vec.h
src/gromacs/trajectoryanalysis/modules.cpp
src/gromacs/trajectoryanalysis/modules/freevolume.cpp [new file with mode: 0644]
src/gromacs/trajectoryanalysis/modules/freevolume.h [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/CMakeLists.txt
src/gromacs/trajectoryanalysis/tests/freevolume.cpp [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/freevolume.tpr [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/freevolume.xtc [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/refdata/FreeVolumeModuleTest_ComputesFreeVolume.xml [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/refdata/FreeVolumeModuleTest_ComputesFreeVolumeSelection.xml [new file with mode: 0644]

index 70b70dc632d109103707fb08be31438f385b3698..3560c53e0778f5776bce075706d1a900cf4cca4d 100644 (file)
@@ -138,6 +138,7 @@ void gmx_rmpbc_done(gmx_rmpbc_t gpbc)
         for (i = 0; i < gpbc->ngraph; i++)
         {
             done_graph(gpbc->graph[i].gr);
+            sfree(gpbc->graph[i].gr);
         }
         if (gpbc->graph != NULL)
         {
index 9cf04d86def7e006086eeacc5627dcce29f3a3b7..c94caa3cf8cc81f07161dcd5e1be42a28d320acf 100644 (file)
@@ -2099,6 +2099,10 @@ static void do_block(t_fileio *fio, t_block *block, gmx_bool bRead, int file_ver
     }
     if (bRead)
     {
+        if ((block->nalloc_index > 0) && (NULL != block->index))
+        {
+            sfree(block->index);
+        }
         block->nalloc_index = block->nr+1;
         snew(block->index, block->nalloc_index);
     }
@@ -2855,6 +2859,7 @@ static void do_mtop(t_fileio *fio, gmx_mtop_t *mtop, gmx_bool bRead,
     {
         if (file_version >= 57)
         {
+            done_block(&mtop->mols);
             mtop->mols = mtop_mols(mtop);
         }
         if (gmx_debug_at)
@@ -3436,6 +3441,27 @@ gmx_bool fn2bTPX(const char *file)
     }
 }
 
+static void done_gmx_groups_t(gmx_groups_t *g)
+{
+    int i;
+
+    for (i = 0; (i < egcNR); i++)
+    {
+        if (NULL != g->grps[i].nm_ind)
+        {
+            sfree(g->grps[i].nm_ind);
+            g->grps[i].nm_ind = NULL;
+        }
+        if (NULL != g->grpnr[i])
+        {
+            sfree(g->grpnr[i]);
+            g->grpnr[i] = NULL;
+        }
+    }
+    /* The contents of this array is in symtab, don't free it here */
+    sfree(g->grpname);
+}
+
 gmx_bool read_tps_conf(const char *infile, char *title, t_topology *top, int *ePBC,
                        rvec **x, rvec **v, matrix box, gmx_bool bMass)
 {
@@ -3463,6 +3489,8 @@ gmx_bool read_tps_conf(const char *infile, char *title, t_topology *top, int *eP
         *ePBC = read_tpx(infile, NULL, box, &natoms,
                          (x == NULL) ? NULL : *x, (v == NULL) ? NULL : *v, NULL, mtop);
         *top = gmx_mtop_t_to_t_topology(mtop);
+        /* In this case we need to throw away the group data too */
+        done_gmx_groups_t(&mtop->groups);
         sfree(mtop);
         strcpy(title, *top->name);
         tpx_make_chain_identifiers(&top->atoms, &top->mols);
index cc90ecb549dbf026ad2ece62aa7d2b4eae3f7057..82d2b4a5fd856e88c62330a43c0e1f05567fde56 100644 (file)
@@ -651,6 +651,16 @@ void done_state(t_state *state)
         sfree(state->cg_gl);
     }
     state->cg_gl_nalloc = 0;
+    if (state->lambda)
+    {
+        sfree(state->lambda);
+    }
+    if (state->ngtc > 0)
+    {
+        sfree(state->nosehoover_xi);
+        sfree(state->nosehoover_vxi);
+        sfree(state->therm_integral);
+    }
 }
 
 static void do_box_rel(t_inputrec *ir, matrix box_rel, matrix b, gmx_bool bInit)
index fd86b8c942c3b878ced68e03fbc534e2d96012aa..85d887754cc05723d91169b1631fa44a256d4dcb 100644 (file)
 #include "physics.h"
 
 #ifdef __cplusplus
+
+static gmx_inline real det(const matrix a)
+{
+    return ( a[XX][XX]*(a[YY][YY]*a[ZZ][ZZ]-a[ZZ][YY]*a[YY][ZZ])
+             -a[YY][XX]*(a[XX][YY]*a[ZZ][ZZ]-a[ZZ][YY]*a[XX][ZZ])
+             +a[ZZ][XX]*(a[XX][YY]*a[YY][ZZ]-a[YY][YY]*a[XX][ZZ]));
+}
+
+static gmx_inline void mvmul(const matrix a, const rvec src, rvec dest)
+{
+    dest[XX] = a[XX][XX]*src[XX]+a[XX][YY]*src[YY]+a[XX][ZZ]*src[ZZ];
+    dest[YY] = a[YY][XX]*src[XX]+a[YY][YY]*src[YY]+a[YY][ZZ]*src[ZZ];
+    dest[ZZ] = a[ZZ][XX]*src[XX]+a[ZZ][YY]*src[YY]+a[ZZ][ZZ]*src[ZZ];
+}
+
 extern "C" {
 #elif 0
 } /* avoid screwing up indentation */
@@ -709,6 +724,7 @@ static gmx_inline real det(matrix a)
              +a[ZZ][XX]*(a[XX][YY]*a[YY][ZZ]-a[YY][YY]*a[XX][ZZ]));
 }
 
+
 static gmx_inline void m_add(matrix a, matrix b, matrix dest)
 {
     dest[XX][XX] = a[XX][XX]+b[XX][XX];
@@ -801,6 +817,7 @@ static gmx_inline void mvmul(matrix a, const rvec src, rvec dest)
     dest[ZZ] = a[ZZ][XX]*src[XX]+a[ZZ][YY]*src[YY]+a[ZZ][ZZ]*src[ZZ];
 }
 
+
 static gmx_inline void mvmul_ur0(matrix a, const rvec src, rvec dest)
 {
     dest[ZZ] = a[ZZ][XX]*src[XX]+a[ZZ][YY]*src[YY]+a[ZZ][ZZ]*src[ZZ];
index 428c88af072b6197fd4bea19252d2ff934144c1a..b5770a4878748e7a98381864e7108627c7ca1452 100644 (file)
@@ -47,6 +47,7 @@
 
 #include "modules/angle.h"
 #include "modules/distance.h"
+#include "modules/freevolume.h"
 #include "modules/select.h"
 
 namespace gmx
@@ -128,6 +129,7 @@ void registerTrajectoryAnalysisModules(CommandLineModuleManager *manager)
     using namespace gmx::analysismodules;
     manager->registerModule<TrajAnalysisCmdLineWrapper<Angle> >();
     manager->registerModule<TrajAnalysisCmdLineWrapper<Distance> >();
+    manager->registerModule<TrajAnalysisCmdLineWrapper<FreeVolume> >();
     manager->registerModule<TrajAnalysisCmdLineWrapper<Select> >();
 }
 
diff --git a/src/gromacs/trajectoryanalysis/modules/freevolume.cpp b/src/gromacs/trajectoryanalysis/modules/freevolume.cpp
new file mode 100644 (file)
index 0000000..67fb4d4
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013, by the GROMACS development team, led by
+ * David van der Spoel, Berk Hess, 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
+ * Implements gmx::analysismodules::Freevolume.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \ingroup module_trajectoryanalysis
+ */
+#include "freevolume.h"
+
+#include "gromacs/legacyheaders/pbc.h"
+#include "gromacs/legacyheaders/vec.h"
+#include "gromacs/legacyheaders/atomprop.h"
+#include "gromacs/legacyheaders/copyrite.h"
+
+#include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/analysisdata/modules/plot.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/filenameoption.h"
+#include "gromacs/options/options.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selectionoption.h"
+#include "gromacs/trajectoryanalysis/analysissettings.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+const char FreeVolume::name[]             = "freevolume";
+const char FreeVolume::shortDescription[] =
+    "Calculate free volume";
+
+// Constructor. Here it is important to initialize the pointer to
+// subclasses that are elements of the main class. Here we have only
+// one. The type of this depends on what kind of tool you need.
+// Here we only have simple value/time kind of data.
+FreeVolume::FreeVolume()
+    : TrajectoryAnalysisModule(name, shortDescription),
+      adata_(new AnalysisDataAverageModule())
+{
+    // We only compute two numbers per frame
+    data_.setColumnCount(2);
+    // Tell the analysis framework that this component exists
+    registerAnalysisDataset(&data_, "freevolume");
+    rng_         = NULL;
+    nbsearch_    = NULL;
+    nmol_        = 0;
+    mtot_        = 0;
+    cutoff_      = 0;
+    probeRadius_ = 0;
+    seed_        = -1;
+    ninsert_     = 1000;
+}
+
+
+FreeVolume::~FreeVolume()
+{
+    // Destroy C structures where there is no automatic memory release
+    // C++ takes care of memory in classes (hopefully)
+    if (NULL != rng_)
+    {
+        gmx_rng_destroy(rng_);
+    }
+    if (NULL != nbsearch_)
+    {
+        gmx_ana_nbsearch_free(nbsearch_);
+    }
+}
+
+
+void
+FreeVolume::initOptions(Options                    *options,
+                        TrajectoryAnalysisSettings *settings)
+{
+    static const char *const desc[] = {
+        "g_freevol can calculate the free volume in a box as",
+        "a function of time. The free volume is",
+        "plotted as a fraction of the total volume.",
+        "The program tries to insert a probe with a given radius,",
+        "into the simulations box and if the distance between the",
+        "probe and any atom is less than the sums of the",
+        "van der Waals radii of both atoms, the position is",
+        "considered to be occupied, i.e. non-free. By using a",
+        "probe radius of 0, the true free volume is computed.",
+        "By using a larger radius, e.g. 0.14 nm, roughly corresponding",
+        "to a water molecule, the free volume for a hypothetical",
+        "particle with that size will be produced.",
+        "Note however, that since atoms are treated as hard-spheres",
+        "these number are very approximate, and typically only",
+        "relative changes are meaningful, for instance by doing a",
+        "series of simulations at different temperature.[PAR]",
+        "The group specified by the selection is considered to",
+        "delineate non-free volume.",
+        "The number of insertions per unit of volume is important",
+        "to get a converged result. About 1000/nm^3 yields an overall",
+        "standard deviation that is determined by the fluctuations in",
+        "the trajectory rather than by the fluctuations due to the",
+        "random numbers.[PAR]",
+        "The results are critically dependent on the van der Waals radii;",
+        "we recommend to use the values due to Bondi (1964).[PAR]",
+        "The Fractional Free Volume (FFV) that some authors like to use",
+        "is given by 1 - 1.3*(1-Free Volume). This value is printed on",
+        "the terminal."
+    };
+
+    // Add the descriptive text (program help text) to the options
+    options->setDescription(concatenateStrings(desc));
+
+    // Add option for optional output file
+    options->addOption(FileNameOption("o").filetype(eftPlot).outputFile()
+                           .store(&fnFreevol_).defaultBasename("freevolume")
+                           .description("Computed free volume"));
+
+    // Add option for selecting a subset of atoms
+    options->addOption(SelectionOption("select").required().valueCount(1)
+                           .store(&sel_)
+                           .onlyAtoms());
+
+    // Add option for the probe radius and initialize it
+    options->addOption(DoubleOption("radius").store(&probeRadius_)
+                           .description("Radius of the probe to be inserted (nm, 0 yields the true free volume)"));
+
+    // Add option for the random number seed and initialize it to
+    // generate a value automatically
+    options->addOption(IntegerOption("seed").store(&seed_)
+                           .description("Seed for random number generator."));
+
+    // Add option to determine number of insertion trials per frame
+    options->addOption(IntegerOption("ninsert").store(&ninsert_)
+                           .description("Number of probe insertions per cubic nm to try for each frame in the trajectory."));
+
+    // Control input settings
+    settings->setFlags(TrajectoryAnalysisSettings::efRequireTop |
+                       TrajectoryAnalysisSettings::efNoUserPBC);
+    settings->setPBC(true);
+}
+
+
+void
+FreeVolume::initAnalysis(const TrajectoryAnalysisSettings &settings,
+                         const TopologyInformation        &top)
+{
+    // Add the module that will contain the averaging and the time series
+    // for our calculation
+    data_.addModule(adata_);
+
+    // Add a module for plotting the data automatically at the end of
+    // the calculation. With this in place you only have to add data
+    // points to the data et.
+    AnalysisDataPlotModulePointer plotm_(new AnalysisDataPlotModule());
+    plotm_->setSettings(settings.plotSettings());
+    plotm_->setFileName(fnFreevol_);
+    plotm_->setTitle("Free Volume");
+    plotm_->setXAxisIsTime();
+    plotm_->setYLabel("Free Volume (%)");
+    plotm_->appendLegend("Free Volume");
+    plotm_->appendLegend("Volume");
+
+    data_.addModule(plotm_);
+
+    // Initiate variable
+    cutoff_               = 0;
+    int            nnovdw = 0;
+    gmx_atomprop_t aps    = gmx_atomprop_init();
+    t_atoms       *atoms  = &(top.topology()->atoms);
+
+    // Compute total mass
+    mtot_ = 0;
+    for (int i = 0; (i < atoms->nr); i++)
+    {
+        mtot_ += atoms->atom[i].m;
+    }
+
+    // Extracts number of molecules
+    nmol_ = top.topology()->mols.nr;
+
+    // Loop over atoms in the selection using an iterator
+    const int          maxnovdw = 10;
+    ConstArrayRef<int> atomind  = sel_.atomIndices();
+    for (ConstArrayRef<int>::iterator ai = atomind.begin(); (ai < atomind.end()); ++ai)
+    {
+        // Dereference the iterator to obtain an atom number
+        int  i = *ai;
+        real value;
+
+        // Lookup the Van der Waals radius of this atom
+        int resnr = atoms->atom[i].resind;
+        if (TRUE == gmx_atomprop_query(aps, epropVDW,
+                                       *(atoms->resinfo[resnr].name),
+                                       *(atoms->atomname[i]),
+                                       &value))
+        {
+            vdw_radius_.push_back(value);
+            if (value > cutoff_)
+            {
+                cutoff_ = value;
+            }
+        }
+        else
+        {
+            nnovdw++;
+            if (nnovdw < maxnovdw)
+            {
+                fprintf(stderr, "Could not determine VDW radius for %s-%s. Set to zero.\n",
+                        *(atoms->resinfo[resnr].name),
+                        *(atoms->atomname[i]));
+            }
+            vdw_radius_.push_back(0.0);
+        }
+    }
+    gmx_atomprop_destroy(aps);
+
+    // Increase cutoff by proberadius to make sure we do not miss
+    // anything
+    cutoff_ += probeRadius_;
+
+    if (nnovdw >= maxnovdw)
+    {
+        fprintf(stderr, "Could not determine VDW radius for %d particles. These were set to zero.\n", nnovdw);
+    }
+
+    // Print parameters to output. Maybe should make dependent on
+    // verbosity flag?
+    printf("cutoff       = %g nm\n", cutoff_);
+    printf("probe_radius = %g nm\n", probeRadius_);
+    printf("seed         = %d\n", seed_);
+    printf("ninsert      = %d probes per nm^3\n", ninsert_);
+
+    // Initiate the random number generator
+    rng_ = gmx_rng_init(seed_);
+
+    // Initiate the neighborsearching code
+    nbsearch_ = gmx_ana_nbsearch_create(cutoff_, atoms->nr);
+}
+
+void
+FreeVolume::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
+                         TrajectoryAnalysisModuleData *pdata)
+{
+    AnalysisDataHandle       dh   = pdata->dataHandle(data_);
+    const Selection         &sel  = pdata->parallelSelection(sel_);
+
+    GMX_RELEASE_ASSERT(NULL != pbc, "You have no periodic boundary conditions");
+
+    // Analysis framework magic
+    dh.startFrame(frnr, fr.time);
+
+    // Compute volume and number of insertions to perform
+    real V       = det(fr.box);
+    int  Ninsert = static_cast<int>(ninsert_*V);
+
+    // Use neighborsearching tools!
+    gmx_ana_nbsearch_pos_init(nbsearch_, pbc, sel.positions());
+
+    // Then loop over insertions
+    int NinsTot = 0;
+    for (int i = 0; (i < Ninsert); i++)
+    {
+        rvec rand, ins, dx;
+
+        for (int m = 0; (m < DIM); m++)
+        {
+            // Generate random number between 0 and 1
+            rand[m] = gmx_rng_uniform_real(rng_);
+        }
+        // Generate random 3D position within the box
+        mvmul(fr.box, rand, ins);
+
+        // Find the first reference position within the cutoff.
+        bool bOverlap = false;
+        int  jp       = NOTSET;
+        if (gmx_ana_nbsearch_first_within(nbsearch_, ins, &jp))
+        {
+            do
+            {
+                // Compute distance vector to first atom in the neighborlist
+                pbc_dx(pbc, ins, sel.position(jp).x(), dx);
+
+                // See whether the distance is smaller than allowed
+                bOverlap = (norm(dx) <
+                            probeRadius_+vdw_radius_[sel.position(jp).refId()]);
+
+                // Finds the next reference position within the cutoff.
+            }
+            while (!bOverlap &&
+                   (gmx_ana_nbsearch_next_within(nbsearch_, &jp)));
+        }
+
+        if (!bOverlap)
+        {
+            // We found some free volume!
+            NinsTot++;
+        }
+    }
+    // Compute total free volume for this frame
+    double frac = 0;
+    if (Ninsert > 0)
+    {
+        frac = (100.0*NinsTot)/Ninsert;
+    }
+    // Add the free volume fraction to the data set in column 0
+    dh.setPoint(0, frac);
+    // Add the total volume to the data set in column 1
+    dh.setPoint(1, V);
+
+    // Magic
+    dh.finishFrame();
+}
+
+
+void
+FreeVolume::finishAnalysis(int nframes)
+{
+    please_cite(stdout, "Bondi1964a");
+}
+
+void
+FreeVolume::writeOutput()
+{
+    // Final results come from statistics module in analysis framework
+    double FVaver  = adata_->average(0);
+    double FVerror = adata_->stddev(0);
+    printf("Free volume %.2f +/- %.2f %%\n", FVaver, FVerror);
+
+    double Vaver  = adata_->average(1);
+    double Verror = adata_->stddev(1);
+    printf("Total volume %.2f +/- %.2f nm^3\n", Vaver, Verror);
+
+    printf("Number of molecules %d total mass %.2f Dalton\n", nmol_, mtot_);
+    double RhoAver  = mtot_ / (adata_->average(1) * 1e-24 * AVOGADRO);
+    double RhoError = sqr(RhoAver / Vaver)*Verror;
+    printf("Average molar mass: %.2f Dalton\n", mtot_/nmol_);
+
+    double VmAver  = Vaver/nmol_;
+    double VmError = Verror/nmol_;
+    printf("Density rho: %.2f +/- %.2f nm^3\n", RhoAver, RhoError);
+    printf("Molecular volume Vm assuming homogeneity: %.4f +/- %.4f nm^3\n",
+           VmAver, VmError);
+
+    double VvdWaver  = (1-FVaver/100)*VmAver;
+    double VvdWerror = 0;
+    printf("Molecular van der Waals volume assuming homogeneity:  %.4f +/- %.4f nm^3\n",
+           VvdWaver, VvdWerror);
+
+    double FFVaver  = 1-1.3*((100-FVaver)/100);
+    double FFVerror = (FVerror/FVaver)*FFVaver;
+    printf("Fractional free volume %.3f +/- %.3f\n", FFVaver, FFVerror);
+}
+
+} // namespace analysismodules
+
+} // namespace gmx
diff --git a/src/gromacs/trajectoryanalysis/modules/freevolume.h b/src/gromacs/trajectoryanalysis/modules/freevolume.h
new file mode 100644 (file)
index 0000000..6c4f215
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013, by the GROMACS development team, led by
+ * David van der Spoel, Berk Hess, 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
+ * Declares trajectory analysis module for distance calculations.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_FREEVOLUME_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_FREEVOLUME_H
+
+#include <string>
+#include "gromacs/legacyheaders/gmx_random.h"
+#include "../analysismodule.h"
+#include "gromacs/selection/nbsearch.h"
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/analysisdata/modules/average.h"
+#include "gromacs/selection/selection.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+/*! \brief
+ * Class used to compute free volume in a simulations box.
+ *
+ * Inherits TrajectoryAnalysisModule and all functions from there.
+ * Does not implement any new functionality.
+ *
+ * \inpublicapi
+ * \ingroup module_trajectoryanalysis
+ */
+class FreeVolume : public TrajectoryAnalysisModule
+{
+    public:
+        //! Name of the tool
+        static const char name[];
+
+        //! One line description
+        static const char shortDescription[];
+
+        //! Constructor
+        FreeVolume();
+
+        //! Destructor
+        virtual ~FreeVolume();
+
+        //! Set the options and setting
+        virtual void initOptions(Options                    *options,
+                                 TrajectoryAnalysisSettings *settings);
+
+        //! First routine called by the analysis frame work
+        virtual void initAnalysis(const TrajectoryAnalysisSettings &settings,
+                                  const TopologyInformation        &top);
+
+        //! Call for each frame of the trajectory
+        virtual void analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
+                                  TrajectoryAnalysisModuleData *pdata);
+
+        //! Last routine called by the analysis frame work
+        virtual void finishAnalysis(int nframes);
+
+        //! Routine to write output, that is additional over the built-in
+        virtual void writeOutput();
+
+    private:
+        std::string                       fnFreevol_;
+        Selection                         sel_;
+        AnalysisData                      data_;
+        AnalysisDataAverageModulePointer  adata_;
+
+        int                               nmol_;
+        double                            mtot_;
+        double                            cutoff_;
+        double                            probeRadius_;
+        gmx_rng_t                         rng_;
+        int                               seed_, ninsert_;
+        gmx_ana_nbsearch_t               *nbsearch_;
+        //! The van der Waals radius per atom
+        std::vector<double>               vdw_radius_;
+
+        // Copy and assign disallowed by base.
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
index 215e8c373172f67ff3545abfb72ad74e88458131..ccf7b856078e25e133dbbbeae9a4527f9c5be9a1 100644 (file)
@@ -35,6 +35,7 @@
 gmx_add_unit_test(TrajectoryAnalysisUnitTests trajectoryanalysis-test
                   moduletest.cpp
                   angle.cpp
+                  freevolume.cpp
                   select.cpp)
 
 add_executable(test_selection test_selection.cpp)
diff --git a/src/gromacs/trajectoryanalysis/tests/freevolume.cpp b/src/gromacs/trajectoryanalysis/tests/freevolume.cpp
new file mode 100644 (file)
index 0000000..e31bbac
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013, by the GROMACS development team, led by
+ * David van der Spoel, Berk Hess, 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
+ * Tests for functionality of the "angle" trajectory analysis module.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \ingroup module_trajectoryanalysis
+ */
+#include <gtest/gtest.h>
+
+#include "gromacs/trajectoryanalysis/modules/freevolume.h"
+
+#include "testutils/cmdlinetest.h"
+
+#include "moduletest.h"
+
+namespace
+{
+
+using gmx::test::CommandLine;
+
+/********************************************************************
+ * Tests for gmx::analysismodules::Angle.
+ */
+
+//! Test fixture for the angle analysis module.
+typedef gmx::test::TrajectoryAnalysisModuleTestFixture<gmx::analysismodules::FreeVolume>
+    FreeVolumeModuleTest;
+
+TEST_F(FreeVolumeModuleTest, ComputesFreeVolume)
+{
+    const char *const cmdline[] = {
+        "freevolume",
+        "-select", "all", "-seed", "13"
+    };
+    setTopology("freevolume.tpr");
+    setTrajectory("freevolume.xtc");
+    runTest(CommandLine::create(cmdline));
+}
+
+TEST_F(FreeVolumeModuleTest, ComputesFreeVolumeSelection)
+{
+    const char *const cmdline[] = {
+        "freevolume",
+        "-select", "not resname = CO2", "-seed", "17"
+    };
+    setTopology("freevolume.tpr");
+    setTrajectory("freevolume.xtc");
+    runTest(CommandLine::create(cmdline));
+}
+
+} // namespace
diff --git a/src/gromacs/trajectoryanalysis/tests/freevolume.tpr b/src/gromacs/trajectoryanalysis/tests/freevolume.tpr
new file mode 100644 (file)
index 0000000..a06c635
Binary files /dev/null and b/src/gromacs/trajectoryanalysis/tests/freevolume.tpr differ
diff --git a/src/gromacs/trajectoryanalysis/tests/freevolume.xtc b/src/gromacs/trajectoryanalysis/tests/freevolume.xtc
new file mode 100644 (file)
index 0000000..6cfc635
Binary files /dev/null and b/src/gromacs/trajectoryanalysis/tests/freevolume.xtc differ
diff --git a/src/gromacs/trajectoryanalysis/tests/refdata/FreeVolumeModuleTest_ComputesFreeVolume.xml b/src/gromacs/trajectoryanalysis/tests/refdata/FreeVolumeModuleTest_ComputesFreeVolume.xml
new file mode 100644 (file)
index 0000000..a423d2c
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="CommandLine">freevolume -select all -seed 13</String>
+  <OutputData Name="Data">
+    <AnalysisData Name="freevolume">
+      <DataFrame Name="Frame0">
+        <Real Name="X">0.000000</Real>
+        <Sequence Name="Y">
+          <Int Name="Length">2</Int>
+          <DataValue>
+            <Real Name="Value">37.199112</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">68.921501</Real>
+          </DataValue>
+        </Sequence>
+      </DataFrame>
+    </AnalysisData>
+  </OutputData>
+</ReferenceData>
diff --git a/src/gromacs/trajectoryanalysis/tests/refdata/FreeVolumeModuleTest_ComputesFreeVolumeSelection.xml b/src/gromacs/trajectoryanalysis/tests/refdata/FreeVolumeModuleTest_ComputesFreeVolumeSelection.xml
new file mode 100644 (file)
index 0000000..a0f75cb
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="CommandLine">freevolume -select 'not resname = CO2' -seed 17</String>
+  <OutputData Name="Data">
+    <AnalysisData Name="freevolume">
+      <DataFrame Name="Frame0">
+        <Real Name="X">0.000000</Real>
+        <Sequence Name="Y">
+          <Int Name="Length">2</Int>
+          <DataValue>
+            <Real Name="Value">38.779182</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">68.921501</Real>
+          </DataValue>
+        </Sequence>
+      </DataFrame>
+    </AnalysisData>
+  </OutputData>
+</ReferenceData>