From 122c759246600ddc8a1d428d0c0468a7dfaa5490 Mon Sep 17 00:00:00 2001 From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de> Date: Thu, 10 Dec 2020 11:51:40 +0100 Subject: [PATCH] Corr: add DataUtils --- Device/Data/DataUtils.cpp | 247 ++++++++++++++++++++++++++++++++++++++ Device/Data/DataUtils.h | 71 +++++++++++ 2 files changed, 318 insertions(+) create mode 100644 Device/Data/DataUtils.cpp create mode 100644 Device/Data/DataUtils.h diff --git a/Device/Data/DataUtils.cpp b/Device/Data/DataUtils.cpp new file mode 100644 index 00000000000..9bcd7511e55 --- /dev/null +++ b/Device/Data/DataUtils.cpp @@ -0,0 +1,247 @@ +// ************************************************************************************************ +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file Device/Data/DataUtils.cpp +//! @brief Implements namespace DataUtils. +//! +//! @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/Data/DataUtils.h" +#include "Base/Math/FourierTransform.h" +#include "Base/Math/Numeric.h" +#include <iostream> + +namespace { + +std::vector<std::vector<double>> FT2DArray(const std::vector<std::vector<double>>& signal) { + FourierTransform ft; + std::vector<std::vector<double>> ret; + ft.fft(signal, ret); + ft.fftshift(ret); // low frequency to center of array + return ret; +} + +} // namespace + +//! Returns relative difference between two data sets sum(dat[i] - ref[i])/ref[i]). +double DataUtils::getRelativeDifference(const OutputData<double>& dat, + const OutputData<double>& ref) { + if (!dat.hasSameDimensions(ref)) + throw std::runtime_error("OutputData dimension differs from reference"); + + double diff = 0.0; + for (size_t i = 0; i < dat.getAllocatedSize(); ++i) + diff += Numeric::GetRelativeDifference(dat[i], ref[i]); + diff /= dat.getAllocatedSize(); + if (std::isnan(diff)) + throw std::runtime_error("diff=NaN!"); + return diff; +} + +//! Returns true is relative difference is below threshold; prints informative output +bool DataUtils::checkRelativeDifference(const OutputData<double>& dat, + const OutputData<double>& ref, const double threshold) { + const double diff = getRelativeDifference(dat, ref); + if (diff > threshold) { + std::cerr << "FAILED: relative deviation of dat from ref is " << diff + << ", above given threshold " << threshold << "\n"; + return false; + } + if (diff) + std::cerr << "- OK: relative deviation of dat from ref is " << diff + << ", within given threshold " << threshold << "\n"; + else + std::cout << "- OK: dat = ref\n"; + return true; +} + +std::unique_ptr<OutputData<double>> +DataUtils::createRelativeDifferenceData(const OutputData<double>& data, + const OutputData<double>& reference) { + if (!data.hasSameDimensions(reference)) + throw std::runtime_error("DataUtils::createRelativeDifferenceData() -> " + "Error. Different dimensions of data and reference."); + std::unique_ptr<OutputData<double>> result(reference.clone()); + for (size_t i = 0; i < result->getAllocatedSize(); ++i) + (*result)[i] = Numeric::GetRelativeDifference(data[i], reference[i]); + return result; +} + +std::unique_ptr<OutputData<double>> +DataUtils::createRearrangedDataSet(const OutputData<double>& data, int n) { + if (data.rank() != 2) + throw std::runtime_error("DataUtils::rotateDataByN90Deg()" + " -> Error! Works only on two-dimensional data"); + n = (4 + n % 4) % 4; + if (n == 0) + return std::unique_ptr<OutputData<double>>(data.clone()); + std::unique_ptr<OutputData<double>> output(new OutputData<double>()); + + // swapping axes if necessary + const IAxis& x_axis = data.axis(0); + const IAxis& y_axis = data.axis(1); + output->addAxis(n == 2 ? x_axis : y_axis); + output->addAxis(n == 2 ? y_axis : x_axis); + + // creating index mapping + std::function<void(std::vector<int>&)> index_mapping; + if (n == 2) { + const int end_bin_x = static_cast<int>(x_axis.size()) - 1; + const int end_bin_y = static_cast<int>(y_axis.size()) - 1; + index_mapping = [end_bin_x, end_bin_y](std::vector<int>& inds) { + inds[0] = end_bin_x - inds[0]; + inds[1] = end_bin_y - inds[1]; + }; + } else { + const size_t rev_axis_i = n % 3; + const size_t end_bin = data.axis(rev_axis_i).size() - 1; + index_mapping = [rev_axis_i, end_bin](std::vector<int>& inds) { + const int tm_index = inds[rev_axis_i]; + inds[rev_axis_i] = inds[rev_axis_i ^ 1]; + inds[rev_axis_i ^ 1] = static_cast<int>(end_bin) - tm_index; + }; + } + + for (size_t index = 0, size = data.getAllocatedSize(); index < size; ++index) { + std::vector<int> axis_inds = data.getAxesBinIndices(index); + index_mapping(axis_inds); + size_t output_index = output->toGlobalIndex( + {static_cast<unsigned>(axis_inds[0]), static_cast<unsigned>(axis_inds[1])}); + (*output)[output_index] = data[index]; + } + return output; +} + +std::unique_ptr<OutputData<double>> +DataUtils::createClippedDataSet(const OutputData<double>& origin, double x1, double y1, double x2, + double y2) { + if (origin.rank() != 2) + throw std::runtime_error("DataUtils::createClippedData()" + " -> Error! Works only on two-dimensional data"); + + std::unique_ptr<OutputData<double>> result(new OutputData<double>); + for (size_t i_axis = 0; i_axis < origin.rank(); i_axis++) { + const IAxis& axis = origin.axis(i_axis); + IAxis* new_axis; + if (i_axis == 0) + new_axis = axis.createClippedAxis(x1, x2); + else + new_axis = axis.createClippedAxis(y1, y2); + result->addAxis(*new_axis); + delete new_axis; + } + result->setAllTo(0.0); + + OutputData<double>::const_iterator it_origin = origin.begin(); + OutputData<double>::iterator it_result = result->begin(); + while (it_origin != origin.end()) { + double x = origin.getAxisValue(it_origin.getIndex(), 0); + double y = origin.getAxisValue(it_origin.getIndex(), 1); + if (result->axis(0).contains(x) && result->axis(1).contains(y)) { + *it_result = *it_origin; + ++it_result; + } + ++it_origin; + } + + return result; +} + +// For axis FixedBinAxis("axis", 8, -5.0, 3.0) the coordinate x=-4.5 (center of bin #0) will +// be converted into 0.5 (which is a bin center expressed in bin fraction coordinates). +// The coordinate -5.0 (outside of axis definition) will be converted to -0.5 +// (center of non-existing bin #-1). +// Used for Mask conversion. + +double DataUtils::coordinateToBinf(double coordinate, const IAxis& axis) { + size_t index = axis.findClosestIndex(coordinate); + Bin1D bin = axis.bin(index); + double f = (coordinate - bin.m_lower) / bin.binSize(); + return static_cast<double>(index) + f; +} + +double DataUtils::coordinateFromBinf(double value, const IAxis& axis) { + int index = static_cast<int>(value); + + double result(0); + if (index < 0) { + Bin1D bin = axis.bin(0); + result = bin.m_lower + value * bin.binSize(); + } else if (index >= static_cast<int>(axis.size())) { + Bin1D bin = axis.bin(axis.size() - 1); + result = bin.m_upper + (value - axis.size()) * bin.binSize(); + } else { + Bin1D bin = axis.bin(static_cast<size_t>(index)); + result = bin.m_lower + (value - static_cast<double>(index)) * bin.binSize(); + } + + return result; +} + +void DataUtils::coordinateToBinf(double& x, double& y, const OutputData<double>& data) { + x = coordinateToBinf(x, data.axis(0)); + y = coordinateToBinf(y, data.axis(1)); +} + +void DataUtils::coordinateFromBinf(double& x, double& y, const OutputData<double>& data) { + x = coordinateFromBinf(x, data.axis(0)); + y = coordinateFromBinf(y, data.axis(1)); +} + +std::vector<std::vector<double>> +DataUtils::create2DArrayfromOutputData(const OutputData<double>& data) { + if (data.rank() != 2) + throw std::runtime_error("DataUtils::create2DArrayfromOutputData() -> " + "Error! Works only on two-dimensional data"); + + std::vector<std::vector<double>> array_2d; + std::vector<double> row_vec; // row vector for constructing each row of 2D array + + size_t nrows = data.axis(0).size(); + size_t ncols = data.axis(1).size(); + + size_t it = 0; // iterator of 'data' + for (size_t row = 0; row < nrows; row++) { + row_vec.clear(); + for (size_t col = 0; col < ncols; col++) { + row_vec.push_back(data[it]); + it++; + } + array_2d.push_back(row_vec); + } + + return array_2d; +} + +std::unique_ptr<OutputData<double>> +DataUtils::createOutputDatafrom2DArray(const std::vector<std::vector<double>>& array_2d) { + std::unique_ptr<OutputData<double>> result(new OutputData<double>); + size_t nrows = array_2d.size(); + size_t ncols = array_2d[0].size(); + + result->addAxis("x", nrows, 0.0, double(nrows)); + result->addAxis("y", ncols, 0.0, double(ncols)); + std::vector<unsigned> axes_indices(2); + for (unsigned row = 0; row < nrows; row++) { + for (unsigned col = 0; col < ncols; col++) { + axes_indices[0] = row; + axes_indices[1] = col; + size_t global_index = result->toGlobalIndex(axes_indices); + (*result)[global_index] = array_2d[row][col]; + } + } + + return result; +} + +std::unique_ptr<OutputData<double>> DataUtils::createFFT(const OutputData<double>& data) { + auto array_2d = DataUtils::create2DArrayfromOutputData(data); + auto fft_array_2d = FT2DArray(array_2d); + return DataUtils::createOutputDatafrom2DArray(fft_array_2d); +} diff --git a/Device/Data/DataUtils.h b/Device/Data/DataUtils.h new file mode 100644 index 00000000000..3b05ea509d4 --- /dev/null +++ b/Device/Data/DataUtils.h @@ -0,0 +1,71 @@ +// ************************************************************************************************ +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file Device/Data/DataUtils.h +//! @brief Defines namespace DataUtils. +//! +//! @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) +// +// ************************************************************************************************ + +#ifndef BORNAGAIN_DEVICE_DATA_DATAUTILS_H +#define BORNAGAIN_DEVICE_DATA_DATAUTILS_H + +#include "Device/Data/OutputData.h" +#include <memory> + +namespace DataUtils { + +//! Returns true is relative difference is below threshold; prints informative output +bool checkRelativeDifference(const OutputData<double>& dat, const OutputData<double>& ref, + const double threshold); + +//! Transforms coordinate on axis into the bin-fraction-coordinate. +double coordinateToBinf(double coordinate, const IAxis& axis); + +//! Transforms bin-fraction-coordinate into axis coordinate. +double coordinateFromBinf(double value, const IAxis& axis); + +//! Transforms x,y coordinate from OutputData axes coordinates to bin-fraction-coordinates. +void coordinateToBinf(double& x, double& y, const OutputData<double>& data); + +//! Transforms x,y coordinate from bin-fraction-coordinates to OutputData's axes coordinates. +void coordinateFromBinf(double& x, double& y, const OutputData<double>& data); + +//! Creates a vector of vectors of double (2D Array) from OutputData. +std::vector<std::vector<double>> create2DArrayfromOutputData(const OutputData<double>& data); + +#ifndef SWIG + +//! Returns relative difference between two data sets sum(dat[i] - ref[i])/ref[i]). +double getRelativeDifference(const OutputData<double>& dat, const OutputData<double>& ref); + +std::unique_ptr<OutputData<double>> +createRelativeDifferenceData(const OutputData<double>& data, const OutputData<double>& reference); + +//! Returns new object with input data rotated by +//! n*90 deg counterclockwise (n > 0) or clockwise (n < 0) +//! Axes are swapped if the data is effectively rotated by 90 or 270 degrees +//! Applicable to 2D arrays only +std::unique_ptr<OutputData<double>> createRearrangedDataSet(const OutputData<double>& data, int n); + +//! Returns new IntensityData objects which axes clipped to represent the specified rectangle. +std::unique_ptr<OutputData<double>> +createClippedDataSet(const OutputData<double>& origin, double x1, double y1, double x2, double y2); + +//! Creates OutputData from a 2D Array. +std::unique_ptr<OutputData<double>> +createOutputDatafrom2DArray(const std::vector<std::vector<double>>& array_2d); + +//! Creates Fourier Transform (OutputData format) of intensity map (OutputData format). +std::unique_ptr<OutputData<double>> createFFT(const OutputData<double>& data); + +#endif // USER_API + +} // namespace DataUtils + +#endif // BORNAGAIN_DEVICE_DATA_DATAUTILS_H -- GitLab