/* * This file is part of the GROMACS molecular simulation package. * * Copyright (c) 2012,2013,2014,2017,2019, by the GROMACS development team, led by * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, * and including many others, as listed in the AUTHORS file in the * top-level source directory and at http://www.gromacs.org. * * GROMACS is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * GROMACS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with GROMACS; if not, see * http://www.gnu.org/licenses, or write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * If you want to redistribute modifications to GROMACS, please * consider that scientific software is very special. Version * control is crucial - bugs must be traceable. We will be happy to * consider code for inclusion in the official distribution, but * derived work must not be called official GROMACS. Details are found * in the README & COPYING files - if they are missing, get the * official version at http://www.gromacs.org. * * To help us fund GROMACS development, we humbly ask that you cite * the research papers on the package. Check out http://www.gromacs.org. */ /*! \file * \brief * Declares classes for accessing data frame information. * * \author Teemu Murtola * \inpublicapi * \ingroup module_analysisdata */ #ifndef GMX_ANALYSISDATA_DATAFRAME_H #define GMX_ANALYSISDATA_DATAFRAME_H #include #include "gromacs/utility/arrayref.h" #include "gromacs/utility/flags.h" #include "gromacs/utility/gmxassert.h" #include "gromacs/utility/real.h" namespace gmx { /*! \brief * Value type for representing a single value in analysis data objects. * * Default copy constructor and assignment operator are used and work as * intended. * * Methods in this class do not throw. * * Non-const methods are provided for use within the library only; currently * it is not possible to access a non-const AnalysisDataValue through the * public interface. * * \inpublicapi * \ingroup module_analysisdata */ class AnalysisDataValue { public: /*! \brief * Constructs an unset value. */ AnalysisDataValue() : value_(0.0), error_(0.0) {} /*! \brief * Constructs a value object with the given value. * * The constructed object is marked as set and present. */ explicit AnalysisDataValue(real value) : value_(value), error_(0.0) { flags_.set(efSet); flags_.set(efPresent); } /*! \brief * Direct access to the value. * * Assigning a value to this does not mark the value as set; setValue() * must be used for this. */ real& value() { return value_; } /*! \brief * Direct access to the error estimate. * * Assigning a value to this does not mark the error estimate as set; * setValue() must be used for this. */ real& error() { return error_; } //! Returns the value for this value. real value() const { return value_; } //! Returns the error estimate for this value, or zero if not set. real error() const { return error_; } /*! \brief * Returns whether this value has been set. * * If this method returns false, the return value of value() and * error() are undefined. */ bool isSet() const { return flags_.test(efSet); } /*! \brief * Returns whether the error estimate for this value has been set. * * If this method returns false, but isSet() returns true, error() * returns zero. */ bool hasError() const { return flags_.test(efErrorSet); } /*! \brief * Returns whether this value has been marked as present. * * If this method returns false, it is up to the source data to define * whether isSet() may return true. */ bool isPresent() const { return flags_.test(efPresent); } //! Clears and unsets this value. void clear() { *this = AnalysisDataValue(); } //! Sets this value. void setValue(real value, bool bPresent = true) { value_ = value; flags_.set(efSet); flags_.set(efPresent, bPresent); } //! Sets this value and its error estimate. void setValue(real value, real error, bool bPresent = true) { value_ = value; error_ = error; flags_.set(efSet); flags_.set(efErrorSet); flags_.set(efPresent, bPresent); } //! Set only error estimate for this value. void setError(real error) { error_ = error; flags_.set(efErrorSet); } private: //! Possible flags for \a flags_. enum Flag { efSet = 1 << 0, //!< Value has been set. efErrorSet = 1 << 1, //!< Error estimate has been set. efPresent = 1 << 2 //!< Value is set as present. }; //! Value for this value. real value_; //! Error estimate for this value, zero if not set. real error_; //! Status flags for thise value. FlagsTemplate flags_; }; //! Shorthand for reference to an array of data values. typedef ArrayRef AnalysisDataValuesRef; /*! \brief * Value type for storing frame-level information for analysis data. * * Default copy constructor and assignment operator are used and work as * intended. * Typically new objects of this type are only constructed internally by the * library and in classes that are derived from AbstractAnalysisData. * * Methods in this class do not throw, but may contain asserts for incorrect * usage. * * Note that it is not possible to change the contents of an initialized * object, except by assigning a new object to replace it completely. * * \inpublicapi * \ingroup module_analysisdata */ class AnalysisDataFrameHeader { public: /*! \brief * Constructs an invalid frame header. * * Return values of other methods than isValid() are unspecified for * the constructed object. */ AnalysisDataFrameHeader(); /*! \brief * Constructs a frame header from given values. * * \param[in] index Index of the frame. Must be >= 0. * \param[in] x x coordinate for the frame. * \param[in] dx Error estimate for x. */ AnalysisDataFrameHeader(int index, real x, real dx); /*! \brief * Returns whether the frame header corresponds to a valid frame. * * If returns false, return values of other methods are not specified. */ bool isValid() const { return index_ >= 0; } /*! \brief * Returns zero-based index of the frame. * * The return value is >= 0 for valid frames. * Should not be called for invalid frames. */ int index() const { GMX_ASSERT(isValid(), "Tried to access invalid frame header"); return index_; } /*! \brief * Returns the x coordinate for the frame. * * Should not be called for invalid frames. */ real x() const { GMX_ASSERT(isValid(), "Tried to access invalid frame header"); return x_; } /*! \brief * Returns error in the x coordinate for the frame (if applicable). * * All data do not provide error estimates. * Typically returns zero in those cases. * * Should not be called for invalid frames. */ real dx() const { GMX_ASSERT(isValid(), "Tried to access invalid frame header"); return dx_; } private: int index_; real x_; real dx_; }; /*! \cond libinternal */ /*! \libinternal \brief * Value type for internal indexing of point sets. * * This class contains the necessary data to split an array of * AnalysisDataValue objects into point sets. It is always specified in the * context of an array of AnalysisDataValues: the point set specified by this * class contains valueCount() values, starting from the array index * valueOffset(). * The value at location valueOffset() corresponds to column firstColumn(). * It is not necessary for code using the analysis data framework to know of * this class, but it is declared in a public header to allow using it in other * types. * * Default copy constructor and assignment operator are used and work as * intended. * Typically new objects of this type are only constructed internally by the * library and in classes that are derived from AbstractAnalysisData. * * Methods in this class do not throw, but may contain asserts for incorrect * usage. * * Note that it is not possible to change the contents of an initialized * object, except by assigning a new object to replace it completely. * * \inlibraryapi * \ingroup module_analysisdata */ class AnalysisDataPointSetInfo { public: //! Construct point set data object with the given values. AnalysisDataPointSetInfo(int valueOffset, int valueCount, int dataSetIndex, int firstColumn) : valueOffset_(valueOffset), valueCount_(valueCount), dataSetIndex_(dataSetIndex), firstColumn_(firstColumn) { GMX_ASSERT(valueOffset >= 0, "Negative value offsets are invalid"); GMX_ASSERT(valueCount >= 0, "Negative value counts are invalid"); GMX_ASSERT(dataSetIndex >= 0, "Negative data set indices are invalid"); GMX_ASSERT(firstColumn >= 0, "Negative column indices are invalid"); } //! Returns the offset of the first value in the referenced value array. int valueOffset() const { return valueOffset_; } //! Returns the number of values in this point set. int valueCount() const { return valueCount_; } //! Returns the data set index for this point set. int dataSetIndex() const { return dataSetIndex_; } //! Returns the index of the first column in this point set. int firstColumn() const { return firstColumn_; } private: int valueOffset_; int valueCount_; int dataSetIndex_; int firstColumn_; }; //! Shorthand for reference to an array of point set data objects. typedef ArrayRef AnalysisDataPointSetInfosRef; //! \endcond /*! \brief * Value type wrapper for non-mutable access to a set of data column values. * * Default copy constructor and assignment operator are used and work as * intended. * Typically new objects of this type are only constructed internally by the * library and in classes that are derived from AbstractAnalysisData. * * Methods in this class do not throw, but may contain asserts for incorrect * usage. * * The design of the interfaces is such that all objects of this type should be * valid, i.e., header().isValid() should always return true. * * Note that it is not possible to change the contents of an initialized * object, except by assigning a new object to replace it completely. * * \inpublicapi * \ingroup module_analysisdata */ class AnalysisDataPointSetRef { public: /*! \brief * Constructs a point set reference from given values. * * \param[in] header Header for the frame. * \param[in] pointSetInfo Information about the point set. * \param[in] values Values for each column. * * The first element of the point set should be found from \p values * using the offset in \p pointSetInfo. */ AnalysisDataPointSetRef(const AnalysisDataFrameHeader& header, const AnalysisDataPointSetInfo& pointSetInfo, const AnalysisDataValuesRef& values); /*! \brief * Constructs a point set reference from given values. * * \param[in] header Header for the frame. * \param[in] values Values for each column. * * The first element in \p values should correspond to the first * column. */ AnalysisDataPointSetRef(const AnalysisDataFrameHeader& header, const std::vector& values); /*! \brief * Constructs a point set reference to a subset of columns. * * \param[in] points Point set to use as source. * \param[in] firstColumn First column index to include. * \param[in] columnCount Number of columns to include. * * Creates a point set that contains \p columnCount columns starting * from \p firstColumn from \p points, or a subset if all requested * columns are not present in \p points. If the requested column range * and the range in \p points do not intersect, the result has * columnCount() == 0. * * \p firstColumn is relative to the whole data set, i.e., not relative * to points.firstColumn(). * * Mainly intended for internal use. */ AnalysisDataPointSetRef(const AnalysisDataPointSetRef& points, int firstColumn, int columnCount); /*! \brief * Returns the frame header for the frame of this point set. */ const AnalysisDataFrameHeader& header() const { return header_; } //! \copydoc AnalysisDataFrameHeader::index() int frameIndex() const { return header_.index(); } //! \copydoc AnalysisDataFrameHeader::x() real x() const { return header_.x(); } //! \copydoc AnalysisDataFrameHeader::dx() real dx() const { return header_.dx(); } //! Returns zero-based index of the dataset that this set is part of. int dataSetIndex() const { return dataSetIndex_; } //! Returns zero-based index of the first column included in this set. int firstColumn() const { return firstColumn_; } //! Returns the number of columns included in this set. int columnCount() const { return ssize(values()); } //! Returns zero-based index of the last column included in this set (inclusive). int lastColumn() const { return firstColumn_ + columnCount() - 1; } /*! \brief * Returns reference container for all values. * * First value in the returned container corresponds to firstColumn(). */ const AnalysisDataValuesRef& values() const { return values_; } /*! \brief * Returns data value for a column in this set. * * \param[in] i Zero-based column index relative to firstColumn(). * Should be >= 0 and < columnCount(). */ real y(int i) const { GMX_ASSERT(i >= 0 && i < columnCount(), "Out of range data access"); return values()[i].value(); } /*! \brief * Returns error estimate for a column in this set if applicable. * * \param[in] i Zero-based column index relative to firstColumn(). * Should be >= 0 and < columnCount(). * * Currently, this method returns zero if the source data does not * specify errors. */ real dy(int i) const { GMX_ASSERT(i >= 0 && i < columnCount(), "Out of range data access"); return values()[i].error(); } /*! \brief * Returns whether a column is present in this set. * * \param[in] i Zero-based column index relative to firstColumn(). * Should be >= 0 and < columnCount(). * * If present(i) returns false, it is depends on the source data * whether y(i) and/or dy(i) are defined. */ bool present(int i) const { GMX_ASSERT(i >= 0 && i < columnCount(), "Out of range data access"); return values()[i].isPresent(); } /*! \brief * Returns true if all points in this point set are present. * * That is, if present() would return true for all points. */ bool allPresent() const; private: AnalysisDataFrameHeader header_; int dataSetIndex_; int firstColumn_; AnalysisDataValuesRef values_; }; /*! \brief * Value type wrapper for non-mutable access to a data frame. * * Default copy constructor and assignment operator are used and work as * intended. * Typically new objects of this type are only constructed internally by the * library and in classes that are derived from AbstractAnalysisData. * * Methods in this class do not throw, but may contain asserts for incorrect * usage. * * Note that it is not possible to change the contents of an initialized * object, except by assigning a new object to replace it completely. * * \inpublicapi * \ingroup module_analysisdata */ class AnalysisDataFrameRef { public: /*! \brief * Constructs an invalid frame reference. * * Return values of other methods than isValid() are unspecified for * the constructed object. */ AnalysisDataFrameRef(); /*! \brief * Constructs a frame reference from given values. * * \param[in] header Header for the frame. * \param[in] values Values for each column. * \param[in] pointSets Point set data. */ AnalysisDataFrameRef(const AnalysisDataFrameHeader& header, const AnalysisDataValuesRef& values, const AnalysisDataPointSetInfosRef& pointSets); /*! \brief * Constructs a frame reference from given values. * * \param[in] header Header for the frame. * \param[in] values Values for each column. * \param[in] pointSets Point set data. */ AnalysisDataFrameRef(const AnalysisDataFrameHeader& header, const std::vector& values, const std::vector& pointSets); /*! \brief * Constructs a frame reference to a subset of columns. * * \param[in] frame Frame to use as source. * \param[in] firstColumn First column index to include. * \param[in] columnCount Number of columns to include. * * Creates a frame reference that contains \p columnCount columns * starting from \p firstColumn from \p frame, or a subset if all * requested columns are not present in \p frame. * * Mainly intended for internal use. */ AnalysisDataFrameRef(const AnalysisDataFrameRef& frame, int firstColumn, int columnCount); /*! \brief * Returns whether the object refers to a valid frame. * * If returns false, return values of other methods are not specified. */ bool isValid() const { return header().isValid(); } //! Returns the header for this frame. const AnalysisDataFrameHeader& header() const { return header_; } //! \copydoc AnalysisDataFrameHeader::index() int frameIndex() const { return header().index(); } //! \copydoc AnalysisDataFrameHeader::x() real x() const { return header().x(); } //! \copydoc AnalysisDataFrameHeader::dx() real dx() const { return header().dx(); } /*! \brief * Returns the number of point sets for this frame. * * Returns zero for an invalid frame. */ int pointSetCount() const { return ssize(pointSets_); } /*! \brief * Returns point set reference for a given point set. * * Should not be called for invalid frames. */ AnalysisDataPointSetRef pointSet(int index) const { GMX_ASSERT(isValid(), "Invalid data frame accessed"); GMX_ASSERT(index >= 0 && index < pointSetCount(), "Out of range data access"); return AnalysisDataPointSetRef(header_, pointSets_[index], values_); } /*! \brief * Convenience method for accessing a column value in simple data. * * \copydetails AnalysisDataPointSetRef::y() */ real y(int i) const { return singleColumnValue(i).value(); } /*! \brief * Convenience method for accessing error for a column value in simple * data. * * \copydetails AnalysisDataPointSetRef::dy() */ real dy(int i) const { return singleColumnValue(i).error(); } /*! \brief * Convenience method for accessing present status for a column in * simple data. * * \copydetails AnalysisDataPointSetRef::present() */ bool present(int i) const { return singleColumnValue(i).isPresent(); } /*! \brief * Returns true if all points in this frame are present. */ bool allPresent() const; private: //! Helper method for accessing single columns in simple data. const AnalysisDataValue& singleColumnValue(int i) const { GMX_ASSERT(isValid(), "Invalid data frame accessed"); GMX_ASSERT(pointSets_.size() == 1U && pointSets_[0].firstColumn() == 0, "Convenience method not available for multiple point sets"); GMX_ASSERT(i >= 0 && i < ssize(values_), "Out of range data access"); return values_[i]; } AnalysisDataFrameHeader header_; AnalysisDataValuesRef values_; AnalysisDataPointSetInfosRef pointSets_; }; } // namespace gmx #endif