Skip to content
Snippets Groups Projects
IOFactory.cpp 7.65 KiB
//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Device/IO/IOFactory.cpp
//! @brief     Implements class DatafieldIOFactory.
//!
//! @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/IO/IOFactory.h"
#include "Base/Util/Assert.h"
#include "Base/Util/FileSystemUtils.h"
#include "Device/Data/Datafield.h"
#include "Device/Histo/DiffUtil.h"
#include "Device/Histo/SimulationResult.h"
#include "Device/IO/DataFormatUtils.h"
#include "Device/IO/ReadReflectometry.h"
#include "Device/IO/ReadWriteINT.h"
#include "Device/IO/ReadWriteNicos.h"
#include "Device/IO/ReadWriteNumpyTXT.h"
#include "Device/IO/ReadWriteTiff.h"
#include <exception>
#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>

#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable : 4244 4275)
#include "Device/IO/boost_streams.h"
#pragma warning(pop)
#else
#include "Device/IO/boost_streams.h"
#endif

Datafield* IOFactory::readDatafield(const std::string& file_name, LoaderSelector selector)
{
    const auto readAs = [=](LoaderSelector testForSelector) {
        return (selector == testForSelector)
               || (selector == automatic
                   && fileTypeMatchesLoaderSelector(file_name, testForSelector));
    };

    Datafield* result = nullptr;

    if (readAs(bornagain))
        result = readDatafield(file_name,
                               [](std::istream& s) { return ReadWriteINT().readDatafield(s); });

    else if (readAs(nicos))
        result = readDatafield(file_name, [](std::istream& s) { return IO::readNicosData(s); });

#ifdef BA_TIFF_SUPPORT
    else if (readAs(tiff))
        result = readDatafield(file_name,
                               [](std::istream& s) { return ReadWriteTiff().readDatafield(s); });
#endif

    else
        // Try to read ASCII by default. Binary maps to ASCII.
        // If the file is not actually a matrix of numbers,
        // the error will be thrown during the reading.
        result = readDatafield(
            file_name, [](std::istream& s) { return ReadWriteNumpyTXT().readDatafield(s); });

    ASSERT(result);
    return result;
}

Datafield* IOFactory::readReflectometryData(const std::string& file_name)
{
    return readDatafield(file_name,
                         [](std::istream& s) { return ReadReflectometry().readDatafield(s); });
}

void IOFactory::writeDatafield(const Datafield& data, const std::string& file_name)
{
    if (DataUtils::Format::isIntFile(file_name))
        writeDatafield(file_name, [&](std::ostream& s) { ReadWriteINT().writeDatafield(data, s); });
#ifdef BA_TIFF_SUPPORT
    else if (DataUtils::Format::isTiffFile(file_name))
        writeDatafield(file_name,
                       [&](std::ostream& s) { ReadWriteTiff().writeDatafield(data, s); });
#endif
    else
        writeDatafield(file_name,
                       [&](std::ostream& s) { ReadWriteNumpyTXT().writeDatafield(data, s); });
}

void IOFactory::writeDatafield(const std::string& file_name,
                               std::function<void(std::ostream&)> writeData)
{
    using namespace DataUtils::Format;

    std::ofstream fout;
    std::ios_base::openmode openmode = std::ios::out;
    if (isTiffFile(file_name) || isCompressed(file_name))
        openmode = std::ios::out | std::ios_base::binary;

#ifdef _WIN32
    fout.open(BaseUtils::Filesystem::convert_utf8_to_utf16(file_name), openmode);
#else
    fout.open(file_name, openmode);
#endif

    if (!fout.is_open())
        throw std::runtime_error("Cannot open file for writing: " + file_name);
    if (!fout.good())
        throw std::runtime_error("File is not good, probably it is a directory: " + file_name);
    std::stringstream ss;
    writeData(ss);

    boost::iostreams::filtering_streambuf<boost::iostreams::input> input_filtered;
    if (DataUtils::Format::isGZipped(file_name))
        input_filtered.push(boost::iostreams::gzip_compressor());
    else if (DataUtils::Format::isBZipped(file_name))
        input_filtered.push(boost::iostreams::bzip2_compressor());
    input_filtered.push(ss);

    boost::iostreams::copy(input_filtered, fout);

    fout.close();
}

bool IOFactory::fileTypeMatchesLoaderSelector(const std::string& fileName, LoaderSelector selector)
{
    switch (selector) {
    case bornagain:
        return DataUtils::Format::isIntFile(fileName);
    case nicos:
        return DataUtils::Format::isNicosFile(fileName);
    case tiff:
        return DataUtils::Format::isTiffFile(fileName);
    case automatic:
        return false;
    }

    return false;
}

void IOFactory::writeSimulationResult(const SimulationResult& result, const std::string& file_name)
{
    writeDatafield(result.data_field(), file_name);
}

Datafield* IOFactory::readDatafield(const std::string& file_name,
                                    std::function<Datafield*(std::istream&)> readData)
{
    if (!BaseUtils::Filesystem::IsFileExists(file_name))
        throw std::runtime_error("File does not exist: " + file_name);

    using namespace DataUtils::Format;
    std::ifstream input_stream;
    std::ios_base::openmode openmode = std::ios::in;
    if (isTiffFile(file_name) || isCompressed(file_name))
        openmode = std::ios::in | std::ios_base::binary;

#ifdef _WIN32
    input_stream.open(BaseUtils::Filesystem::convert_utf8_to_utf16(file_name), openmode);
#else
    input_stream.open(file_name, openmode);
#endif

    if (!input_stream.is_open())
        throw std::runtime_error("Cannot open file for reading: " + file_name);
    if (!input_stream.good())
        throw std::runtime_error("File is not good, probably it is a directory:" + file_name);

    boost::iostreams::filtering_streambuf<boost::iostreams::input> input_filtered;
    if (DataUtils::Format::isGZipped(file_name))
        input_filtered.push(boost::iostreams::gzip_decompressor());
    else if (DataUtils::Format::isBZipped(file_name))
        input_filtered.push(boost::iostreams::bzip2_decompressor());
    input_filtered.push(input_stream);
    // we use stringstream since it provides random access which is important for tiff files
    std::stringstream str;
    boost::iostreams::copy(input_filtered, str);

    return readData(str);
}


bool IOUtil::filesAgree(const std::string& datFileName, const std::string& refFileName, double tol)
{
    std::unique_ptr<Datafield> datDat;
    try {
        datDat.reset(IOFactory::readDatafield(datFileName));
    } catch (const std::runtime_error& ex) {
        std::cerr << "File comparison: Could not read data from file " << datFileName
                  << ". Runtime error: " << ex.what() << std::endl;
    } catch (const std::exception& ex) {
        std::cerr << "File comparison: Could not read data from file " << datFileName
                  << ". Exception: " << ex.what() << std::endl;
    } catch (...) {
        std::cerr << "File comparison: Could not read data from file " << datFileName
                  << ". Unknown exception." << std::endl;
        return false;
    }
    ASSERT(datDat);

    std::unique_ptr<Datafield> refDat;
    try {
        refDat.reset(IOFactory::readDatafield(refFileName));
    } catch (...) {
        std::cerr << "File comparison: Could not read reference data from file " << refFileName
                  << std::endl;
        return false;
    }
    ASSERT(refDat);

    return DiffUtil::checkRelativeDifference(datDat->flatVector(), refDat->flatVector(), tol);
}