2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
5 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6 * others, as listed in the AUTHORS file in the top-level source
7 * 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
42 #include "gromacs/analysisdata/modules/histogram.h"
49 #include "gromacs/analysisdata/dataframe.h"
50 #include "gromacs/analysisdata/datastorage.h"
51 #include "gromacs/utility/exceptions.h"
52 #include "gromacs/utility/gmxassert.h"
54 #include "frameaverager.h"
59 //! Value used to signify that a real-valued histogram setting is not set.
60 const real UNDEFINED = std::numeric_limits<real>::max();
61 //! Checks whether \p value is defined.
62 bool isDefined(real value)
64 return value != UNDEFINED;
72 /********************************************************************
73 * AnalysisHistogramSettingsInitializer
76 AnalysisHistogramSettingsInitializer::AnalysisHistogramSettingsInitializer()
77 : min_(UNDEFINED), max_(UNDEFINED), binWidth_(UNDEFINED),
78 binCount_(0), bIntegerBins_(false), bRoundRange_(false),
84 /********************************************************************
85 * AnalysisHistogramSettings
88 AnalysisHistogramSettings::AnalysisHistogramSettings()
89 : firstEdge_(0.0), lastEdge_(0.0), binWidth_(0.0), inverseBinWidth_(0.0),
90 binCount_(0), bAll_(false)
95 AnalysisHistogramSettings::AnalysisHistogramSettings(
96 const AnalysisHistogramSettingsInitializer &settings)
98 GMX_RELEASE_ASSERT(isDefined(settings.min_),
99 "Histogram start value must be defined");
100 GMX_RELEASE_ASSERT(!isDefined(settings.max_) || settings.max_ > settings.min_,
101 "Histogram end value must be larger than start value");
102 GMX_RELEASE_ASSERT(!isDefined(settings.binWidth_) || settings.binWidth_ > 0.0,
103 "Histogram bin width must be positive");
104 GMX_RELEASE_ASSERT(settings.binCount_ >= 0,
105 "Histogram bin count must be positive");
107 if (!isDefined(settings.max_))
109 GMX_RELEASE_ASSERT(isDefined(settings.binWidth_) && settings.binCount_ > 0,
110 "Not all required values provided");
111 GMX_RELEASE_ASSERT(!settings.bRoundRange_,
112 "Rounding only supported for min/max ranges");
114 firstEdge_ = settings.min_;
115 binCount_ = settings.binCount_;
116 binWidth_ = settings.binWidth_;
117 if (settings.bIntegerBins_)
119 firstEdge_ -= 0.5 * binWidth_;
121 lastEdge_ = firstEdge_ + binCount_ * binWidth_;
125 GMX_RELEASE_ASSERT(!(isDefined(settings.binWidth_) && settings.binCount_ > 0),
126 "Conflicting histogram bin specifications");
127 GMX_RELEASE_ASSERT(isDefined(settings.binWidth_) || settings.binCount_ > 0,
128 "Not all required values provided");
130 if (settings.bRoundRange_)
132 GMX_RELEASE_ASSERT(!settings.bIntegerBins_,
133 "Rounding and integer bins cannot be combined");
134 GMX_RELEASE_ASSERT(isDefined(settings.binWidth_),
135 "Rounding only makes sense with defined binwidth");
136 binWidth_ = settings.binWidth_;
137 firstEdge_ = binWidth_ * floor(settings.min_ / binWidth_);
138 lastEdge_ = binWidth_ * ceil(settings.max_ / binWidth_);
139 binCount_ = static_cast<int>((lastEdge_ - firstEdge_) / binWidth_ + 0.5);
143 firstEdge_ = settings.min_;
144 lastEdge_ = settings.max_;
145 if (settings.binCount_ > 0)
147 binCount_ = settings.binCount_;
148 if (settings.bIntegerBins_)
150 GMX_RELEASE_ASSERT(settings.binCount_ > 1,
151 "Bin count must be at least two with integer bins");
152 binWidth_ = (lastEdge_ - firstEdge_) / (binCount_ - 1);
153 firstEdge_ -= 0.5 * binWidth_;
154 lastEdge_ += 0.5 * binWidth_;
158 binWidth_ = (lastEdge_ - firstEdge_) / binCount_;
163 binWidth_ = settings.binWidth_;
164 binCount_ = static_cast<int>((lastEdge_ - firstEdge_) / binWidth_ + 0.5);
165 if (settings.bIntegerBins_)
167 firstEdge_ -= 0.5 * binWidth_;
170 lastEdge_ = firstEdge_ + binCount_ * binWidth_;
175 inverseBinWidth_ = 1.0 / binWidth_;
176 bAll_ = settings.bIncludeAll_;
181 AnalysisHistogramSettings::findBin(real y) const
185 return bAll_ ? 0 : -1;
187 int bin = static_cast<int>((y - firstEdge_) * inverseBinWidth_);
188 if (bin >= binCount_)
190 return bAll_ ? binCount_ - 1 : -1;
196 /********************************************************************
197 * StaticAverageHistogram
204 * Represents copies of average histograms.
206 * Methods in AbstractAverageHistogram that return new histogram instances
207 * return objects of this class.
208 * Initialization of values is handled in those methods.
210 * \ingroup module_analysisdata
212 class StaticAverageHistogram : public AbstractAverageHistogram
215 StaticAverageHistogram();
216 //! Creates an average histogram module with defined bin parameters.
217 explicit StaticAverageHistogram(const AnalysisHistogramSettings &settings);
219 // Copy and assign disallowed by base.
222 StaticAverageHistogram::StaticAverageHistogram()
227 StaticAverageHistogram::StaticAverageHistogram(
228 const AnalysisHistogramSettings &settings)
229 : AbstractAverageHistogram(settings)
236 /********************************************************************
237 * AbstractAverageHistogram
240 AbstractAverageHistogram::AbstractAverageHistogram()
245 AbstractAverageHistogram::AbstractAverageHistogram(
246 const AnalysisHistogramSettings &settings)
247 : settings_(settings)
249 setRowCount(settings.binCount());
250 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
251 settings.binWidth());
255 AbstractAverageHistogram::~AbstractAverageHistogram()
261 AbstractAverageHistogram::init(const AnalysisHistogramSettings &settings)
263 settings_ = settings;
264 setRowCount(settings.binCount());
265 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
266 settings.binWidth());
270 AverageHistogramPointer
271 AbstractAverageHistogram::resampleDoubleBinWidth(bool bIntegerBins) const
276 nbins = (rowCount() + 1) / 2;
280 nbins = rowCount() / 2;
283 AverageHistogramPointer dest(
284 new StaticAverageHistogram(
285 histogramFromBins(xstart(), nbins, 2*xstep())
286 .integerBins(bIntegerBins)));
287 dest->setColumnCount(columnCount());
288 dest->allocateValues();
291 for (i = j = 0; i < nbins; ++i)
293 const bool bFirstHalfBin = (bIntegerBins && i == 0);
294 for (int c = 0; c < columnCount(); ++c)
300 v1 = value(0, c).value();
301 e1 = value(0, c).error();
307 v1 = value(j, c).value();
308 e1 = value(j, c).error();
309 v2 = value(j + 1, c).value();
310 e2 = value(j + 1, c).error();
312 dest->value(i, c).setValue(v1 + v2, std::sqrt(e1 * e1 + e2 * e2));
327 AverageHistogramPointer
328 AbstractAverageHistogram::clone() const
330 AverageHistogramPointer dest(new StaticAverageHistogram());
331 copyContents(this, dest.get());
337 AbstractAverageHistogram::normalizeProbability()
339 for (int c = 0; c < columnCount(); ++c)
342 for (int i = 0; i < rowCount(); ++i)
344 sum += value(i, c).value();
348 scaleSingle(c, 1.0 / (sum * xstep()));
355 AbstractAverageHistogram::scaleSingle(int index, real factor)
357 for (int i = 0; i < rowCount(); ++i)
359 value(i, index).value() *= factor;
360 value(i, index).error() *= factor;
366 AbstractAverageHistogram::scaleAll(real factor)
368 for (int i = 0; i < columnCount(); ++i)
370 scaleSingle(i, factor);
376 AbstractAverageHistogram::scaleAllByVector(real factor[])
378 for (int c = 0; c < columnCount(); ++c)
380 for (int i = 0; i < rowCount(); ++i)
382 value(i, c).value() *= factor[i];
383 value(i, c).error() *= factor[i];
389 /********************************************************************
390 * BasicAverageHistogramModule
397 * Implements average histogram module that averages per-frame histograms.
399 * This class is used for accumulating average histograms in per-frame
400 * histogram modules (those that use BasicHistogramImpl as their implementation
402 * There are two columns, first for the average and second for standard
405 * \ingroup module_analysisdata
407 class BasicAverageHistogramModule : public AbstractAverageHistogram,
408 public AnalysisDataModuleSerial
411 BasicAverageHistogramModule();
412 //! Creates an average histogram module with defined bin parameters.
413 explicit BasicAverageHistogramModule(const AnalysisHistogramSettings &settings);
415 using AbstractAverageHistogram::init;
417 virtual int flags() const;
419 virtual void dataStarted(AbstractAnalysisData *data);
420 virtual void frameStarted(const AnalysisDataFrameHeader &header);
421 virtual void pointsAdded(const AnalysisDataPointSetRef &points);
422 virtual void frameFinished(const AnalysisDataFrameHeader &header);
423 virtual void dataFinished();
426 //! Averaging helper objects for each input data set.
427 std::vector<AnalysisDataFrameAverager> averagers_;
429 // Copy and assign disallowed by base.
432 BasicAverageHistogramModule::BasicAverageHistogramModule()
437 BasicAverageHistogramModule::BasicAverageHistogramModule(
438 const AnalysisHistogramSettings &settings)
439 : AbstractAverageHistogram(settings)
445 BasicAverageHistogramModule::flags() const
447 return efAllowMulticolumn | efAllowMultipleDataSets;
452 BasicAverageHistogramModule::dataStarted(AbstractAnalysisData *data)
454 setColumnCount(data->dataSetCount());
455 averagers_.resize(data->dataSetCount());
456 for (int i = 0; i < data->dataSetCount(); ++i)
458 GMX_RELEASE_ASSERT(rowCount() == data->columnCount(i),
459 "Inconsistent data sizes, something is wrong in the initialization");
460 averagers_[i].setColumnCount(data->columnCount(i));
466 BasicAverageHistogramModule::frameStarted(const AnalysisDataFrameHeader & /*header*/)
472 BasicAverageHistogramModule::pointsAdded(const AnalysisDataPointSetRef &points)
474 averagers_[points.dataSetIndex()].addPoints(points);
479 BasicAverageHistogramModule::frameFinished(const AnalysisDataFrameHeader & /*header*/)
485 BasicAverageHistogramModule::dataFinished()
488 for (int i = 0; i < columnCount(); ++i)
490 averagers_[i].finish();
491 for (int j = 0; j < rowCount(); ++j)
493 value(j, i).setValue(averagers_[i].average(j),
494 std::sqrt(averagers_[i].variance(j)));
500 /********************************************************************
505 * Private implementation class for AnalysisDataSimpleHistogramModule and
506 * AnalysisDataWeightedHistogramModule.
508 * \ingroup module_analysisdata
510 class BasicHistogramImpl
513 //! Smart pointer to manage an BasicAverageHistogramModule object.
514 typedef boost::shared_ptr<BasicAverageHistogramModule>
515 BasicAverageHistogramModulePointer;
517 BasicHistogramImpl();
518 //! Creates an histogram impl with defined bin parameters.
519 explicit BasicHistogramImpl(const AnalysisHistogramSettings &settings);
520 ~BasicHistogramImpl();
523 * (Re)initializes the histogram from settings.
525 void init(const AnalysisHistogramSettings &settings);
527 * Initializes data storage frame when a new frame starts.
529 void initFrame(int dataSetCount, AnalysisDataStorageFrame *frame);
531 //! Storage implementation object.
532 AnalysisDataStorage storage_;
533 //! Settings for the histogram object.
534 AnalysisHistogramSettings settings_;
536 BasicAverageHistogramModulePointer averager_;
539 BasicHistogramImpl::BasicHistogramImpl()
540 : averager_(new BasicAverageHistogramModule())
545 BasicHistogramImpl::BasicHistogramImpl(const AnalysisHistogramSettings &settings)
546 : settings_(settings), averager_(new BasicAverageHistogramModule(settings))
551 BasicHistogramImpl::~BasicHistogramImpl()
556 void BasicHistogramImpl::init(const AnalysisHistogramSettings &settings)
558 settings_ = settings;
559 averager_->init(settings);
564 BasicHistogramImpl::initFrame(int dataSetCount, AnalysisDataStorageFrame *frame)
566 for (int s = 0; s < dataSetCount; ++s)
568 frame->selectDataSet(s);
569 for (int i = 0; i < frame->columnCount(); ++i)
571 frame->setValue(i, 0.0);
574 frame->selectDataSet(0);
577 } // namespace internal
580 /********************************************************************
581 * AnalysisDataSimpleHistogramModule
584 AnalysisDataSimpleHistogramModule::AnalysisDataSimpleHistogramModule()
585 : impl_(new internal::BasicHistogramImpl())
590 AnalysisDataSimpleHistogramModule::AnalysisDataSimpleHistogramModule(
591 const AnalysisHistogramSettings &settings)
592 : impl_(new internal::BasicHistogramImpl(settings))
597 AnalysisDataSimpleHistogramModule::~AnalysisDataSimpleHistogramModule()
602 void AnalysisDataSimpleHistogramModule::init(const AnalysisHistogramSettings &settings)
604 impl_->init(settings);
608 AbstractAverageHistogram &
609 AnalysisDataSimpleHistogramModule::averager()
611 return *impl_->averager_;
615 const AnalysisHistogramSettings &
616 AnalysisDataSimpleHistogramModule::settings() const
618 return impl_->settings_;
623 AnalysisDataSimpleHistogramModule::frameCount() const
625 return impl_->storage_.frameCount();
630 AnalysisDataSimpleHistogramModule::flags() const
632 return efAllowMulticolumn | efAllowMultipoint | efAllowMissing
633 | efAllowMultipleDataSets;
638 AnalysisDataSimpleHistogramModule::parallelDataStarted(
639 AbstractAnalysisData *data,
640 const AnalysisDataParallelOptions &options)
642 addModule(impl_->averager_);
643 setDataSetCount(data->dataSetCount());
644 for (int i = 0; i < data->dataSetCount(); ++i)
646 setColumnCount(i, settings().binCount());
648 impl_->storage_.startParallelDataStorage(this, &moduleManager(), options);
654 AnalysisDataSimpleHistogramModule::frameStarted(const AnalysisDataFrameHeader &header)
656 AnalysisDataStorageFrame &frame = impl_->storage_.startFrame(header);
657 impl_->initFrame(dataSetCount(), &frame);
662 AnalysisDataSimpleHistogramModule::pointsAdded(const AnalysisDataPointSetRef &points)
664 AnalysisDataStorageFrame &frame =
665 impl_->storage_.currentFrame(points.frameIndex());
666 frame.selectDataSet(points.dataSetIndex());
667 for (int i = 0; i < points.columnCount(); ++i)
669 if (points.present(i))
671 const int bin = settings().findBin(points.y(i));
674 frame.value(bin) += 1;
682 AnalysisDataSimpleHistogramModule::frameFinished(const AnalysisDataFrameHeader &header)
684 impl_->storage_.finishFrame(header.index());
689 AnalysisDataSimpleHistogramModule::dataFinished()
691 impl_->storage_.finishDataStorage();
696 AnalysisDataSimpleHistogramModule::tryGetDataFrameInternal(int index) const
698 return impl_->storage_.tryGetDataFrame(index);
703 AnalysisDataSimpleHistogramModule::requestStorageInternal(int nframes)
705 return impl_->storage_.requestStorage(nframes);
709 /********************************************************************
710 * AnalysisDataWeightedHistogramModule
713 AnalysisDataWeightedHistogramModule::AnalysisDataWeightedHistogramModule()
714 : impl_(new internal::BasicHistogramImpl())
719 AnalysisDataWeightedHistogramModule::AnalysisDataWeightedHistogramModule(
720 const AnalysisHistogramSettings &settings)
721 : impl_(new internal::BasicHistogramImpl(settings))
726 AnalysisDataWeightedHistogramModule::~AnalysisDataWeightedHistogramModule()
731 void AnalysisDataWeightedHistogramModule::init(const AnalysisHistogramSettings &settings)
733 impl_->init(settings);
737 AbstractAverageHistogram &
738 AnalysisDataWeightedHistogramModule::averager()
740 return *impl_->averager_;
744 const AnalysisHistogramSettings &
745 AnalysisDataWeightedHistogramModule::settings() const
747 return impl_->settings_;
752 AnalysisDataWeightedHistogramModule::frameCount() const
754 return impl_->storage_.frameCount();
759 AnalysisDataWeightedHistogramModule::flags() const
761 return efAllowMulticolumn | efAllowMultipoint | efAllowMultipleDataSets;
766 AnalysisDataWeightedHistogramModule::parallelDataStarted(
767 AbstractAnalysisData *data,
768 const AnalysisDataParallelOptions &options)
770 addModule(impl_->averager_);
771 setDataSetCount(data->dataSetCount());
772 for (int i = 0; i < data->dataSetCount(); ++i)
774 setColumnCount(i, settings().binCount());
776 impl_->storage_.startParallelDataStorage(this, &moduleManager(), options);
782 AnalysisDataWeightedHistogramModule::frameStarted(const AnalysisDataFrameHeader &header)
784 AnalysisDataStorageFrame &frame = impl_->storage_.startFrame(header);
785 impl_->initFrame(dataSetCount(), &frame);
790 AnalysisDataWeightedHistogramModule::pointsAdded(const AnalysisDataPointSetRef &points)
792 if (points.firstColumn() != 0 || points.columnCount() < 2)
794 GMX_THROW(APIError("Invalid data layout"));
796 int bin = settings().findBin(points.y(0));
799 AnalysisDataStorageFrame &frame =
800 impl_->storage_.currentFrame(points.frameIndex());
801 frame.selectDataSet(points.dataSetIndex());
802 for (int i = 1; i < points.columnCount(); ++i)
804 frame.value(bin) += points.y(i);
811 AnalysisDataWeightedHistogramModule::frameFinished(const AnalysisDataFrameHeader &header)
813 impl_->storage_.finishFrame(header.index());
818 AnalysisDataWeightedHistogramModule::dataFinished()
820 impl_->storage_.finishDataStorage();
825 AnalysisDataWeightedHistogramModule::tryGetDataFrameInternal(int index) const
827 return impl_->storage_.tryGetDataFrame(index);
832 AnalysisDataWeightedHistogramModule::requestStorageInternal(int nframes)
834 return impl_->storage_.requestStorage(nframes);
838 /********************************************************************
839 * AnalysisDataBinAverageModule
842 class AnalysisDataBinAverageModule::Impl
846 explicit Impl(const AnalysisHistogramSettings &settings)
847 : settings_(settings)
851 //! Histogram settings.
852 AnalysisHistogramSettings settings_;
853 //! Averaging helper objects for each input data set.
854 std::vector<AnalysisDataFrameAverager> averagers_;
857 AnalysisDataBinAverageModule::AnalysisDataBinAverageModule()
864 AnalysisDataBinAverageModule::AnalysisDataBinAverageModule(
865 const AnalysisHistogramSettings &settings)
866 : impl_(new Impl(settings))
868 setRowCount(settings.binCount());
869 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
870 settings.binWidth());
874 AnalysisDataBinAverageModule::~AnalysisDataBinAverageModule()
880 AnalysisDataBinAverageModule::init(const AnalysisHistogramSettings &settings)
882 impl_->settings_ = settings;
883 setRowCount(settings.binCount());
884 setXAxis(settings.firstEdge() + 0.5 * settings.binWidth(),
885 settings.binWidth());
889 const AnalysisHistogramSettings &
890 AnalysisDataBinAverageModule::settings() const
892 return impl_->settings_;
897 AnalysisDataBinAverageModule::flags() const
899 return efAllowMulticolumn | efAllowMultipoint | efAllowMultipleDataSets;
904 AnalysisDataBinAverageModule::dataStarted(AbstractAnalysisData *data)
906 setColumnCount(data->dataSetCount());
907 impl_->averagers_.resize(data->dataSetCount());
908 for (int i = 0; i < data->dataSetCount(); ++i)
910 impl_->averagers_[i].setColumnCount(rowCount());
916 AnalysisDataBinAverageModule::frameStarted(const AnalysisDataFrameHeader & /*header*/)
922 AnalysisDataBinAverageModule::pointsAdded(const AnalysisDataPointSetRef &points)
924 if (points.firstColumn() != 0 || points.columnCount() < 2)
926 GMX_THROW(APIError("Invalid data layout"));
928 int bin = settings().findBin(points.y(0));
931 AnalysisDataFrameAverager &averager = impl_->averagers_[points.dataSetIndex()];
932 for (int i = 1; i < points.columnCount(); ++i)
934 averager.addValue(bin, points.y(i));
941 AnalysisDataBinAverageModule::frameFinished(const AnalysisDataFrameHeader & /*header*/)
947 AnalysisDataBinAverageModule::dataFinished()
950 for (int i = 0; i < columnCount(); ++i)
952 AnalysisDataFrameAverager &averager = impl_->averagers_[i];
954 for (int j = 0; j < rowCount(); ++j)
956 value(j, i).setValue(averager.average(j),
957 std::sqrt(averager.variance(j)));