diff --git a/Device/InputOutput/OutputDataReadWriteNicos.cpp b/Device/InputOutput/OutputDataReadWriteNicos.cpp new file mode 100644 index 0000000000000000000000000000000000000000..669987496bdce29ec9caaf2e8912edce055c03c0 --- /dev/null +++ b/Device/InputOutput/OutputDataReadWriteNicos.cpp @@ -0,0 +1,147 @@ +// ************************************************************************************************ +// +// BornAgain: simulate and fit reflection and scattering +// +//! @file Device/InputOutput/OutputDataReadWriteNicos.cpp +//! @brief Implements class OutputDataReadWriteNicos +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2021 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************************************ + +#include "Device/InputOutput/OutputDataReadWriteNicos.h" +#include "Base/Utils/StringUtils.h" + +OutputData<double>* OutputDataReadWriteNicos::readOutputData(std::istream& input_stream) +{ + OutputData<double>* result = new OutputData<double>; + std::string line; + m_currentLineNr = 0; + + int m_width = 0; + int m_height = 0; + + // -- read dimensions + bool inFileSection = false; + bool fileSectionFound = false; + while (std::getline(input_stream, line)) { + m_currentLineNr++; + line = StringUtils::trimFront(line, " "); + if (!inFileSection && StringUtils::startsWith(line, "%File")) { + inFileSection = true; + fileSectionFound = true; + continue; + } + + if (!inFileSection) + continue; + + if (StringUtils::startsWith(line, "%")) { + inFileSection = false; + break; + } + + if (StringUtils::startsWith(line, "DataSizeX") && inFileSection) { + m_width = readAssignedIntValue(line); + } else if (StringUtils::startsWith(line, "DataSizeY") && inFileSection) { + m_height = readAssignedIntValue(line); + } + + if (m_width != 0 && m_height != 0) + break; + } + + if (!fileSectionFound) + throw std::runtime_error("Could not find 'File' section."); + if (m_width == 0) + throw std::runtime_error("Could not find DataSizeX value."); + if (m_height == 0) + throw std::runtime_error("Could not find DataSizeY value."); + + result->addAxis("x", m_width, 0.0, m_width); + result->addAxis("y", m_height, 0.0, m_height); + + // -- read data + bool inCountSection = false; + bool countSectionFound = false; + int dataRow = 0; + std::vector<unsigned> axes_indices(2); + + while (std::getline(input_stream, line)) { + m_currentLineNr++; + line = StringUtils::trimFront(line, " "); + if (!inCountSection && StringUtils::startsWith(line, "%Counts")) { + inCountSection = true; + countSectionFound = true; + continue; + } + + if (!inCountSection) + continue; + + if (StringUtils::startsWith(line, "%")) { + inCountSection = false; + break; + } + + // line is a data line + line = StringUtils::trim(line, " "); + if (line.empty()) + continue; + + const auto valuesAsString = StringUtils::split(line, ","); + if (valuesAsString.size() != m_width) + throw std::runtime_error(lineRelatedError( + "Number of found values (" + std::to_string(valuesAsString.size()) + + ") does not match DataSizeX (" + std::to_string(m_width) + ").")); + + for (unsigned col = 0; col < m_width; ++col) { + axes_indices[0] = col; + axes_indices[1] = static_cast<unsigned>(m_height) - 1 + - dataRow; // #baNicos check this (taken from TIFF) + const size_t global_index = result->toGlobalIndex(axes_indices); + + int value = 0; + if (!StringUtils::to_int(valuesAsString[col], &value)) + throw std::runtime_error(lineRelatedError( + "Value '" + valuesAsString[col] + "' could not be converted to integer.")); + + (*result)[global_index] = value; + } + dataRow++; + + if (dataRow == m_height) + break; + } + + if (!countSectionFound) + throw std::runtime_error("Could not find 'Counts' section."); + if (dataRow != m_height) + throw std::runtime_error("Number of found data rows (" + std::to_string(dataRow) + + ") does not match DataSizeY (" + std::to_string(m_height) + + ")."); + + return result; +} + +int OutputDataReadWriteNicos::readAssignedIntValue(const std::string& line) const +{ + auto parts = StringUtils::split(line, "="); + if (parts.size() != 2) + throw std::runtime_error(lineRelatedError("Missing assigned value.")); + + int value = 0; + if (!StringUtils::to_int(parts[1], &value)) + throw std::runtime_error( + lineRelatedError("Can't parse assigned value '" + parts[1] + "'.")); + + return value; +} + +std::string OutputDataReadWriteNicos::lineRelatedError(const std::string& errorText) const +{ + return "Line " + std::to_string(m_currentLineNr) + ": " + errorText; +} diff --git a/Device/InputOutput/OutputDataReadWriteNicos.h b/Device/InputOutput/OutputDataReadWriteNicos.h new file mode 100644 index 0000000000000000000000000000000000000000..dfa1c8dd6e149f7aa912969a93804b91fcc8c993 --- /dev/null +++ b/Device/InputOutput/OutputDataReadWriteNicos.h @@ -0,0 +1,38 @@ +// ************************************************************************************************ +// +// BornAgain: simulate and fit reflection and scattering +// +//! @file Device/InputOutput/OutputDataReadWriteNicos.h +//! @brief Defines class OutputDataReadWriteNicos +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2021 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************************************ + +#ifndef BORNAGAIN_DEVICE_INPUTOUTPUT_OUTPUTDATAREADWRITENICOS_H +#define BORNAGAIN_DEVICE_INPUTOUTPUT_OUTPUTDATAREADWRITENICOS_H + +#include "Device/Data/OutputData.h" + +//! Read/write Nicos files (*.001). +//! @ingroup input_output_internal + +class OutputDataReadWriteNicos { +public: + OutputData<double>* readOutputData(std::istream& input_stream); + +private: + //! Reads the assigned integer value from a line content like "DataSizeX = 100" + //! Throws if not successful + int readAssignedIntValue(const std::string& line) const; + + //! Returns errorText with prepended line number (suitable for throwing errors) + std::string lineRelatedError(const std::string& errorText) const; + + int m_currentLineNr = 0; //!< "1" means "first line" (human readable counting) +}; + +#endif // BORNAGAIN_DEVICE_INPUTOUTPUT_OUTPUTDATAREADWRITENICOS_H