2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2013,2014,2015,2016,2017, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
38 * Tool for extracting AWH (Accelerated Weight Histogram) data from energy files.
40 * \author Viveca Lindahl
54 #include "gromacs/commandline/pargs.h"
55 #include "gromacs/fileio/enxio.h"
56 #include "gromacs/fileio/gmxfio.h"
57 #include "gromacs/fileio/oenv.h"
58 #include "gromacs/fileio/tpxio.h"
59 #include "gromacs/fileio/trxio.h"
60 #include "gromacs/fileio/xvgr.h"
61 #include "gromacs/gmxana/gmx_ana.h"
62 #include "gromacs/math/units.h"
63 #include "gromacs/mdtypes/awh-params.h"
64 #include "gromacs/mdtypes/inputrec.h"
65 #include "gromacs/topology/mtop_util.h"
66 #include "gromacs/topology/topology.h"
67 #include "gromacs/utility/arraysize.h"
68 #include "gromacs/utility/basedefinitions.h"
69 #include "gromacs/utility/fatalerror.h"
70 #include "gromacs/utility/futil.h"
71 #include "gromacs/utility/gmxassert.h"
72 #include "gromacs/utility/smalloc.h"
73 #include "gromacs/utility/stringutil.h"
76 using gmx::AwhBiasParams
;
81 //! Enum for choosing AWH graph output
82 enum class AwhGraphSelection
84 Pmf
, //!< Only the PMF
85 All
//!< All possible AWH graphs
88 //! Energy unit options
97 /*! \brief All meta-data that is shared for one output file type for one bias */
101 /*! \brief Constructor, Set the output base file name and title.
103 * Result is a valid object, but will produce empty output files.
105 OutputFile(const std::string filename
,
106 const std::string baseTitle
,
110 /*! \brief Initializes the output file setup for the AWH output.
112 void initializeAwhOutputFile(int subblockStart
,
114 const AwhBiasParams
*awhBiasParams
,
115 AwhGraphSelection graphSelection
,
116 EnergyUnit energyUnit
,
119 /*! \brief Opens a single output file for a bias, prints title and legends.
121 * \param[in] time The time for of frame to be written.
122 * \param[in] oenv The output environment.
123 * \returns the file pointer.
125 FILE * openBiasOutputFile(double time
,
126 const gmx_output_env_t
*oenv
) const;
128 /*! \brief Writes data selected from \p block to file.
130 * \param[in] block The energy block with the data to write.
131 * \param[in] subBlockStart The sub-block start index.
132 * \param[in] fp The file pointer.
134 void writeData(const t_enxblock
&block
,
139 std::string baseFilename_
; /**< Base of the output file name. */
140 std::string title_
; /**< Title for the graph. */
141 int numDim_
; /**< Number of dimensions. */
142 int firstGraphSubblock_
; /**< Index in the energy sub-blocks for the first graph. */
143 int numGraph_
; /**< Number of actual graphs. */
144 bool useKTForEnergy_
; /**< Whether to use kT instead of kJ/mol for energy. */
145 std::vector
<real
> scaleFactor_
; /**< Scale factors from energy file data to output for each of the numGraph quantities. */
147 std::vector
<std::string
> legend_
; /**< Legends for the output */
148 std::string xLabel_
; /**< Label for the x-axis. */
149 std::string yLabel_
; /**< Label for the y-axis. */
152 /*! \brief All meta-data that is shared for all output files for one bias */
157 BiasReader(int subblockStart
,
159 std::unique_ptr
<OutputFile
> awhOutputFile
) :
160 subblockStart_(subblockStart
),
161 numSubblocks_(numSubblocks
),
162 awhOutputFile_(std::move(awhOutputFile
))
166 //! Return the AWH output file data.
167 const OutputFile
&awhOutputFile() const
169 return *awhOutputFile_
.get();
172 //! Return the starting subblock.
173 int subblockStart() const
175 return subblockStart_
;
179 const int subblockStart_
; /**< The start index of the subblocks to read. */
180 const int numSubblocks_
; /**< Number of subblocks to read. */
181 std::unique_ptr
<OutputFile
> awhOutputFile_
; /**< The standard AWH output file data. */
182 /* NOTE: A second OutputFile will be added soon, this will also make numSubblocks_ useful. */
185 /*! \brief All options and meta-data needed for the AWH output */
190 AwhReader(const AwhParams
*awhParams
,
192 const t_filenm
*filenames
,
193 AwhGraphSelection awhGraphSelection
,
194 EnergyUnit energyUnit
,
196 const t_enxblock
*block
);
198 //! Extract and print AWH data for an AWH block of one energy frame
199 void processAwhFrame(const t_enxblock
&block
,
201 const gmx_output_env_t
*oenv
) const;
204 std::vector
<BiasReader
> biasReader_
; /**< The readers, one for each AWH bias. */
206 const real kT_
; /**< kB*T in kJ/mol. */
212 /* NOTE: A second value will be added soon. */
217 /*! \brief The maximum number of observables per subblock, not including the full friction tensor.
219 * This number, as well as the legends in make_legend() should match
220 * the output that mdrun writes. It would be better to define these
221 * values in a single location.
223 static constexpr int maxAwhGraphs
= 5;
225 /*! \brief Constructs a legend for a standard awh output file */
226 std::vector
<std::string
>makeLegend(const AwhBiasParams
*awhBiasParams
,
230 const std::array
<std::string
, maxAwhGraphs
> legendBase
=
236 "Target ref value distr" }
239 std::vector
<std::string
> legend
;
240 /* Give legends to dimensions higher than the first */
241 for (int d
= 1; d
< awhBiasParams
->ndim
; d
++)
243 legend
.push_back(gmx::formatString("dim%d", d
));
246 switch (awhGraphType
)
248 case awhGraphTypeAwh
:
250 /* Add as many legends as possible from the "base" legend list */
251 size_t legendBaseIndex
= 0;
252 while (legend
.size() < numLegend
&& legendBaseIndex
< legendBase
.size())
254 legend
.push_back(legendBase
[legendBaseIndex
]);
261 GMX_RELEASE_ASSERT(legend
.size() == numLegend
, "The number of legends requested for printing and the number generated should be the same");
268 OutputFile::OutputFile(const std::string filename
,
269 const std::string baseTitle
,
273 firstGraphSubblock_(0),
281 // cppcheck-suppress useInitializationList
282 baseFilename_
= filename
.substr(0, filename
.find('.'));
286 baseFilename_
+= gmx::formatString("%d", biasIndex
+ 1);
287 title_
+= gmx::formatString(" %d", biasIndex
+ 1);
291 void OutputFile::initializeAwhOutputFile(int subblockStart
,
293 const AwhBiasParams
*awhBiasParams
,
294 AwhGraphSelection graphSelection
,
295 EnergyUnit energyUnit
,
298 /* The first subblock with actual graph y-values is index 1 + ndim */
299 numDim_
= awhBiasParams
->ndim
;
300 firstGraphSubblock_
= subblockStart
+ 1 + numDim_
;
301 if (graphSelection
== AwhGraphSelection::All
)
303 /* There are one metadata and ndim coordinate blocks */
304 numGraph_
= std::min(numSubblocks
- 1 - numDim_
,
311 useKTForEnergy_
= (energyUnit
== EnergyUnit::KT
);
312 scaleFactor_
.resize(numGraph_
, 1);
313 if (!useKTForEnergy_
)
315 /* The first two graphs are in units of energy, multiply by kT */
316 std::fill(scaleFactor_
.begin(), scaleFactor_
.begin() + std::min(2, numGraph_
), kTValue
);
318 int numLegend
= numDim_
- 1 + numGraph_
;
319 legend_
= makeLegend(awhBiasParams
, awhGraphTypeAwh
, numLegend
);
320 /* We could have both length and angle coordinates in a single bias */
321 xLabel_
= "(nm or deg)";
322 yLabel_
= useKTForEnergy_
? "(k\\sB\\NT)" : "(kJ/mol)";
323 if (graphSelection
== AwhGraphSelection::All
)
325 yLabel_
+= gmx::formatString(", (nm\\S-%d\\N or rad\\S-%d\\N), (-)",
326 awhBiasParams
->ndim
, awhBiasParams
->ndim
);
330 AwhReader::AwhReader(const AwhParams
*awhParams
,
332 const t_filenm
*filenames
,
333 AwhGraphSelection awhGraphSelection
,
334 EnergyUnit energyUnit
,
336 const t_enxblock
*block
) :
339 GMX_RELEASE_ASSERT(block
!= nullptr, "NULL pointer passed to initAwhReader");
341 /* The first subblock of each AWH block has metadata about
342 * the number of subblocks belonging to that AWH block.
343 * (block->nsub gives only the total number of subblocks and not how
344 * they are distributed between the AWH biases).
347 /* Keep track of the first subblock of this AWH */
348 int subblockStart
= 0;
349 for (int k
= 0; k
< awhParams
->numBias
; k
++)
351 AwhBiasParams
*awhBiasParams
= &awhParams
->awhBiasParams
[k
];
353 int numSubblocks
= (int)block
->sub
[subblockStart
].fval
[0];
355 std::unique_ptr
<OutputFile
> outputFileAwh(new OutputFile(opt2fn("-o", numFileOptions
, filenames
), "AWH", awhParams
->numBias
, k
));
357 outputFileAwh
->initializeAwhOutputFile(subblockStart
, numSubblocks
,
358 awhBiasParams
, awhGraphSelection
,
361 biasReader_
.emplace_back(BiasReader(subblockStart
, numSubblocks
, std::move(outputFileAwh
)));
363 subblockStart
+= numSubblocks
;
367 FILE * OutputFile::openBiasOutputFile(double time
,
368 const gmx_output_env_t
*oenv
) const
370 std::string filename
= baseFilename_
+ gmx::formatString("_t%g.xvg", time
);
372 FILE *fp
= xvgropen(filename
.c_str(), title_
.c_str(), xLabel_
, yLabel_
, oenv
);
373 xvgrLegend(fp
, legend_
, oenv
);
378 /*! \brief Prints data selected by \p outputFile from \p block to \p fp */
379 void OutputFile::writeData(const t_enxblock
&block
,
383 int numPoints
= block
.sub
[subBlockStart
+ 1].nr
;
384 for (int j
= 0; j
< numPoints
; j
++)
386 /* Print the coordinates for numDim dimensions */
387 for (int d
= 0; d
< numDim_
; d
++)
389 fprintf(fp
, " %8.4f", block
.sub
[subBlockStart
+ 1 + d
].fval
[j
]);
392 /* Print numGraph observables */
393 for (int i
= 0; i
< numGraph_
; i
++)
395 fprintf(fp
, " %g", block
.sub
[firstGraphSubblock_
+ i
].fval
[j
]*scaleFactor_
[i
]);
402 void AwhReader::processAwhFrame(const t_enxblock
&block
,
404 const gmx_output_env_t
*oenv
) const
406 /* We look for AWH data every energy frame and count the no of AWH frames found. We only extract every 'skip' AWH frame. */
408 for (auto &biasReader
: biasReader_
)
410 /* Each frame and AWH instance extracted generates one xvg file. */
412 const OutputFile
&awhOutputFile
= biasReader
.awhOutputFile();
414 const int subStart
= biasReader
.subblockStart();
416 FILE *fpAwh
= awhOutputFile
.openBiasOutputFile(time
, oenv
);
418 /* Now do the actual printing. Metadata in first subblock is treated separately. */
419 fprintf(fpAwh
, "# AWH metadata: target error = %.2f kT = %.2f kJ/mol\n",
420 block
.sub
[subStart
].fval
[1],
421 block
.sub
[subStart
].fval
[1]*kT_
);
423 fprintf(fpAwh
, "# AWH metadata: log sample weight = %4.2f\n",
424 block
.sub
[subStart
].fval
[2]);
426 awhOutputFile
.writeData(block
, subStart
, fpAwh
);
433 /*! \brief The main function for the AWH tool */
434 int gmx_awh(int argc
, char *argv
[])
436 const char *desc
[] = {
437 "[THISMODULE] extracts AWH data from an energy file.",
438 "One file is written per AWH bias per time frame.",
439 "The bias index, if more than one, is appended to the file, as well as",
440 "the time of the frame. By default only the PMF is printed.",
441 "With [TT]-more[tt] the bias, target and coordinate distributions",
444 static gmx_bool moreGraphs
= FALSE
;
446 static gmx_bool kTUnit
= FALSE
;
448 { "-skip", FALSE
, etINT
, {&skip
},
449 "Skip number of frames between data points" },
450 { "-more", FALSE
, etBOOL
, {&moreGraphs
},
451 "Print more output" },
452 { "-kt", FALSE
, etBOOL
, {&kTUnit
},
453 "Print free energy output in units of kT instead of kJ/mol" }
458 gmx_enxnm_t
*enm
= nullptr;
461 gmx_output_env_t
*oenv
;
464 { efEDR
, "-f", nullptr, ffREAD
},
465 { efTPR
, "-s", nullptr, ffREAD
},
466 { efXVG
, "-o", "awh", ffWRITE
}
468 const int nfile
= asize(fnm
);
469 if (!parse_common_args(&argc
, argv
,
470 PCA_CAN_VIEW
| PCA_CAN_BEGIN
| PCA_CAN_END
,
471 nfile
, fnm
, asize(pa
), pa
, asize(desc
), desc
, 0, nullptr, &oenv
))
477 fp
= open_enx(ftp2fn(efEDR
, nfile
, fnm
), "r");
478 do_enxnms(fp
, &nre
, &enm
);
480 /* We just need the AWH parameters from inputrec. These are used to initialize
481 the AWH reader when we have a frame to read later on. */
485 read_tpx(ftp2fn(efTPR
, nfile
, fnm
), &ir
, box
, &natoms
, nullptr, nullptr, &mtop
);
489 gmx_fatal(FARGS
, "No AWH data in %s\n", opt2fn("-f", nfile
, fnm
));
492 std::unique_ptr
<AwhReader
> awhReader
;
494 /* Initiate counters */
496 int awhFrameCounter
= 0;
500 haveFrame
= do_enx(fp
, frame
);
502 bool useFrame
= false;
504 t_enxblock
*block
= nullptr;
508 timeCheck
= check_times(frame
->t
);
512 block
= find_block_id_enxframe(frame
, enxAWH
, nullptr);
514 if (block
!= nullptr)
516 if ((skip
== 0) || (awhFrameCounter
% skip
== 0))
527 /* We read a valid frame with an AWH block, so we can use it */
529 /* Currently we have the number of subblocks per AWH stored
530 * in the first subblock (we cannot get this directly from the tpr),
531 * so we have to read an AWH block before making the legends.
533 if (awhReader
== nullptr)
535 AwhGraphSelection awhGraphSelection
= (moreGraphs
? AwhGraphSelection::All
: AwhGraphSelection::Pmf
);
536 EnergyUnit energyUnit
= (kTUnit
? EnergyUnit::KT
: EnergyUnit::KJPerMol
);
538 std::unique_ptr
<AwhReader
>(new AwhReader(ir
.awhParams
,
541 energyUnit
, BOLTZ
*ir
.opts
.ref_t
[0],
545 awhReader
->processAwhFrame(*block
, frame
->t
, oenv
);
548 while (haveFrame
&& (timeCheck
<= 0));
550 fprintf(stderr
, "\n");