2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013,2014, 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.
37 * Implements classes in histogram.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_analysisdata
44 #include "histogram.h"
51 #include "gromacs/analysisdata/dataframe.h"
52 #include "gromacs/analysisdata/datastorage.h"
53 #include "gromacs/utility/exceptions.h"
54 #include "gromacs/utility/gmxassert.h"
56 #include "frameaverager.h"
61 //! Value used to signify that a real-valued histogram setting is not set.
62 const real UNDEFINED = std::numeric_limits<real>::max();
63 //! Checks whether \p value is defined.
64 bool isDefined(real value)
66 return value != UNDEFINED;
74 /********************************************************************
75 * AnalysisHistogramSettingsInitializer
78 AnalysisHistogramSettingsInitializer::AnalysisHistogramSettingsInitializer()
79 : min_(UNDEFINED), max_(UNDEFINED), binWidth_(UNDEFINED),
80 binCount_(0), bIntegerBins_(false), bRoundRange_(false),
86 /********************************************************************
87 * AnalysisHistogramSettings
90 AnalysisHistogramSettings::AnalysisHistogramSettings()
91 : firstEdge_(0.0), lastEdge_(0.0), binWidth_(0.0), inverseBinWidth_(0.0),
92 binCount_(0), bAll_(false)
97 AnalysisHistogramSettings::AnalysisHistogramSettings(
98 const AnalysisHistogramSettingsInitializer &settings)
100 GMX_RELEASE_ASSERT(isDefined(settings.min_),
101 "Histogram start value must be defined");
102 GMX_RELEASE_ASSERT(!isDefined(settings.max_) || settings.max_ > settings.min_,
103 "Histogram end value must be larger than start value");
104 GMX_RELEASE_ASSERT(!isDefined(settings.binWidth_) || settings.binWidth_ > 0.0,
105 "Histogram bin width must be positive");
106 GMX_RELEASE_ASSERT(settings.binCount_ >= 0,
107 "Histogram bin count must be positive");
109 if (!isDefined(settings.max_))
111 GMX_RELEASE_ASSERT(isDefined(settings.binWidth_) && settings.binCount_ > 0,
112 "Not all required values provided");
113 GMX_RELEASE_ASSERT(!settings.bRoundRange_,
114 "Rounding only supported for min/max ranges");
116 firstEdge_ = settings.min_;
117 binCount_ = settings.binCount_;
118 binWidth_ = settings.binWidth_;
119 if (settings.bIntegerBins_)
121 firstEdge_ -= 0.5 * binWidth_;
123 lastEdge_ = firstEdge_ + binCount_ * binWidth_;
127 GMX_RELEASE_ASSERT(!(isDefined(settings.binWidth_) && settings.binCount_ > 0),
128 "Conflicting histogram bin specifications");
129 GMX_RELEASE_ASSERT(isDefined(settings.binWidth_) || settings.binCount_ > 0,
130 "Not all required values provided");
132 if (settings.bRoundRange_)
134 GMX_RELEASE_ASSERT(!settings.bIntegerBins_,
135 "Rounding and integer bins cannot be combined");
136 GMX_RELEASE_ASSERT(isDefined(settings.binWidth_),
137 "Rounding only makes sense with defined binwidth");
138 binWidth_ = settings.binWidth_;
139 firstEdge_ = binWidth_ * floor(settings.min_ / binWidth_);
140 lastEdge_ = binWidth_ * ceil(settings.max_ / binWidth_);
141 binCount_ = static_cast<int>((lastEdge_ - firstEdge_) / binWidth_ + 0.5);
145 firstEdge_ = settings.min_;
146 lastEdge_ = settings.max_;
147 if (settings.binCount_ > 0)
149 binCount_ = settings.binCount_;
150 if (settings.bIntegerBins_)
152 GMX_RELEASE_ASSERT(settings.binCount_ > 1,
153 "Bin count must be at least two with integer bins");
154 binWidth_ = (lastEdge_ - firstEdge_) / (binCount_ - 1);
155 firstEdge_ -= 0.5 * binWidth_;
156 lastEdge_ += 0.5 * binWidth_;
160 binWidth_ = (lastEdge_ - firstEdge_) / binCount_;
165 binWidth_ = settings.binWidth_;
166 binCount_ = static_cast<int>((lastEdge_ - firstEdge_) / binWidth_ + 0.5);
167 if (settings.bIntegerBins_)
169 firstEdge_ -= 0.5 * binWidth_;
172 lastEdge_ = firstEdge_ + binCount_ * binWidth_;
177 inverseBinWidth_ = 1.0 / binWidth_;
178 bAll_ = settings.bIncludeAll_;
183 AnalysisHistogramSettings::findBin(real y) const
187 return bAll_ ? 0 : -1;
189 int bin = static_cast<int>((y - firstEdge_) * inverseBinWidth_);
190 if (bin >= binCount_)
192 return bAll_ ? binCount_ - 1 : -1;
198 /********************************************************************
199 * StaticAverageHistogram
206 * Represents copies of average histograms.
208 * Methods in AbstractAverageHistogram that return new histogram instances
209 * return objects of this class.
210 * Initialization of values is handled in those methods.
212 * \ingroup module_analysisdata
214 class StaticAverageHistogram : public AbstractAverageHistogram
217 StaticAverageHistogram();
218 //! Creates an average histogram module with defined bin parameters.
219 explicit StaticAverageHistogram(const AnalysisHistogramSettings &settings);
221 // Copy and assign disallowed by base.
224 StaticAverageHistogram::StaticAverageHistogram()
229 StaticAverageHistogram::StaticAverageHistogram(
230 const AnalysisHistogramSettings &settings)
231 : AbstractAverageHistogram(settings)
238 /********************************************************************
239 * AbstractAverageHistogram
242 AbstractAverageHistogram::AbstractAverageHistogram()
247 AbstractAverageHistogram::AbstractAverageHistogram(
248 const AnalysisHistogramSettings &settings)
249 : settings_(settings)
251 setRowCount(settings.binCount());
252 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
253 settings.binWidth());
257 AbstractAverageHistogram::~AbstractAverageHistogram()
263 AbstractAverageHistogram::init(const AnalysisHistogramSettings &settings)
265 settings_ = settings;
266 setRowCount(settings.binCount());
267 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
268 settings.binWidth());
272 AverageHistogramPointer
273 AbstractAverageHistogram::resampleDoubleBinWidth(bool bIntegerBins) const
278 nbins = (rowCount() + 1) / 2;
282 nbins = rowCount() / 2;
285 AverageHistogramPointer dest(
286 new StaticAverageHistogram(
287 histogramFromBins(xstart(), nbins, 2*xstep())
288 .integerBins(bIntegerBins)));
289 dest->setColumnCount(columnCount());
290 dest->allocateValues();
293 for (i = j = 0; i < nbins; ++i)
295 const bool bFirstHalfBin = (bIntegerBins && i == 0);
296 for (int c = 0; c < columnCount(); ++c)
302 v1 = value(0, c).value();
303 e1 = value(0, c).error();
309 v1 = value(j, c).value();
310 e1 = value(j, c).error();
311 v2 = value(j + 1, c).value();
312 e2 = value(j + 1, c).error();
314 dest->value(i, c).setValue(v1 + v2, std::sqrt(e1 * e1 + e2 * e2));
329 AverageHistogramPointer
330 AbstractAverageHistogram::clone() const
332 AverageHistogramPointer dest(new StaticAverageHistogram());
333 copyContents(this, dest.get());
339 AbstractAverageHistogram::normalizeProbability()
341 for (int c = 0; c < columnCount(); ++c)
344 for (int i = 0; i < rowCount(); ++i)
346 sum += value(i, c).value();
350 scaleSingle(c, 1.0 / (sum * xstep()));
357 AbstractAverageHistogram::scaleSingle(int index, real factor)
359 for (int i = 0; i < rowCount(); ++i)
361 value(i, index).value() *= factor;
362 value(i, index).error() *= factor;
368 AbstractAverageHistogram::scaleAll(real factor)
370 for (int i = 0; i < columnCount(); ++i)
372 scaleSingle(i, factor);
378 AbstractAverageHistogram::scaleAllByVector(real factor[])
380 for (int c = 0; c < columnCount(); ++c)
382 for (int i = 0; i < rowCount(); ++i)
384 value(i, c).value() *= factor[i];
385 value(i, c).error() *= factor[i];
391 /********************************************************************
392 * BasicAverageHistogramModule
400 * Implements average histogram module that averages per-frame histograms.
402 * This class is used for accumulating average histograms in per-frame
403 * histogram modules (those that use BasicHistogramImpl as their implementation
405 * There are two columns, first for the average and second for standard
408 * \ingroup module_analysisdata
410 class BasicAverageHistogramModule : public AbstractAverageHistogram,
411 public AnalysisDataModuleSerial
414 BasicAverageHistogramModule();
415 //! Creates an average histogram module with defined bin parameters.
416 explicit BasicAverageHistogramModule(const AnalysisHistogramSettings &settings);
418 using AbstractAverageHistogram::init;
420 virtual int flags() const;
422 virtual void dataStarted(AbstractAnalysisData *data);
423 virtual void frameStarted(const AnalysisDataFrameHeader &header);
424 virtual void pointsAdded(const AnalysisDataPointSetRef &points);
425 virtual void frameFinished(const AnalysisDataFrameHeader &header);
426 virtual void dataFinished();
429 //! Averaging helper objects for each input data set.
430 std::vector<AnalysisDataFrameAverager> averagers_;
432 // Copy and assign disallowed by base.
435 BasicAverageHistogramModule::BasicAverageHistogramModule()
440 BasicAverageHistogramModule::BasicAverageHistogramModule(
441 const AnalysisHistogramSettings &settings)
442 : AbstractAverageHistogram(settings)
448 BasicAverageHistogramModule::flags() const
450 return efAllowMulticolumn | efAllowMultipleDataSets;
455 BasicAverageHistogramModule::dataStarted(AbstractAnalysisData *data)
457 setColumnCount(data->dataSetCount());
458 averagers_.resize(data->dataSetCount());
459 for (int i = 0; i < data->dataSetCount(); ++i)
461 GMX_RELEASE_ASSERT(rowCount() == data->columnCount(i),
462 "Inconsistent data sizes, something is wrong in the initialization");
463 averagers_[i].setColumnCount(data->columnCount(i));
469 BasicAverageHistogramModule::frameStarted(const AnalysisDataFrameHeader & /*header*/)
475 BasicAverageHistogramModule::pointsAdded(const AnalysisDataPointSetRef &points)
477 averagers_[points.dataSetIndex()].addPoints(points);
482 BasicAverageHistogramModule::frameFinished(const AnalysisDataFrameHeader & /*header*/)
488 BasicAverageHistogramModule::dataFinished()
491 for (int i = 0; i < columnCount(); ++i)
493 averagers_[i].finish();
494 for (int j = 0; j < rowCount(); ++j)
496 value(j, i).setValue(averagers_[i].average(j),
497 std::sqrt(averagers_[i].variance(j)));
503 /********************************************************************
508 * Private implementation class for AnalysisDataSimpleHistogramModule and
509 * AnalysisDataWeightedHistogramModule.
511 * \ingroup module_analysisdata
513 class BasicHistogramImpl
516 //! Smart pointer to manage an BasicAverageHistogramModule object.
517 typedef boost::shared_ptr<BasicAverageHistogramModule>
518 BasicAverageHistogramModulePointer;
520 BasicHistogramImpl();
521 //! Creates an histogram impl with defined bin parameters.
522 explicit BasicHistogramImpl(const AnalysisHistogramSettings &settings);
523 ~BasicHistogramImpl();
526 * (Re)initializes the histogram from settings.
528 void init(const AnalysisHistogramSettings &settings);
530 * Initializes data storage frame when a new frame starts.
532 void initFrame(int dataSetCount, AnalysisDataStorageFrame *frame);
534 //! Storage implementation object.
535 AnalysisDataStorage storage_;
536 //! Settings for the histogram object.
537 AnalysisHistogramSettings settings_;
539 BasicAverageHistogramModulePointer averager_;
542 BasicHistogramImpl::BasicHistogramImpl()
543 : averager_(new BasicAverageHistogramModule())
548 BasicHistogramImpl::BasicHistogramImpl(const AnalysisHistogramSettings &settings)
549 : settings_(settings), averager_(new BasicAverageHistogramModule(settings))
554 BasicHistogramImpl::~BasicHistogramImpl()
559 void BasicHistogramImpl::init(const AnalysisHistogramSettings &settings)
561 settings_ = settings;
562 averager_->init(settings);
567 BasicHistogramImpl::initFrame(int dataSetCount, AnalysisDataStorageFrame *frame)
569 for (int s = 0; s < dataSetCount; ++s)
571 frame->selectDataSet(s);
572 for (int i = 0; i < frame->columnCount(); ++i)
574 frame->setValue(i, 0.0);
577 frame->selectDataSet(0);
580 } // namespace internal
583 /********************************************************************
584 * AnalysisDataSimpleHistogramModule
587 AnalysisDataSimpleHistogramModule::AnalysisDataSimpleHistogramModule()
588 : impl_(new internal::BasicHistogramImpl())
593 AnalysisDataSimpleHistogramModule::AnalysisDataSimpleHistogramModule(
594 const AnalysisHistogramSettings &settings)
595 : impl_(new internal::BasicHistogramImpl(settings))
600 AnalysisDataSimpleHistogramModule::~AnalysisDataSimpleHistogramModule()
605 void AnalysisDataSimpleHistogramModule::init(const AnalysisHistogramSettings &settings)
607 impl_->init(settings);
611 AbstractAverageHistogram &
612 AnalysisDataSimpleHistogramModule::averager()
614 return *impl_->averager_;
618 const AnalysisHistogramSettings &
619 AnalysisDataSimpleHistogramModule::settings() const
621 return impl_->settings_;
626 AnalysisDataSimpleHistogramModule::frameCount() const
628 return impl_->storage_.frameCount();
633 AnalysisDataSimpleHistogramModule::flags() const
635 return efAllowMulticolumn | efAllowMultipoint | efAllowMissing
636 | efAllowMultipleDataSets;
641 AnalysisDataSimpleHistogramModule::parallelDataStarted(
642 AbstractAnalysisData *data,
643 const AnalysisDataParallelOptions &options)
645 addModule(impl_->averager_);
646 setDataSetCount(data->dataSetCount());
647 for (int i = 0; i < data->dataSetCount(); ++i)
649 setColumnCount(i, settings().binCount());
651 impl_->storage_.startParallelDataStorage(this, &moduleManager(), options);
657 AnalysisDataSimpleHistogramModule::frameStarted(const AnalysisDataFrameHeader &header)
659 AnalysisDataStorageFrame &frame = impl_->storage_.startFrame(header);
660 impl_->initFrame(dataSetCount(), &frame);
665 AnalysisDataSimpleHistogramModule::pointsAdded(const AnalysisDataPointSetRef &points)
667 AnalysisDataStorageFrame &frame =
668 impl_->storage_.currentFrame(points.frameIndex());
669 frame.selectDataSet(points.dataSetIndex());
670 for (int i = 0; i < points.columnCount(); ++i)
672 if (points.present(i))
674 const int bin = settings().findBin(points.y(i));
677 frame.value(bin) += 1;
685 AnalysisDataSimpleHistogramModule::frameFinished(const AnalysisDataFrameHeader &header)
687 impl_->storage_.finishFrame(header.index());
692 AnalysisDataSimpleHistogramModule::dataFinished()
694 impl_->storage_.finishDataStorage();
699 AnalysisDataSimpleHistogramModule::tryGetDataFrameInternal(int index) const
701 return impl_->storage_.tryGetDataFrame(index);
706 AnalysisDataSimpleHistogramModule::requestStorageInternal(int nframes)
708 return impl_->storage_.requestStorage(nframes);
712 /********************************************************************
713 * AnalysisDataWeightedHistogramModule
716 AnalysisDataWeightedHistogramModule::AnalysisDataWeightedHistogramModule()
717 : impl_(new internal::BasicHistogramImpl())
722 AnalysisDataWeightedHistogramModule::AnalysisDataWeightedHistogramModule(
723 const AnalysisHistogramSettings &settings)
724 : impl_(new internal::BasicHistogramImpl(settings))
729 AnalysisDataWeightedHistogramModule::~AnalysisDataWeightedHistogramModule()
734 void AnalysisDataWeightedHistogramModule::init(const AnalysisHistogramSettings &settings)
736 impl_->init(settings);
740 AbstractAverageHistogram &
741 AnalysisDataWeightedHistogramModule::averager()
743 return *impl_->averager_;
747 const AnalysisHistogramSettings &
748 AnalysisDataWeightedHistogramModule::settings() const
750 return impl_->settings_;
755 AnalysisDataWeightedHistogramModule::frameCount() const
757 return impl_->storage_.frameCount();
762 AnalysisDataWeightedHistogramModule::flags() const
764 return efAllowMulticolumn | efAllowMultipoint | efAllowMultipleDataSets;
769 AnalysisDataWeightedHistogramModule::parallelDataStarted(
770 AbstractAnalysisData *data,
771 const AnalysisDataParallelOptions &options)
773 addModule(impl_->averager_);
774 setDataSetCount(data->dataSetCount());
775 for (int i = 0; i < data->dataSetCount(); ++i)
777 setColumnCount(i, settings().binCount());
779 impl_->storage_.startParallelDataStorage(this, &moduleManager(), options);
785 AnalysisDataWeightedHistogramModule::frameStarted(const AnalysisDataFrameHeader &header)
787 AnalysisDataStorageFrame &frame = impl_->storage_.startFrame(header);
788 impl_->initFrame(dataSetCount(), &frame);
793 AnalysisDataWeightedHistogramModule::pointsAdded(const AnalysisDataPointSetRef &points)
795 if (points.firstColumn() != 0 || points.columnCount() < 2)
797 GMX_THROW(APIError("Invalid data layout"));
799 int bin = settings().findBin(points.y(0));
802 AnalysisDataStorageFrame &frame =
803 impl_->storage_.currentFrame(points.frameIndex());
804 frame.selectDataSet(points.dataSetIndex());
805 for (int i = 1; i < points.columnCount(); ++i)
807 frame.value(bin) += points.y(i);
814 AnalysisDataWeightedHistogramModule::frameFinished(const AnalysisDataFrameHeader &header)
816 impl_->storage_.finishFrame(header.index());
821 AnalysisDataWeightedHistogramModule::dataFinished()
823 impl_->storage_.finishDataStorage();
828 AnalysisDataWeightedHistogramModule::tryGetDataFrameInternal(int index) const
830 return impl_->storage_.tryGetDataFrame(index);
835 AnalysisDataWeightedHistogramModule::requestStorageInternal(int nframes)
837 return impl_->storage_.requestStorage(nframes);
841 /********************************************************************
842 * AnalysisDataBinAverageModule
845 class AnalysisDataBinAverageModule::Impl
849 explicit Impl(const AnalysisHistogramSettings &settings)
850 : settings_(settings)
854 //! Histogram settings.
855 AnalysisHistogramSettings settings_;
856 //! Averaging helper objects for each input data set.
857 std::vector<AnalysisDataFrameAverager> averagers_;
860 AnalysisDataBinAverageModule::AnalysisDataBinAverageModule()
867 AnalysisDataBinAverageModule::AnalysisDataBinAverageModule(
868 const AnalysisHistogramSettings &settings)
869 : impl_(new Impl(settings))
871 setRowCount(settings.binCount());
872 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
873 settings.binWidth());
877 AnalysisDataBinAverageModule::~AnalysisDataBinAverageModule()
883 AnalysisDataBinAverageModule::init(const AnalysisHistogramSettings &settings)
885 impl_->settings_ = settings;
886 setRowCount(settings.binCount());
887 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
888 settings.binWidth());
892 const AnalysisHistogramSettings &
893 AnalysisDataBinAverageModule::settings() const
895 return impl_->settings_;
900 AnalysisDataBinAverageModule::flags() const
902 return efAllowMulticolumn | efAllowMultipoint | efAllowMultipleDataSets;
907 AnalysisDataBinAverageModule::dataStarted(AbstractAnalysisData *data)
909 setColumnCount(data->dataSetCount());
910 impl_->averagers_.resize(data->dataSetCount());
911 for (int i = 0; i < data->dataSetCount(); ++i)
913 impl_->averagers_[i].setColumnCount(rowCount());
919 AnalysisDataBinAverageModule::frameStarted(const AnalysisDataFrameHeader & /*header*/)
925 AnalysisDataBinAverageModule::pointsAdded(const AnalysisDataPointSetRef &points)
927 if (points.firstColumn() != 0 || points.columnCount() < 2)
929 GMX_THROW(APIError("Invalid data layout"));
931 int bin = settings().findBin(points.y(0));
934 AnalysisDataFrameAverager &averager = impl_->averagers_[points.dataSetIndex()];
935 for (int i = 1; i < points.columnCount(); ++i)
937 averager.addValue(bin, points.y(i));
944 AnalysisDataBinAverageModule::frameFinished(const AnalysisDataFrameHeader & /*header*/)
950 AnalysisDataBinAverageModule::dataFinished()
953 for (int i = 0; i < columnCount(); ++i)
955 AnalysisDataFrameAverager &averager = impl_->averagers_[i];
957 for (int j = 0; j < rowCount(); ++j)
959 value(j, i).setValue(averager.average(j),
960 std::sqrt(averager.variance(j)));