Skip to content
Snippets Groups Projects
IHistogram.cpp 9.15 KiB
//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Device/Histo/IHistogram.cpp
//! @brief     Implements class IntensityDataObject.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "Device/Histo/IHistogram.h"
#include "Base/Axis/Frame.h"
#include "Base/Math/Numeric.h"
#include "Base/Util/Assert.h"
#include "Device/Data/Powerfield.h"
#include "Device/Histo/Histogram1D.h"
#include "Device/Histo/Histogram2D.h"
#include "Device/Histo/IOFactory.h"
#include <memory>

IHistogram::IHistogram(const IAxis& axis_x)
    : m_frame(new Frame({axis_x.clone()}))
    , m_data(new Powerfield<CumulativeValue>(axis_x))
{
}

IHistogram::IHistogram(const IAxis& axis_x, const IAxis& axis_y)
    : m_frame(new Frame({axis_x.clone(), axis_y.clone()}))
    , m_data(new Powerfield<CumulativeValue>(axis_x, axis_y))
{
}

IHistogram::IHistogram(Powerfield<CumulativeValue>* data)
    : m_frame(new Frame(data->cloned_axes()))
    , m_data(data)
{
}


IHistogram::IHistogram(const IHistogram& other)
{
    m_data.reset(other.m_data->clone());
}


size_t IHistogram::getTotalNumberOfBins() const
{
    return m_data->allocatedSize();
}

const IAxis& IHistogram::xAxis() const
{
    return m_frame->axis(0);
}

const IAxis& IHistogram::yAxis() const
{
    return m_frame->axis(1);
}

double IHistogram::xMin() const
{
    return xAxis().min();
}

double IHistogram::xMax() const
{
    return xAxis().max();
}

size_t IHistogram::getNbinsX() const
{
    return xAxis().size();
}

double IHistogram::yMin() const
{
    return yAxis().min();
}

double IHistogram::yMax() const
{
    return yAxis().max();
}

size_t IHistogram::getNbinsY() const
{
    return yAxis().size();
}

size_t IHistogram::getGlobalBin(size_t binx, size_t biny) const
{
    std::vector<unsigned> axes_indices;
    axes_indices.push_back(static_cast<unsigned>(binx));
    if (rank() == 2)
        axes_indices.push_back(static_cast<unsigned>(biny));
    return m_data->toGlobalIndex(axes_indices);
}

size_t IHistogram::findGlobalBin(double x, double y) const
{
    std::vector<double> coordinates;
    coordinates.push_back(x);
    if (rank() == 2)
        coordinates.push_back(y);
    return m_data->findGlobalIndex(coordinates);
}

size_t IHistogram::xAxisIndex(size_t i) const
{
    return m_frame->projectedIndex(i, 0);
}

size_t IHistogram::yAxisIndex(size_t i) const
{
    return m_frame->projectedIndex(i, 1);
}

double IHistogram::xAxisValue(size_t i)
{
    return m_frame->projectedCoord(i, 0);
}

double IHistogram::yAxisValue(size_t i)
{
    return m_frame->projectedCoord(i, 1);
}

double IHistogram::binContent(size_t i) const
{
    return (*m_data)[i].getContent();
}

double IHistogram::binContent(size_t binx, size_t biny) const
{
    return binContent(getGlobalBin(binx, biny));
}

void IHistogram::setBinContent(size_t i, double value)
{
    (*m_data)[i].setContent(value);
}

void IHistogram::addBinContent(size_t i, double value)
{
    (*m_data)[i].add(value);
}

double IHistogram::binError(size_t i) const
{
    return (*m_data)[i].getRMS();
}

double IHistogram::binError(size_t binx, size_t biny) const
{
    return binError(getGlobalBin(binx, biny));
}

double IHistogram::binAverage(size_t i) const
{
    return (*m_data)[i].getAverage();
}

double IHistogram::binAverage(size_t binx, size_t biny) const
{
    return binAverage(getGlobalBin(binx, biny));
}

double IHistogram::getMaximum() const
{
    ASSERT(m_data->allocatedSize()>0);
    double ret = (*m_data)[0].getContent();
    for (size_t i=1; i < m_data->allocatedSize(); ++i)
        if ((*m_data)[i].getContent() > ret)
            ret = (*m_data)[i].getContent();
    return ret;
}

double IHistogram::getMinimum() const
{
    ASSERT(m_data->allocatedSize()>0);
    double ret = (*m_data)[0].getContent();
    for (size_t i=1; i < m_data->allocatedSize(); ++i)
        if ((*m_data)[i].getContent() < ret)
            ret = (*m_data)[i].getContent();
    return ret;
}

void IHistogram::scale(double value)
{
    for (size_t index = 0; index < getTotalNumberOfBins(); ++index)
        (*m_data)[index].setContent(value * (*m_data)[index].getContent());
}

