-
Wuttke, Joachim authoredWuttke, Joachim authored
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);
}