3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
9 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11 * Copyright (c) 2001-2009, The GROMACS development team,
12 * check out http://www.gromacs.org for more information.
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * If you want to redistribute modifications, please consider that
20 * scientific software is very special. Version control is crucial -
21 * bugs must be traceable. We will be happy to consider code for
22 * inclusion in the official distribution, but derived work must not
23 * be called official GROMACS. Details are found in the README & COPYING
24 * files - if they are missing, get the official version at www.gromacs.org.
26 * To help us fund GROMACS development, we humbly ask that you cite
27 * the papers on the package - you can find them in the top README file.
29 * For more info, check our website at http://www.gromacs.org
33 * Implements classes in histogram.h.
35 * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36 * \ingroup module_analysisdata
38 #include "gromacs/analysisdata/modules/histogram.h"
44 #include "gromacs/analysisdata/dataframe.h"
45 #include "gromacs/analysisdata/datastorage.h"
46 #include "gromacs/utility/exceptions.h"
47 #include "gromacs/utility/gmxassert.h"
52 //! Value used to signify that a real-valued histogram setting is not set.
53 const real UNDEFINED = std::numeric_limits<real>::max();
54 //! Checks whether \p value is defined.
55 bool isDefined(real value)
57 return value != UNDEFINED;
65 /********************************************************************
66 * AnalysisHistogramSettingsInitializer
69 AnalysisHistogramSettingsInitializer::AnalysisHistogramSettingsInitializer()
70 : min_(UNDEFINED), max_(UNDEFINED), binWidth_(UNDEFINED),
71 binCount_(0), bIntegerBins_(false), bRoundRange_(false),
77 /********************************************************************
78 * AnalysisHistogramSettings
81 AnalysisHistogramSettings::AnalysisHistogramSettings()
82 : firstEdge_(0.0), lastEdge_(0.0), binWidth_(0.0), inverseBinWidth_(0.0),
83 binCount_(0), bAll_(false)
88 AnalysisHistogramSettings::AnalysisHistogramSettings(
89 const AnalysisHistogramSettingsInitializer &settings)
91 GMX_RELEASE_ASSERT(isDefined(settings.min_),
92 "Histogram start value must be defined");
93 GMX_RELEASE_ASSERT(!isDefined(settings.max_) || settings.max_ > settings.min_,
94 "Histogram end value must be larger than start value");
95 GMX_RELEASE_ASSERT(!isDefined(settings.binWidth_) || settings.binWidth_ > 0.0,
96 "Histogram bin width must be positive");
97 GMX_RELEASE_ASSERT(settings.binCount_ >= 0,
98 "Histogram bin count must be positive");
100 if (!isDefined(settings.max_))
102 GMX_RELEASE_ASSERT(isDefined(settings.binWidth_) && settings.binCount_ > 0,
103 "Not all required values provided");
104 GMX_RELEASE_ASSERT(!settings.bRoundRange_,
105 "Rounding only supported for min/max ranges");
107 firstEdge_ = settings.min_;
108 binCount_ = settings.binCount_;
109 binWidth_ = settings.binWidth_;
110 if (settings.bIntegerBins_)
112 firstEdge_ -= 0.5 * binWidth_;
114 lastEdge_ = firstEdge_ + binCount_ * binWidth_;
118 GMX_RELEASE_ASSERT(!(isDefined(settings.binWidth_) && settings.binCount_ > 0),
119 "Conflicting histogram bin specifications");
120 GMX_RELEASE_ASSERT(isDefined(settings.binWidth_) || settings.binCount_ > 0,
121 "Not all required values provided");
123 if (settings.bRoundRange_)
125 GMX_RELEASE_ASSERT(!settings.bIntegerBins_,
126 "Rounding and integer bins cannot be combined");
127 GMX_RELEASE_ASSERT(isDefined(settings.binWidth_),
128 "Rounding only makes sense with defined binwidth");
129 binWidth_ = settings.binWidth_;
130 firstEdge_ = binWidth_ * floor(settings.min_ / binWidth_);
131 lastEdge_ = binWidth_ * ceil(settings.max_ / binWidth_);
132 binCount_ = static_cast<int>((lastEdge_ - firstEdge_) / binWidth_ + 0.5);
136 firstEdge_ = settings.min_;
137 lastEdge_ = settings.max_;
138 if (settings.binCount_ > 0)
140 binCount_ = settings.binCount_;
141 if (settings.bIntegerBins_)
143 GMX_RELEASE_ASSERT(settings.binCount_ > 1,
144 "Bin count must be at least two with integer bins");
145 binWidth_ = (lastEdge_ - firstEdge_) / (binCount_ - 1);
146 firstEdge_ -= 0.5 * binWidth_;
147 lastEdge_ += 0.5 * binWidth_;
151 binWidth_ = (lastEdge_ - firstEdge_) / binCount_;
156 binWidth_ = settings.binWidth_;
157 binCount_ = static_cast<int>((lastEdge_ - firstEdge_) / binWidth_ + 0.5);
158 if (settings.bIntegerBins_)
160 firstEdge_ -= 0.5 * binWidth_;
163 lastEdge_ = firstEdge_ + binCount_ * binWidth_;
168 inverseBinWidth_ = 1.0 / binWidth_;
169 bAll_ = settings.bIncludeAll_;
174 AnalysisHistogramSettings::findBin(real y) const
178 return bAll_ ? 0 : -1;
180 int bin = static_cast<int>((y - firstEdge_) * inverseBinWidth_);
181 if (bin >= binCount_)
183 return bAll_ ? binCount_ - 1 : -1;
189 /********************************************************************
190 * StaticAverageHistogram
197 * Represents copies of average histograms.
199 * Methods in AbstractAverageHistogram that return new histogram instances
200 * return objects of this class.
201 * Initialization of values is handled in those methods.
203 * \ingroup module_analysisdata
205 class StaticAverageHistogram : public AbstractAverageHistogram
208 StaticAverageHistogram();
209 //! Creates an average histogram module with defined bin parameters.
210 explicit StaticAverageHistogram(const AnalysisHistogramSettings &settings);
212 // Copy and assign disallowed by base.
215 StaticAverageHistogram::StaticAverageHistogram()
220 StaticAverageHistogram::StaticAverageHistogram(
221 const AnalysisHistogramSettings &settings)
222 : AbstractAverageHistogram(settings)
229 /********************************************************************
230 * AbstractAverageHistogram
233 AbstractAverageHistogram::AbstractAverageHistogram()
238 AbstractAverageHistogram::AbstractAverageHistogram(
239 const AnalysisHistogramSettings &settings)
240 : settings_(settings)
242 setRowCount(settings.binCount());
243 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
244 settings.binWidth());
248 AbstractAverageHistogram::~AbstractAverageHistogram()
254 AbstractAverageHistogram::init(const AnalysisHistogramSettings &settings)
256 settings_ = settings;
257 setRowCount(settings.binCount());
258 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
259 settings.binWidth());
263 AverageHistogramPointer
264 AbstractAverageHistogram::resampleDoubleBinWidth(bool bIntegerBins) const
269 nbins = (rowCount() + 1) / 2;
273 nbins = rowCount() / 2;
276 AverageHistogramPointer dest(
277 new StaticAverageHistogram(
278 histogramFromBins(xstart(), nbins, 2*xstep())
279 .integerBins(bIntegerBins)));
280 dest->setColumnCount(columnCount());
281 dest->allocateValues();
284 for (i = j = 0; i < nbins; ++i)
286 const bool bFirstHalfBin = (bIntegerBins && i == 0);
287 for (int c = 0; c < columnCount(); ++c)
298 v2 = value(j + 1, c);
302 dest->setValue(i, c, sqrt(v1 * v1 + v2 * v2));
306 dest->setValue(i, c, v1 + v2);
322 AverageHistogramPointer
323 AbstractAverageHistogram::clone() const
325 AverageHistogramPointer dest(new StaticAverageHistogram());
326 copyContents(this, dest.get());
332 AbstractAverageHistogram::normalizeProbability()
335 for (int i = 0; i < rowCount(); ++i)
339 scale(1.0 / (sum * xstep()));
344 AbstractAverageHistogram::scale(real norm)
346 for (int i = 0; i < rowCount(); ++i)
355 AbstractAverageHistogram::scaleVector(real norm[])
357 for (int i = 0; i < rowCount(); ++i)
359 value(i, 0) *= norm[i];
360 value(i, 1) *= norm[i];
365 /********************************************************************
366 * BasicAverageHistogramModule
373 * Implements average histogram module that averages per-frame histograms.
375 * This class is used for accumulating average histograms in per-frame
376 * histogram modules (those that use BasicHistogramImpl as their implementation
378 * There are two columns, first for the average and second for standard
381 * \ingroup module_analysisdata
383 class BasicAverageHistogramModule : public AbstractAverageHistogram,
384 public AnalysisDataModuleInterface
387 BasicAverageHistogramModule();
388 //! Creates an average histogram module with defined bin parameters.
389 explicit BasicAverageHistogramModule(const AnalysisHistogramSettings &settings);
391 using AbstractAverageHistogram::init;
393 virtual int flags() const;
395 virtual void dataStarted(AbstractAnalysisData *data);
396 virtual void frameStarted(const AnalysisDataFrameHeader &header);
397 virtual void pointsAdded(const AnalysisDataPointSetRef &points);
398 virtual void frameFinished(const AnalysisDataFrameHeader &header);
399 virtual void dataFinished();
402 //! Number of frames accumulated so far.
405 // Copy and assign disallowed by base.
408 BasicAverageHistogramModule::BasicAverageHistogramModule()
415 BasicAverageHistogramModule::BasicAverageHistogramModule(
416 const AnalysisHistogramSettings &settings)
417 : AbstractAverageHistogram(settings), frameCount_(0)
424 BasicAverageHistogramModule::flags() const
426 return efAllowMulticolumn;
431 BasicAverageHistogramModule::dataStarted(AbstractAnalysisData *data)
433 GMX_RELEASE_ASSERT(rowCount() == data->columnCount(),
434 "Inconsistent data sizes, something is wrong in the initialization");
440 BasicAverageHistogramModule::frameStarted(const AnalysisDataFrameHeader & /*header*/)
446 BasicAverageHistogramModule::pointsAdded(const AnalysisDataPointSetRef &points)
448 int firstcol = points.firstColumn();
449 for (int i = 0; i < points.columnCount(); ++i)
451 real y = points.y(i);
452 value(firstcol + i, 0) += y;
453 value(firstcol + i, 1) += y * y;
459 BasicAverageHistogramModule::frameFinished(const AnalysisDataFrameHeader & /*header*/)
466 BasicAverageHistogramModule::dataFinished()
468 for (int i = 0; i < rowCount(); ++i)
470 real ave = value(i, 0) / frameCount_;
471 real std = sqrt(value(i, 1) / frameCount_ - ave * ave);
478 /********************************************************************
483 * Private implementation class for AnalysisDataSimpleHistogramModule and
484 * AnalysisDataWeightedHistogramModule.
486 * \ingroup module_analysisdata
488 class BasicHistogramImpl
491 //! Smart pointer to manage an BasicAverageHistogramModule object.
492 typedef boost::shared_ptr<BasicAverageHistogramModule>
493 BasicAverageHistogramModulePointer;
495 BasicHistogramImpl();
496 //! Creates an histogram impl with defined bin parameters.
497 explicit BasicHistogramImpl(const AnalysisHistogramSettings &settings);
498 ~BasicHistogramImpl();
501 * (Re)initializes the histogram from settings.
503 void init(const AnalysisHistogramSettings &settings);
505 * Initializes data storage frame when a new frame starts.
507 void initFrame(AnalysisDataStorageFrame *frame);
509 //! Storage implementation object.
510 AnalysisDataStorage storage_;
511 //! Settings for the histogram object.
512 AnalysisHistogramSettings settings_;
514 BasicAverageHistogramModulePointer averager_;
517 BasicHistogramImpl::BasicHistogramImpl()
518 : averager_(new BasicAverageHistogramModule())
523 BasicHistogramImpl::BasicHistogramImpl(const AnalysisHistogramSettings &settings)
524 : settings_(settings), averager_(new BasicAverageHistogramModule(settings))
529 BasicHistogramImpl::~BasicHistogramImpl()
534 void BasicHistogramImpl::init(const AnalysisHistogramSettings &settings)
536 settings_ = settings;
537 averager_->init(settings);
542 BasicHistogramImpl::initFrame(AnalysisDataStorageFrame *frame)
544 for (int i = 0; i < frame->columnCount(); ++i)
546 frame->setValue(i, 0.0);
550 } // namespace internal
553 /********************************************************************
554 * AnalysisDataSimpleHistogramModule
557 AnalysisDataSimpleHistogramModule::AnalysisDataSimpleHistogramModule()
558 : impl_(new internal::BasicHistogramImpl())
563 AnalysisDataSimpleHistogramModule::AnalysisDataSimpleHistogramModule(
564 const AnalysisHistogramSettings &settings)
565 : impl_(new internal::BasicHistogramImpl(settings))
570 AnalysisDataSimpleHistogramModule::~AnalysisDataSimpleHistogramModule()
575 void AnalysisDataSimpleHistogramModule::init(const AnalysisHistogramSettings &settings)
577 impl_->init(settings);
581 AbstractAverageHistogram &
582 AnalysisDataSimpleHistogramModule::averager()
584 return *impl_->averager_;
588 const AnalysisHistogramSettings &
589 AnalysisDataSimpleHistogramModule::settings() const
591 return impl_->settings_;
596 AnalysisDataSimpleHistogramModule::flags() const
598 return efAllowMulticolumn | efAllowMultipoint;
603 AnalysisDataSimpleHistogramModule::dataStarted(AbstractAnalysisData *data)
605 addModule(impl_->averager_);
606 setColumnCount(settings().binCount());
608 impl_->storage_.startDataStorage(this);
613 AnalysisDataSimpleHistogramModule::frameStarted(const AnalysisDataFrameHeader &header)
615 AnalysisDataStorageFrame &frame = impl_->storage_.startFrame(header);
616 impl_->initFrame(&frame);
621 AnalysisDataSimpleHistogramModule::pointsAdded(const AnalysisDataPointSetRef &points)
623 AnalysisDataStorageFrame &frame =
624 impl_->storage_.currentFrame(points.frameIndex());
625 for (int i = 0; i < points.columnCount(); ++i)
627 int bin = settings().findBin(points.y(i));
630 frame.value(bin) += 1;
637 AnalysisDataSimpleHistogramModule::frameFinished(const AnalysisDataFrameHeader &header)
639 impl_->storage_.finishFrame(header.index());
644 AnalysisDataSimpleHistogramModule::dataFinished()
651 AnalysisDataSimpleHistogramModule::tryGetDataFrameInternal(int index) const
653 return impl_->storage_.tryGetDataFrame(index);
658 AnalysisDataSimpleHistogramModule::requestStorageInternal(int nframes)
660 return impl_->storage_.requestStorage(nframes);
664 /********************************************************************
665 * AnalysisDataWeightedHistogramModule
668 AnalysisDataWeightedHistogramModule::AnalysisDataWeightedHistogramModule()
669 : impl_(new internal::BasicHistogramImpl())
674 AnalysisDataWeightedHistogramModule::AnalysisDataWeightedHistogramModule(
675 const AnalysisHistogramSettings &settings)
676 : impl_(new internal::BasicHistogramImpl(settings))
681 AnalysisDataWeightedHistogramModule::~AnalysisDataWeightedHistogramModule()
686 void AnalysisDataWeightedHistogramModule::init(const AnalysisHistogramSettings &settings)
688 impl_->init(settings);
692 AbstractAverageHistogram &
693 AnalysisDataWeightedHistogramModule::averager()
695 return *impl_->averager_;
699 const AnalysisHistogramSettings &
700 AnalysisDataWeightedHistogramModule::settings() const
702 return impl_->settings_;
707 AnalysisDataWeightedHistogramModule::flags() const
709 return efAllowMulticolumn | efAllowMultipoint;
714 AnalysisDataWeightedHistogramModule::dataStarted(AbstractAnalysisData *data)
716 addModule(impl_->averager_);
717 setColumnCount(settings().binCount());
719 impl_->storage_.startDataStorage(this);
724 AnalysisDataWeightedHistogramModule::frameStarted(const AnalysisDataFrameHeader &header)
726 AnalysisDataStorageFrame &frame = impl_->storage_.startFrame(header);
727 impl_->initFrame(&frame);
732 AnalysisDataWeightedHistogramModule::pointsAdded(const AnalysisDataPointSetRef &points)
734 if (points.firstColumn() != 0 || points.columnCount() < 2)
736 GMX_THROW(APIError("Invalid data layout"));
738 int bin = settings().findBin(points.y(0));
741 AnalysisDataStorageFrame &frame =
742 impl_->storage_.currentFrame(points.frameIndex());
743 for (int i = 1; i < points.columnCount(); ++i)
745 frame.value(bin) += points.y(i);
752 AnalysisDataWeightedHistogramModule::frameFinished(const AnalysisDataFrameHeader &header)
754 impl_->storage_.finishFrame(header.index());
759 AnalysisDataWeightedHistogramModule::dataFinished()
766 AnalysisDataWeightedHistogramModule::tryGetDataFrameInternal(int index) const
768 return impl_->storage_.tryGetDataFrame(index);
773 AnalysisDataWeightedHistogramModule::requestStorageInternal(int nframes)
775 return impl_->storage_.requestStorage(nframes);
779 /********************************************************************
780 * AnalysisDataBinAverageModule
783 AnalysisDataBinAverageModule::AnalysisDataBinAverageModule()
789 AnalysisDataBinAverageModule::AnalysisDataBinAverageModule(
790 const AnalysisHistogramSettings &settings)
791 : settings_(settings)
794 setRowCount(settings.binCount());
795 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
796 settings.binWidth());
800 AnalysisDataBinAverageModule::~AnalysisDataBinAverageModule()
806 AnalysisDataBinAverageModule::init(const AnalysisHistogramSettings &settings)
808 settings_ = settings;
809 setRowCount(settings.binCount());
810 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
811 settings.binWidth());
816 AnalysisDataBinAverageModule::flags() const
818 return efAllowMulticolumn | efAllowMultipoint;
823 AnalysisDataBinAverageModule::dataStarted(AbstractAnalysisData * /*data*/)
830 AnalysisDataBinAverageModule::frameStarted(const AnalysisDataFrameHeader & /*header*/)
836 AnalysisDataBinAverageModule::pointsAdded(const AnalysisDataPointSetRef &points)
838 if (points.firstColumn() != 0 || points.columnCount() < 2)
840 GMX_THROW(APIError("Invalid data layout"));
842 int bin = settings().findBin(points.y(0));
845 for (int i = 1; i < points.columnCount(); ++i)
847 real y = points.y(i);
849 value(bin, 1) += y * y;
851 value(bin, 2) += points.columnCount() - 1;
857 AnalysisDataBinAverageModule::frameFinished(const AnalysisDataFrameHeader & /*header*/)
863 AnalysisDataBinAverageModule::dataFinished()
865 for (int i = 0; i < settings().binCount(); ++i)
867 real n = value(i, 2);
870 real ave = value(i, 0) / n;
871 real std = sqrt(value(i, 1) / n - ave * ave);