double IHistogram::integral() const
{
    double result(0.0);
    for (size_t index = 0; index < getTotalNumberOfBins(); ++index)
        result += (*m_data)[index].getContent();
    return result;
}

#ifdef BORNAGAIN_PYTHON
PyObject* IHistogram::array(DataType dataType) const
{
    const std::unique_ptr<Powerfield<double>> data(createPowerfield(dataType));
    return data->getArray();
}
#endif // BORNAGAIN_PYTHON

void IHistogram::reset()
{
    m_data->setAllTo(CumulativeValue());
}

IHistogram* IHistogram::createHistogram(const Powerfield<double>& source)
{
    if (source.rank() == 1)
        return new Histogram1D(source);
    if (source.rank() == 2)
        return new Histogram2D(source);
    std::ostringstream message;
    message << "IHistogram::createHistogram(const Powerfield<double>& source) -> Error. ";
    message << "The rank of source " << source.rank() << " ";
    message << "is not suitable for creation neither 1-dim nor 2-dim histograms.";
    throw std::runtime_error(message.str());
}

std::vector<double> IHistogram::meanVector() const
{
    std::vector<double> result(getTotalNumberOfBins());
    for (size_t i = 0; i < result.size(); ++i)
        result[i] = binContent(i);
    return result;
}

void IHistogram::init_from_data(const Powerfield<double>& source)
{
    if (rank() != source.rank()) {
        std::ostringstream message;
        message << "IHistogram::IHistogram(const Powerfield<double>& data) -> Error. ";
        message << "The dimension of this histogram " << rank() << " ";
        message << "is differ from the dimension of source " << m_data->rank() << std::endl;
        throw std::runtime_error(message.str());
    }

    m_data.reset(new Powerfield<CumulativeValue>(source.cloned_axes()));
    for (size_t i = 0; i < source.allocatedSize(); ++i)
        (*m_data)[i].add(source[i]);
}

//! Returns data of requested type for globalbin number
double IHistogram::binData(size_t i, IHistogram::DataType dataType) const
{
    if (dataType == DataType::INTEGRAL)
        return binContent(i);
    if (dataType == DataType::AVERAGE)
        return binAverage(i);
    if (dataType == DataType::STANDARD_ERROR)
        return binError(i);
    throw std::runtime_error("IHistogram::binData() -> Error. Unknown data type.");
}

//! Returns vector of values of requested DataType
std::vector<double> IHistogram::getDataVector(IHistogram::DataType dataType) const
{
    std::vector<double> result;
    result.resize(getTotalNumberOfBins(), 0.0);
    for (size_t index = 0; index < getTotalNumberOfBins(); ++index)
        result[index] = binData(index, dataType);
    return result;
}

//! Copy content (but not the axes) from other histogram. Dimensions should be the same.
void IHistogram::copyContentFrom(const IHistogram& other)
{
    if (!hasSameSizes(other))
        throw std::runtime_error(
            "IHistogram::copyContentFrom() -> Error. Can't copy the data of different shape.");
    reset();
    for (size_t i = 0; i < getTotalNumberOfBins(); ++i)
        (*m_data)[i] = (*other.m_data)[i];
}

//! creates new Powerfield with histogram's shape and put there values corresponding to DataType
Powerfield<double>* IHistogram::createPowerfield(IHistogram::DataType dataType) const
{
    auto* result = new Powerfield<double>(m_data->cloned_axes());
    for (size_t i = 0; i < getTotalNumberOfBins(); ++i)
        (*result)[i] = binData(i, dataType);
    return result;
}

bool IHistogram::hasSameShape(const IHistogram& other) const
{
    return m_data->hasSameShape(*other.m_data);
}

bool IHistogram::hasSameSizes(const IHistogram& other) const
{
    return m_data->hasSameSizes(*other.m_data);
}

const IHistogram& IHistogram::operator+=(const IHistogram& right)
{
    if (!hasSameSizes(right))
        throw std::runtime_error(
            "IHistogram::operator+=() -> Error. Histograms have different dimension");
    for (size_t i = 0; i < getTotalNumberOfBins(); ++i)
        addBinContent(i, right.binContent(i));
    return *this;
}

IHistogram* IHistogram::relativeDifferenceHistogram(const IHistogram& rhs) const
{
    if (!hasSameSizes(rhs))
        throw std::runtime_error("IHistogram::relativeDifferenceHistogram() -> Error. "
                                 "Histograms have different dimensions");

    IHistogram* result = this->clone();
    result->reset();

    for (size_t i = 0; i < getTotalNumberOfBins(); ++i) {
        double diff = Numeric::GetRelativeDifference(binContent(i), rhs.binContent(i));
        result->setBinContent(i, diff);
    }
    return result;
}

void IHistogram::save(const std::string& filename) const
{
    IOFactory::writeIntensityData(*this, filename);
}

void IHistogram::load(const std::string& filename)
{
    const std::unique_ptr<IHistogram> hist(IOFactory::readIntensityData(filename));
    copyContentFrom(*hist);
}