Update energy and trajectory frame types
[gromacs.git] / src / gromacs / gmxana / gmx_awh.cpp
blob36f3aa3a13b969c0ab0a33d4076758b667aedb99
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2013,2014,2015,2016,2017,2018, 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.
36 /*! \internal \file
37 * \brief
38 * Tool for extracting AWH (Accelerated Weight Histogram) data from energy files.
40 * \author Viveca Lindahl
41 * \author Berk Hess
44 #include "gmxpre.h"
46 #include <cstdlib>
47 #include <cstring>
49 #include <algorithm>
50 #include <array>
51 #include <memory>
52 #include <string>
54 #include "gromacs/commandline/pargs.h"
55 #include "gromacs/compat/make_unique.h"
56 #include "gromacs/fileio/enxio.h"
57 #include "gromacs/fileio/gmxfio.h"
58 #include "gromacs/fileio/oenv.h"
59 #include "gromacs/fileio/tpxio.h"
60 #include "gromacs/fileio/trxio.h"
61 #include "gromacs/fileio/xvgr.h"
62 #include "gromacs/gmxana/gmx_ana.h"
63 #include "gromacs/math/units.h"
64 #include "gromacs/mdtypes/awh-params.h"
65 #include "gromacs/mdtypes/inputrec.h"
66 #include "gromacs/topology/mtop_util.h"
67 #include "gromacs/topology/topology.h"
68 #include "gromacs/trajectory/energyframe.h"
69 #include "gromacs/utility/arraysize.h"
70 #include "gromacs/utility/basedefinitions.h"
71 #include "gromacs/utility/fatalerror.h"
72 #include "gromacs/utility/futil.h"
73 #include "gromacs/utility/gmxassert.h"
74 #include "gromacs/utility/smalloc.h"
75 #include "gromacs/utility/stringutil.h"
77 using gmx::AwhParams;
78 using gmx::AwhBiasParams;
80 namespace
83 //! Enum for choosing AWH graph output
84 enum class AwhGraphSelection
86 Pmf, //!< Only the PMF
87 All //!< All possible AWH graphs
90 //! Energy unit options
91 enum class EnergyUnit
93 KJPerMol, //!< kJ/mol
94 KT //!< kT
97 } // namespace
99 /*! \brief All meta-data that is shared for one output file type for one bias */
100 class OutputFile
102 public:
103 /*! \brief Constructor, Set the output base file name and title.
105 * Result is a valid object, but will produce empty output files.
107 * \param[in] filename The name for output files, frame time, and possibly bias number, will be added per file/frame.
108 * \param[in] baseTitle The base title of the plot, the bias number might be added.
109 * \param[in] numBias The total number of AWH biases in the system.
110 * \param[in] biasIndex The index of this bias.
112 OutputFile(const std::string filename,
113 const std::string baseTitle,
114 int numBias,
115 int biasIndex);
117 /*! \brief Initializes the output file setup for the AWH output.
119 * \param[in] subBlockStart Index of the first sub-block to write in the energy frame.
120 * \param[in] numSubBlocks The total number of sub-blocks in the framw.
121 * \param[in] awhBiasParams The AWH bias parameters.
122 * \param[in] graphSelection Selects which graphs to plot.
123 * \param[in] energyUnit Requested energy unit in output.
124 * \param[in] kTValue kB*T in kJ/mol.
126 void initializeAwhOutputFile(int subBlockStart,
127 int numSubBlocks,
128 const AwhBiasParams *awhBiasParams,
129 AwhGraphSelection graphSelection,
130 EnergyUnit energyUnit,
131 real kTValue);
133 /*! \brief Initializes the output file setup for the fricion output.
135 * \param[in] subBlockStart Index of the first sub-block to write in the energy frame.
136 * \param[in] numSubBlocks The total number of sub-blocks in the framw.
137 * \param[in] awhBiasParams The AWH bias parameters.
138 * \param[in] energyUnit Requested energy unit in output.
139 * \param[in] kTValue kB*T in kJ/mol.
141 void initializeFrictionOutputFile(int subBlockStart,
142 int numSubBlocks,
143 const AwhBiasParams *awhBiasParams,
144 EnergyUnit energyUnit,
145 real kTValue);
147 /*! \brief Opens a single output file for a bias, prints title and legends.
149 * \param[in] time The time for of frame to be written.
150 * \param[in] oenv The output environment.
151 * \returns the file pointer.
153 FILE * openBiasOutputFile(double time,
154 const gmx_output_env_t *oenv) const;
156 /*! \brief Writes data selected from \p block to file.
158 * \param[in] block The energy block with the data to write.
159 * \param[in] subBlockStart The sub-block start index.
160 * \param[in] fp The file pointer.
162 void writeData(const t_enxblock &block,
163 int subBlockStart,
164 FILE *fp) const;
166 private:
167 std::string baseFilename_; /**< Base of the output file name. */
168 std::string title_; /**< Title for the graph. */
169 int numDim_; /**< Number of dimensions. */
170 int firstGraphSubBlock_; /**< Index in the energy sub-blocks for the first graph. */
171 int numGraph_; /**< Number of actual graphs. */
172 bool useKTForEnergy_; /**< Whether to use kT instead of kJ/mol for energy. */
173 std::vector<real> scaleFactor_; /**< Scale factors from energy file data to output for each of the numGraph quantities. */
175 std::vector<std::string> legend_; /**< Legends for the output */
176 std::string xLabel_; /**< Label for the x-axis. */
177 std::string yLabel_; /**< Label for the y-axis. */
180 /*! \brief All meta-data that is shared for all output files for one bias */
181 struct BiasOutputSetup
183 //! Constructor.
184 BiasOutputSetup(int subblockStart,
185 std::unique_ptr<OutputFile> awhOutputFile,
186 std::unique_ptr<OutputFile> frictionOutputFile) :
187 subblockStart_(subblockStart),
188 awhOutputFile_(std::move(awhOutputFile)),
189 frictionOutputFile_(std::move(frictionOutputFile))
193 //! Return the AWH output file data.
194 const OutputFile &awhOutputFile() const
196 GMX_RELEASE_ASSERT(awhOutputFile_ != nullptr, "awhOutputFile() called without initialized AWH output file");
198 return *awhOutputFile_.get();
201 //! Return the a pointer to the friction output file data, can return nullptr
202 const OutputFile *frictionOutputFile() const
204 return frictionOutputFile_.get();
207 //! Return the starting subblock.
208 int subblockStart() const
210 return subblockStart_;
213 private:
214 const int subblockStart_; /**< The start index of the subblocks to read. */
215 std::unique_ptr<OutputFile> awhOutputFile_; /**< The standard AWH output file data. */
216 std::unique_ptr<OutputFile> frictionOutputFile_; /**< The friction/metric tensor output file data */
219 /*! \brief All options and meta-data needed for the AWH output */
220 class AwhReader
222 public:
223 //! Constructor
224 AwhReader(const AwhParams *awhParams,
225 int numFileOptions,
226 const t_filenm *filenames,
227 AwhGraphSelection awhGraphSelection,
228 EnergyUnit energyUnit,
229 real kT,
230 const t_enxblock *block);
232 //! Extract and print AWH data for an AWH block of one energy frame
233 void processAwhFrame(const t_enxblock &block,
234 double time,
235 const gmx_output_env_t *oenv) const;
237 private:
238 std::vector<BiasOutputSetup> biasOutputSetups_; /**< The output setups, one for each AWH bias. */
239 public:
240 const real kT_; /**< kB*T in kJ/mol. */
243 namespace
246 //! Enum for selecting output file type.
247 enum class OutputFileType
249 Awh, //!< AWH, all data except friction tensor.
250 Friction //!< The full friction tensor.
253 /*! \brief The maximum number of observables per subblock, not including the full friction tensor.
255 * This number, as well as the legends in make_legend() should match
256 * the output that mdrun writes. It would be better to define these
257 * values in a single location.
259 static constexpr int maxAwhGraphs = 6;
261 /*! \brief Constructs a legend for a standard awh output file */
262 std::vector<std::string>makeLegend(const AwhBiasParams *awhBiasParams,
263 OutputFileType outputFileType,
264 size_t numLegend)
266 const std::array<std::string, maxAwhGraphs> legendBase =
268 { "PMF",
269 "Coord bias",
270 "Coord distr",
271 "Ref value distr",
272 "Target ref value distr",
273 "Friction metric" }
276 std::vector<std::string> legend;
277 /* Give legends to dimensions higher than the first */
278 for (int d = 1; d < awhBiasParams->ndim; d++)
280 legend.push_back(gmx::formatString("dim%d", d));
283 switch (outputFileType)
285 case OutputFileType::Awh:
287 /* Add as many legends as possible from the "base" legend list */
288 size_t legendBaseIndex = 0;
289 while (legend.size() < numLegend && legendBaseIndex < legendBase.size())
291 legend.push_back(legendBase[legendBaseIndex]);
292 legendBaseIndex++;
295 break;
296 case OutputFileType::Friction:
297 for (int i0 = 0; i0 < awhBiasParams->ndim; i0++)
299 for (int i1 = 0; i1 <= i0; i1++)
301 legend.push_back(gmx::formatString("%d,%d", i0, i1));
304 break;
307 GMX_RELEASE_ASSERT(legend.size() == numLegend, "The number of legends requested for printing and the number generated should be the same");
309 return legend;
312 } // namespace
314 OutputFile::OutputFile(const std::string filename,
315 const std::string baseTitle,
316 int numBias,
317 int biasIndex) :
318 numDim_(0),
319 firstGraphSubBlock_(0),
320 numGraph_(0),
321 useKTForEnergy_(0),
322 scaleFactor_(),
323 legend_(),
324 xLabel_(),
325 yLabel_()
327 // cppcheck-suppress useInitializationList
328 baseFilename_ = filename.substr(0, filename.find('.'));
329 title_ = baseTitle;
330 if (numBias > 1)
332 baseFilename_ += gmx::formatString("%d", biasIndex + 1);
333 title_ += gmx::formatString(" %d", biasIndex + 1);
337 void OutputFile::initializeAwhOutputFile(int subblockStart,
338 int numSubBlocks,
339 const AwhBiasParams *awhBiasParams,
340 AwhGraphSelection graphSelection,
341 EnergyUnit energyUnit,
342 real kTValue)
344 /* The first subblock with actual graph y-values is index 1 + ndim */
345 numDim_ = awhBiasParams->ndim;
346 firstGraphSubBlock_ = subblockStart + 1 + numDim_;
347 if (graphSelection == AwhGraphSelection::All)
349 /* There are one metadata and ndim coordinate blocks */
350 numGraph_ = std::min(numSubBlocks - 1 - numDim_,
351 maxAwhGraphs);
353 else
355 numGraph_ = 1;
357 useKTForEnergy_ = (energyUnit == EnergyUnit::KT);
358 scaleFactor_.resize(numGraph_, 1);
359 if (!useKTForEnergy_)
361 /* The first two graphs are in units of energy, multiply by kT */
362 std::fill(scaleFactor_.begin(), scaleFactor_.begin() + std::min(2, numGraph_), kTValue);
364 int numLegend = numDim_ - 1 + numGraph_;
365 legend_ = makeLegend(awhBiasParams, OutputFileType::Awh, numLegend);
366 /* We could have both length and angle coordinates in a single bias */
367 xLabel_ = "(nm or deg)";
368 yLabel_ = useKTForEnergy_ ? "(k\\sB\\NT)" : "(kJ/mol)";
369 if (graphSelection == AwhGraphSelection::All)
371 yLabel_ += gmx::formatString(", (nm\\S-%d\\N or rad\\S-%d\\N), (-)",
372 awhBiasParams->ndim, awhBiasParams->ndim);
376 /*! \brief Initializes the output file setup for the fricion output (note that the filename is not set here). */
377 void OutputFile::initializeFrictionOutputFile(int subBlockStart,
378 int numSubBlocks,
379 const AwhBiasParams *awhBiasParams,
380 EnergyUnit energyUnit,
381 real kTValue)
383 /* The first subblock with actual graph y-values is index 1 + ndim */
384 numDim_ = awhBiasParams->ndim;
385 int numTensorElements = (numDim_*(numDim_ + 1))/2;
387 /* The friction tensor elements are always the last subblocks */
388 if (numSubBlocks < 1 + numDim_ + maxAwhGraphs + numTensorElements)
390 gmx_fatal(FARGS, "You requested friction tensor output, but the AWH data in the energy file does not contain the friction tensor");
392 GMX_ASSERT(numSubBlocks == 1 + numDim_ + maxAwhGraphs + numTensorElements, "The number of sub-blocks per bias should be 1 + ndim + maxAwhGraphs + (ndim*(ndim + 1))/2");
394 firstGraphSubBlock_ = subBlockStart + numSubBlocks - numTensorElements;
395 numGraph_ = numTensorElements;
396 useKTForEnergy_ = (energyUnit == EnergyUnit::KT);
397 scaleFactor_.resize(numGraph_, useKTForEnergy_ ? 1 : kTValue);
398 int numLegend = numDim_ - 1 + numGraph_;
399 legend_ = makeLegend(awhBiasParams, OutputFileType::Friction, numLegend);
400 xLabel_ = "(nm or deg)";
401 if (useKTForEnergy_)
403 yLabel_ = "friction/k\\sB\\NT (nm\\S-2\\Nps or rad\\S-2\\Nps)";
405 else
407 yLabel_ = "friction (kJ mol\\S-1\\Nnm\\S-2\\Nps or kJ mol\\S-1\\Nrad\\S-2\\Nps)";
411 AwhReader::AwhReader(const AwhParams *awhParams,
412 int numFileOptions,
413 const t_filenm *filenames,
414 AwhGraphSelection awhGraphSelection,
415 EnergyUnit energyUnit,
416 real kT,
417 const t_enxblock *block) :
418 kT_(kT)
420 GMX_RELEASE_ASSERT(block != nullptr, "NULL pointer passed to initAwhReader");
422 bool outputFriction = opt2bSet("-fric", numFileOptions, filenames);
424 /* The first subblock of each AWH block has metadata about
425 * the number of subblocks belonging to that AWH block.
426 * (block->nsub gives only the total number of subblocks and not how
427 * they are distributed between the AWH biases).
430 /* Keep track of the first subblock of this AWH */
431 int subblockStart = 0;
432 for (int k = 0; k < awhParams->numBias; k++)
434 AwhBiasParams *awhBiasParams = &awhParams->awhBiasParams[k];
436 int numSubBlocks = (int)block->sub[subblockStart].fval[0];
438 std::unique_ptr<OutputFile> awhOutputFile(new OutputFile(opt2fn("-o", numFileOptions, filenames), "AWH", awhParams->numBias, k));
440 awhOutputFile->initializeAwhOutputFile(subblockStart, numSubBlocks,
441 awhBiasParams, awhGraphSelection,
442 energyUnit, kT);
444 std::unique_ptr<OutputFile> frictionOutputFile;
445 if (outputFriction)
447 frictionOutputFile = gmx::compat::make_unique<OutputFile>(opt2fn("-fric", numFileOptions, filenames), "Friction tensor", awhParams->numBias, k);
449 frictionOutputFile->initializeFrictionOutputFile(subblockStart, numSubBlocks, awhBiasParams, energyUnit, kT);
452 biasOutputSetups_.emplace_back(BiasOutputSetup(subblockStart,
453 std::move(awhOutputFile),
454 std::move(frictionOutputFile)));
456 subblockStart += numSubBlocks;
460 FILE * OutputFile::openBiasOutputFile(double time,
461 const gmx_output_env_t *oenv) const
463 std::string filename = baseFilename_ + gmx::formatString("_t%g.xvg", time);
465 FILE *fp = xvgropen(filename.c_str(), title_.c_str(), xLabel_, yLabel_, oenv);
466 xvgrLegend(fp, legend_, oenv);
468 return fp;
471 /*! \brief Prints data selected by \p outputFile from \p block to \p fp */
472 void OutputFile::writeData(const t_enxblock &block,
473 int subBlockStart,
474 FILE *fp) const
476 int numPoints = block.sub[subBlockStart + 1].nr;
477 for (int j = 0; j < numPoints; j++)
479 /* Print the coordinates for numDim dimensions */
480 for (int d = 0; d < numDim_; d++)
482 fprintf(fp, " %8.4f", block.sub[subBlockStart + 1 + d].fval[j]);
485 /* Print numGraph observables */
486 for (int i = 0; i < numGraph_; i++)
488 fprintf(fp, " %g", block.sub[firstGraphSubBlock_ + i].fval[j]*scaleFactor_[i]);
491 fprintf(fp, "\n");
495 void AwhReader::processAwhFrame(const t_enxblock &block,
496 double time,
497 const gmx_output_env_t *oenv) const
499 /* We look for AWH data every energy frame and count the no of AWH frames found. We only extract every 'skip' AWH frame. */
501 for (const auto &setup : biasOutputSetups_)
503 const int subStart = setup.subblockStart();
505 /* Each frame and AWH instance extracted generates one xvg file. */
507 const OutputFile &awhOutputFile = setup.awhOutputFile();
509 FILE *fpAwh = awhOutputFile.openBiasOutputFile(time, oenv);
511 /* Now do the actual printing. Metadata in first subblock is treated separately. */
512 fprintf(fpAwh, "# AWH metadata: target error = %.2f kT = %.2f kJ/mol\n",
513 block.sub[subStart].fval[1],
514 block.sub[subStart].fval[1]*kT_);
516 fprintf(fpAwh, "# AWH metadata: log sample weight = %4.2f\n",
517 block.sub[subStart].fval[2]);
519 awhOutputFile.writeData(block, subStart, fpAwh);
521 gmx_ffclose(fpAwh);
524 const OutputFile *frictionOutputFile = setup.frictionOutputFile();
525 if (frictionOutputFile != nullptr)
527 FILE *fpFriction = frictionOutputFile->openBiasOutputFile(time, oenv);
529 frictionOutputFile->writeData(block, subStart, fpFriction);
531 gmx_ffclose(fpFriction);
536 /*! \brief The main function for the AWH tool */
537 int gmx_awh(int argc, char *argv[])
539 const char *desc[] = {
540 "[THISMODULE] extracts AWH data from an energy file.",
541 "One or two files are written per AWH bias per time frame.",
542 "The bias index, if more than one, is appended to the file, as well as",
543 "the time of the frame. By default only the PMF is printed.",
544 "With [TT]-more[tt] the bias, target and coordinate distributions",
545 "are also printed.",
546 "With [TT]-more[tt] the bias, target and coordinate distributions",
547 "are also printed, as well as the metric sqrt(det(friction_tensor))",
548 "normalized such that the average is 1.",
549 "Option [TT]-fric[tt] prints all components of the friction tensor",
550 "to an additional set of files."
552 static gmx_bool moreGraphs = FALSE;
553 static int skip = 0;
554 static gmx_bool kTUnit = FALSE;
555 t_pargs pa[] = {
556 { "-skip", FALSE, etINT, {&skip},
557 "Skip number of frames between data points" },
558 { "-more", FALSE, etBOOL, {&moreGraphs},
559 "Print more output" },
560 { "-kt", FALSE, etBOOL, {&kTUnit},
561 "Print free energy output in units of kT instead of kJ/mol" }
564 ener_file_t fp;
565 t_inputrec ir;
566 gmx_enxnm_t *enm = nullptr;
567 t_enxframe *frame;
568 int nre;
569 gmx_output_env_t *oenv;
571 t_filenm fnm[] = {
572 { efEDR, "-f", nullptr, ffREAD },
573 { efTPR, "-s", nullptr, ffREAD },
574 { efXVG, "-o", "awh", ffWRITE },
575 { efXVG, "-fric", "friction", ffOPTWR }
577 const int nfile = asize(fnm);
578 if (!parse_common_args(&argc, argv,
579 PCA_CAN_VIEW | PCA_CAN_BEGIN | PCA_CAN_END,
580 nfile, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
582 return 0;
585 snew(frame, 1);
586 fp = open_enx(ftp2fn(efEDR, nfile, fnm), "r");
587 do_enxnms(fp, &nre, &enm);
589 /* We just need the AWH parameters from inputrec. These are used to initialize
590 the AWH reader when we have a frame to read later on. */
591 gmx_mtop_t mtop;
592 int natoms;
593 matrix box;
594 read_tpx(ftp2fn(efTPR, nfile, fnm), &ir, box, &natoms, nullptr, nullptr, &mtop);
596 if (!ir.bDoAwh)
598 gmx_fatal(FARGS, "No AWH data in %s\n", opt2fn("-f", nfile, fnm));
601 std::unique_ptr<AwhReader> awhReader;
603 /* Initiate counters */
604 gmx_bool haveFrame;
605 int awhFrameCounter = 0;
606 int timeCheck = 0;
609 haveFrame = do_enx(fp, frame);
611 bool useFrame = false;
613 t_enxblock *block = nullptr;
615 if (haveFrame)
617 timeCheck = check_times(frame->t);
619 if (timeCheck == 0)
621 block = find_block_id_enxframe(frame, enxAWH, nullptr);
623 if (block != nullptr)
625 if ((skip == 0) || (awhFrameCounter % skip == 0))
627 useFrame = true;
629 awhFrameCounter++;
634 if (useFrame)
636 /* We read a valid frame with an AWH block, so we can use it */
638 /* Currently we have the number of subblocks per AWH stored
639 * in the first subblock (we cannot get this directly from the tpr),
640 * so we have to read an AWH block before making the legends.
642 if (awhReader == nullptr)
644 AwhGraphSelection awhGraphSelection = (moreGraphs ? AwhGraphSelection::All : AwhGraphSelection::Pmf);
645 EnergyUnit energyUnit = (kTUnit ? EnergyUnit::KT : EnergyUnit::KJPerMol);
646 awhReader =
647 std::unique_ptr<AwhReader>(new AwhReader(ir.awhParams,
648 nfile, fnm,
649 awhGraphSelection,
650 energyUnit, BOLTZ*ir.opts.ref_t[0],
651 block));
654 awhReader->processAwhFrame(*block, frame->t, oenv);
657 while (haveFrame && (timeCheck <= 0));
659 fprintf(stderr, "\n");
661 close_enx(fp);
663 return 0;