Merge pull request #36 from slsdetectorgroup/file-tests

added first tests for reading files
This commit is contained in:
Bechir Braham 2024-03-28 15:25:57 +01:00 committed by GitHub
commit 643ea39670
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 311 additions and 178 deletions

View File

@ -68,6 +68,16 @@ template <typename T, ssize_t Ndim=2> class NDView {
T &operator()(ssize_t i) { return buffer_[i]; }
T &operator[](ssize_t i) { return buffer_[i]; }
bool operator==(const NDView &other) const {
if (size_ != other.size_)
return false;
for (ssize_t i = 0; i != size_; ++i) {
if (buffer_[i] != other.buffer_[i])
return false;
}
return true;
}
NDView &operator+=(const T val) { return elemenwise(val, std::plus<T>()); }
NDView &operator-=(const T val) { return elemenwise(val, std::minus<T>()); }
NDView &operator*=(const T val) { return elemenwise(val, std::multiplies<T>()); }

View File

@ -1,116 +1,116 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_all.hpp>
#include "aare/CircularFifo.hpp"
#include "aare/CircularFifo.hpp"
using aare::CircularFifo;
using aare::CircularFifo;
// Only for testing. To make sure we can avoid copy constructor
// and copy assignment
// Only for testing. To make sure we can avoid copy constructor
// and copy assignment
struct MoveOnlyInt {
int value{};
struct MoveOnlyInt {
int value{};
MoveOnlyInt() = default;
MoveOnlyInt(int i) : value(i){};
MoveOnlyInt(const MoveOnlyInt &) = delete;
MoveOnlyInt &operator=(const MoveOnlyInt &) = delete;
MoveOnlyInt(MoveOnlyInt &&other) { std::swap(value, other.value); }
MoveOnlyInt &operator=(MoveOnlyInt &&other) {
std::swap(value, other.value);
return *this;
}
bool operator==(int other) const { return value == other; }
};
MoveOnlyInt() = default;
MoveOnlyInt(int i) : value(i){};
MoveOnlyInt(const MoveOnlyInt &) = delete;
MoveOnlyInt &operator=(const MoveOnlyInt &) = delete;
MoveOnlyInt(MoveOnlyInt &&other) { std::swap(value, other.value); }
MoveOnlyInt &operator=(MoveOnlyInt &&other) {
std::swap(value, other.value);
return *this;
}
bool operator==(int other) const { return value == other; }
};
TEST_CASE("CircularFifo can be default constructed") { CircularFifo<MoveOnlyInt> f; }
TEST_CASE("CircularFifo can be default constructed") { CircularFifo<MoveOnlyInt> f; }
TEST_CASE("Newly constructed fifo has the right size") {
size_t size = 17;
CircularFifo<MoveOnlyInt> f(size);
CHECK(f.numFreeSlots() == size);
CHECK(f.numFilledSlots() == 0);
}
TEST_CASE("Newly constructed fifo has the right size") {
size_t size = 17;
CircularFifo<MoveOnlyInt> f(size);
CHECK(f.numFreeSlots() == size);
CHECK(f.numFilledSlots() == 0);
}
TEST_CASE("Can fit size number of objects") {
size_t size = 8;
size_t numPushedItems = 0;
CircularFifo<MoveOnlyInt> f(size);
for (size_t i = 0; i < size; ++i) {
MoveOnlyInt a;
bool popped = f.try_pop_free(a);
CHECK(popped);
if (popped) {
a.value = i;
bool pushed = f.try_push_value(std::move(a));
CHECK(pushed);
if (pushed)
numPushedItems++;
}
}
CHECK(f.numFreeSlots() == 0);
CHECK(f.numFilledSlots() == size);
CHECK(numPushedItems == size);
}
TEST_CASE("Can fit size number of objects") {
size_t size = 8;
size_t numPushedItems = 0;
CircularFifo<MoveOnlyInt> f(size);
for (size_t i = 0; i < size; ++i) {
MoveOnlyInt a;
bool popped = f.try_pop_free(a);
CHECK(popped);
if (popped) {
a.value = i;
bool pushed = f.try_push_value(std::move(a));
CHECK(pushed);
if (pushed)
numPushedItems++;
}
}
CHECK(f.numFreeSlots() == 0);
CHECK(f.numFilledSlots() == size);
CHECK(numPushedItems == size);
}
TEST_CASE("Push move only type") {
CircularFifo<MoveOnlyInt> f;
f.push_value(5);
}
TEST_CASE("Push move only type") {
CircularFifo<MoveOnlyInt> f;
f.push_value(5);
}
TEST_CASE("Push pop") {
CircularFifo<MoveOnlyInt> f;
f.push_value(MoveOnlyInt(1));
TEST_CASE("Push pop") {
CircularFifo<MoveOnlyInt> f;
f.push_value(MoveOnlyInt(1));
auto a = f.pop_value();
CHECK(a == 1);
}
auto a = f.pop_value();
CHECK(a == 1);
}
TEST_CASE("Pop free and then push") {
CircularFifo<MoveOnlyInt> f;
TEST_CASE("Pop free and then push") {
CircularFifo<MoveOnlyInt> f;
auto a = f.pop_free();
a.value = 5;
f.push_value(std::move(a)); // Explicit move since we can't copy
auto b = f.pop_value();
auto a = f.pop_free();
a.value = 5;
f.push_value(std::move(a)); // Explicit move since we can't copy
auto b = f.pop_value();
CHECK(a == 0); // Moved from value
CHECK(b == 5); // Original value
}
CHECK(a == 0); // Moved from value
CHECK(b == 5); // Original value
}
TEST_CASE("Skip the first value") {
CircularFifo<MoveOnlyInt> f;
TEST_CASE("Skip the first value") {
CircularFifo<MoveOnlyInt> f;
for (int i = 0; i != 10; ++i) {
auto a = f.pop_free();
a.value = i + 1;
f.push_value(std::move(a)); // Explicit move since we can't copy
}
for (int i = 0; i != 10; ++i) {
auto a = f.pop_free();
a.value = i + 1;
f.push_value(std::move(a)); // Explicit move since we can't copy
}
auto b = f.pop_value();
CHECK(b == 1);
f.next();
auto c = f.pop_value();
CHECK(c == 3);
}
auto b = f.pop_value();
CHECK(b == 1);
f.next();
auto c = f.pop_value();
CHECK(c == 3);
}
TEST_CASE("Use in place and move to free") {
size_t size = 18;
CircularFifo<MoveOnlyInt> f(size);
TEST_CASE("Use in place and move to free") {
size_t size = 18;
CircularFifo<MoveOnlyInt> f(size);
//Push 10 values to the fifo
for (int i = 0; i != 10; ++i) {
auto a = f.pop_free();
a.value = i + 1;
f.push_value(std::move(a)); // Explicit move since we can't copy
}
//Push 10 values to the fifo
for (int i = 0; i != 10; ++i) {
auto a = f.pop_free();
a.value = i + 1;
f.push_value(std::move(a)); // Explicit move since we can't copy
}
auto b = f.frontPtr();
CHECK(*b == 1);
CHECK(f.numFilledSlots() == 10);
CHECK(f.numFreeSlots() == size-10);
f.next();
auto c = f.frontPtr();
CHECK(*c == 2);
CHECK(f.numFilledSlots() == 9);
CHECK(f.numFreeSlots() == size-9);
}
auto b = f.frontPtr();
CHECK(*b == 1);
CHECK(f.numFilledSlots() == 10);
CHECK(f.numFreeSlots() == size-10);
f.next();
auto c = f.frontPtr();
CHECK(*c == 2);
CHECK(f.numFilledSlots() == 9);
CHECK(f.numFreeSlots() == size-9);
}

View File

@ -175,4 +175,20 @@ TEST_CASE("Retrieve shape"){
REQUIRE(data.shape()[0] == 3);
REQUIRE(data.shape()[1] == 4);
}
TEST_CASE("compare two views"){
std::vector<int> vec1;
for (int i = 0; i != 12; ++i) {
vec1.push_back(i);
}
NDView<int,2> view1(vec1.data(), Shape<2>{3,4});
std::vector<int> vec2;
for (int i = 0; i != 12; ++i) {
vec2.push_back(i);
}
NDView<int,2> view2(vec2.data(), Shape<2>{3,4});
REQUIRE(view1 == view2);
}

View File

@ -1,49 +1,49 @@
#include <catch2/catch_all.hpp>
#include "aare/ProducerConsumerQueue.hpp"
#include <catch2/catch_all.hpp>
#include "aare/ProducerConsumerQueue.hpp"
// using arve::SimpleQueue;
TEST_CASE("push pop"){
// using arve::SimpleQueue;
TEST_CASE("push pop"){
folly::ProducerConsumerQueue<int> q(5);
int a = 3;
int b = 8;
CHECK(q.sizeGuess() == 0);
CHECK(q.write(a));
CHECK(q.sizeGuess() == 1);
CHECK(q.write(b));
CHECK(q.sizeGuess() == 2);
int c = 0;
folly::ProducerConsumerQueue<int> q(5);
int a = 3;
int b = 8;
CHECK(q.sizeGuess() == 0);
CHECK(q.write(a));
CHECK(q.sizeGuess() == 1);
CHECK(q.write(b));
CHECK(q.sizeGuess() == 2);
int c = 0;
CHECK(q.read(c));
CHECK(c == 3);
CHECK(q.sizeGuess() == 1);
CHECK(q.read(c));
CHECK(c == 8);
CHECK(q.sizeGuess() == 0);
}
CHECK(q.read(c));
CHECK(c == 3);
CHECK(q.sizeGuess() == 1);
CHECK(q.read(c));
CHECK(c == 8);
CHECK(q.sizeGuess() == 0);
}
TEST_CASE("Cannot push to a full queue"){
folly::ProducerConsumerQueue<int> q(3);
int a = 3;
int b = 4;
int c = 0;
CHECK(q.write(a));
CHECK(q.write(b));
CHECK_FALSE(q.write(a));
TEST_CASE("Cannot push to a full queue"){
folly::ProducerConsumerQueue<int> q(3);
int a = 3;
int b = 4;
int c = 0;
CHECK(q.write(a));
CHECK(q.write(b));
CHECK_FALSE(q.write(a));
//values are still ok
CHECK(q.read(c));
CHECK(c == 3);
CHECK(q.read(c));
CHECK(c == 4);
}
//values are still ok
CHECK(q.read(c));
CHECK(c == 3);
CHECK(q.read(c));
CHECK(c == 4);
}
TEST_CASE("Cannot pop from an empty queue"){
folly::ProducerConsumerQueue<int> q(2);
int a=0;
CHECK_FALSE(q.read(a));
}
TEST_CASE("Cannot pop from an empty queue"){
folly::ProducerConsumerQueue<int> q(2);
int a=0;
CHECK_FALSE(q.read(a));
}
// TEST_CASE("fail"){
// CHECK(false);
// }
// TEST_CASE("fail"){
// CHECK(false);
// }

Binary file not shown.

View File

@ -24,7 +24,7 @@ header_dt = np.dtype(
# Read three frames from a jungfrau file with a single interface
rows = 512
cols = 1024
frames = 3
frames = 10
data = np.zeros((frames,rows,cols), dtype = np.uint16)
header = np.zeros(frames, dtype = header_dt)
@ -32,17 +32,18 @@ for file_id in range(4):
file_name = 'jungfrau_single_d0_f{}_0.raw'.format(file_id)
print("Reading file:", file_name)
with open(file_name) as f:
for i in range(frames if file_id != 3 else 1):
header[i] = np.fromfile(f, dtype=header_dt, count = 1)
data[i] = np.fromfile(f, dtype=np.uint16,count = rows*cols).reshape(rows,cols)
for i in range(3 if file_id != 3 else 1):
header[i+file_id*3] = np.fromfile(f, dtype=header_dt, count = 1)
data[i+file_id*3] = np.fromfile(f, dtype=np.uint16,count = rows*cols).reshape(rows,cols)
for i in range(frames if file_id != 3 else 1 ):
print("frame:",i)
print(data[i][0,0],data[i][0,1],data[i][1,0],data[i][rows-1,cols-1])
print("")
# for i in range(frames if file_id != 3 else 1 ):
# print("frame:",i)
# print(header[i][0,0],data[i][0,1],data[i][1,0],data[i][rows-1,cols-1])
# print("")
print(header[1]["Frame Number"])
#fig, ax = plt.subplots()
#im = ax.imshow(data[0])
#im.set_clim(2000,4000)

View File

@ -0,0 +1,29 @@
import numpy as np
from pathlib import Path
header_dt = np.dtype(
[
("Frame Number", "u8"),
("SubFrame Number/ExpLength", "u4"),
("Packet Number", "u4"),
("Bunch ID", "u8"),
("Timestamp", "u8"),
("Module Id", "u2"),
("Row", "u2"),
("Column", "u2"),
("Reserved", "u2"),
("Debug", "u4"),
("Round Robin Number", "u2"),
("Detector Type", "u1"),
("Header Version", "u1"),
("Packets caught mask", "8u8")
]
)
with open("data/eiger/eiger_500k_16bit_d0_f0_0.raw", "rb") as f:
for i in range(3):
frame_number = np.fromfile(f, dtype=header_dt, count=1)["Frame Number"][0]
print(frame_number)
f.seek(262144,1)

View File

@ -32,7 +32,8 @@ endif()
if(AARE_TESTS)
set(TestSources
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/NumpyHelpers.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/RawFile.test.cpp
)
target_sources(tests PRIVATE ${TestSources} )
target_link_libraries(tests PRIVATE core file_io)

View File

@ -20,9 +20,9 @@ class NumpyFile : public FileInterface {
void seek(size_t frame_number) override { this->current_frame = frame_number; }
size_t tell() override { return this->current_frame; }
size_t total_frames() const override { return header.shape[0]; }
ssize_t rows()const override { return header.shape[1]; }
ssize_t cols()const override { return header.shape[2]; }
ssize_t bitdepth()const override { return header.dtype.itemsize; }
ssize_t rows() const override { return header.shape[1]; }
ssize_t cols() const override { return header.shape[2]; }
ssize_t bitdepth() const override { return header.dtype.itemsize; }
NumpyFile(std::filesystem::path fname);
header_t header{};

View File

@ -2,8 +2,8 @@
#include "aare/defs.hpp"
#include <cstdint>
#include <filesystem>
#include <variant>
#include <map>
#include <variant>
class SubFile {
protected:
@ -19,20 +19,21 @@ class SubFile {
std::map<std::pair<DetectorType, int>, pfunc> read_impl_map = {
{{DetectorType::Moench, 16}, &SubFile::read_impl_reorder<uint16_t>},
{{DetectorType::Jungfrau, 16}, &SubFile::read_impl_normal},
{{DetectorType::ChipTestBoard,16}, &SubFile::read_impl_normal},
{{DetectorType::Mythen3, 32}, &SubFile::read_impl_normal}
};
{{DetectorType::ChipTestBoard, 16}, &SubFile::read_impl_normal},
{{DetectorType::Mythen3, 32}, &SubFile::read_impl_normal},
{{DetectorType::Eiger, 32}, &SubFile::read_impl_normal},
{{DetectorType::Eiger, 16}, &SubFile::read_impl_normal}
};
public:
// pointer to a read_impl function. pointer will be set to the appropriate read_impl function in the constructor
pfunc read_impl = nullptr;
size_t read_impl_normal(std::byte *buffer);
template <typename DataType> size_t read_impl_flip(std::byte *buffer);
template <typename DataType> size_t read_impl_reorder(std::byte *buffer);
template <typename DataType> size_t read_impl_flip(std::byte *buffer);
template <typename DataType> size_t read_impl_reorder(std::byte *buffer);
SubFile(std::filesystem::path fname,DetectorType detector, ssize_t rows, ssize_t cols, uint16_t bitdepth);
SubFile(std::filesystem::path fname, DetectorType detector, ssize_t rows, ssize_t cols, uint16_t bitdepth);
size_t get_part(std::byte *buffer, int frame_number);
size_t frame_number(int frame_index);
@ -40,5 +41,4 @@ class SubFile {
// TODO: define the inlines as variables and assign them in constructor
inline size_t bytes_per_part() { return (m_bitdepth / 8) * m_rows * m_cols; }
inline size_t pixels_per_part() { return m_rows * m_cols; }
};

View File

@ -10,7 +10,7 @@ File::File(std::filesystem::path fname, std::string mode) {
}
Frame File::read() { return file_impl->read(); }
size_t File::total_frames() const { return file_impl->m_total_frames; }
size_t File::total_frames() const { return file_impl->total_frames(); }
std::vector<Frame> File::read(size_t n_frames) { return file_impl->read(n_frames); }
void File::read_into(std::byte *image_buf) { file_impl->read_into(image_buf); }
void File::read_into(std::byte *image_buf, size_t n_frames) { file_impl->read_into(image_buf, n_frames); }

View File

@ -13,16 +13,16 @@ FileFactory *FileFactory::get_factory(std::filesystem::path fpath) {
}
if (fpath.extension() == ".raw" || fpath.extension() == ".json"){
aare::logger::info("Loading",fpath.extension(),"file");
aare::logger::debug("Loading",fpath.extension(),"file");
return new RawFileFactory(fpath);
}
if (fpath.extension() == ".raw" || fpath.extension() == ".json"){
aare::logger::info("Loading",fpath.extension(),"file");
aare::logger::debug("Loading",fpath.extension(),"file");
return new RawFileFactory(fpath);
}
// check if extension is numpy
else if (fpath.extension() == ".npy") {
aare::logger::info("Loading numpy file");
aare::logger::debug("Loading numpy file");
return new NumpyFileFactory(fpath);
}

View File

@ -1,10 +1,8 @@
#include "aare/NumpyFileFactory.hpp"
#include "aare/NumpyHelpers.hpp"
NumpyFileFactory::NumpyFileFactory(std::filesystem::path fpath) {
this->m_fpath = fpath;
}
NumpyFileFactory::NumpyFileFactory(std::filesystem::path fpath) { this->m_fpath = fpath; }
void NumpyFileFactory::parse_metadata(FileInterface *_file) {
auto file = dynamic_cast<NumpyFile*>(_file);
auto file = dynamic_cast<NumpyFile *>(_file);
// open ifsteam to file
f = std::ifstream(file->m_fname, std::ios::binary);
// check if file exists
@ -46,7 +44,7 @@ void NumpyFileFactory::parse_metadata(FileInterface *_file) {
// parse header
std::vector<std::string> keys{"descr", "fortran_order", "shape"};
std::cout << "original header: " << '"' << header << '"' << std::endl;
aare::logger::debug("original header: \"header\"");
auto dict_map = parse_dict(header, keys);
if (dict_map.size() == 0)
@ -72,11 +70,8 @@ void NumpyFileFactory::parse_metadata(FileInterface *_file) {
file->header = {dtype, fortran_order, shape};
}
NumpyFile* NumpyFileFactory::load_file() {
NumpyFile* file = new NumpyFile(this->m_fpath);
NumpyFile *NumpyFileFactory::load_file() {
NumpyFile *file = new NumpyFile(this->m_fpath);
parse_metadata(file);
std::cout << "parsed header: " << file->header.to_string() << std::endl;
return file;
};

View File

@ -87,7 +87,6 @@ void RawFileFactory::parse_json_metadata(RawFile *file) {
json j;
ifs >> j;
double v = j["Version"];
std::cout << "Version: " << v << std::endl;
file->version = fmt::format("{:.1f}", v);
file->m_type = StringTo<DetectorType>(j["Detector Type"].get<std::string>());
file->timing_mode = StringTo<TimingMode>(j["Timing Mode"].get<std::string>());

View File

@ -10,7 +10,8 @@ SubFile::SubFile(std::filesystem::path fname, DetectorType detector, ssize_t row
this->m_bitdepth = bitdepth;
this->n_frames = std::filesystem::file_size(fname) / (sizeof(sls_detector_header) + rows * cols * bitdepth / 8);
if (read_impl_map.find({detector, bitdepth}) == read_impl_map.end()) {
throw std::runtime_error(LOCATION + "Unsupported detector/bitdepth combination");
auto error_msg = LOCATION + "No read_impl function found for detector: " + toString(detector) + " and bitdepth: " + std::to_string(bitdepth);
throw std::runtime_error(error_msg);
}
this->read_impl = read_impl_map.at({detector, bitdepth});
}
@ -88,7 +89,7 @@ size_t SubFile::frame_number(int frame_index) {
FILE *fp = fopen(this->m_fname.c_str(), "r");
if (!fp)
throw std::runtime_error(fmt::format("Could not open: {} for reading", m_fname.c_str()));
fseek(fp, (sizeof(sls_detector_header) + bytes_per_part()) * frame_index, SEEK_SET);
size_t rc = fread(reinterpret_cast<char *>(&h), sizeof(h), 1, fp);
fclose(fp);
if (rc != 1)

View File

@ -0,0 +1,81 @@
#include "aare/File.hpp"
#include "aare/utils/logger.hpp"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include "test_config.hpp"
TEST_CASE("Read number of frames from a jungfrau raw file") {
auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
File f(fpath, "r");
REQUIRE(f.total_frames() == 10);
}
TEST_CASE("Read frame numbers from a jungfrau raw file") {
auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
File f(fpath, "r");
// we know this file has 10 frames with frame numbers 1 to 10
// f0 1,2,3
// f1 4,5,6
// f2 7,8,9
// f3 10
for (size_t i = 0; i < 10; i++) {
CHECK(f.frame_number(i) == i + 1);
}
}
TEST_CASE("Read data from a jungfrau 500k single port raw file") {
auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
File f(fpath, "r");
// we know this file has 10 frames with pixel 0,0 being: 2123, 2051, 2109, 2117, 2089, 2095, 2072, 2126, 2097, 2102
std::vector<uint16_t> pixel_0_0 = {2123, 2051, 2109, 2117, 2089, 2095, 2072, 2126, 2097, 2102};
for (size_t i = 0; i < 10; i++) {
auto frame = f.read();
CHECK(frame.rows() == 512);
CHECK(frame.cols() == 1024);
CHECK(frame.view<uint16_t>()(0, 0) == pixel_0_0[i]);
}
}
TEST_CASE("Read frame numbers from a raw file") {
auto fpath = test_data_path() / "eiger" / "eiger_500k_16bit_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
// we know this file has 3 frames with frame numbers 14, 15, 16
std::vector<size_t> frame_numbers = {14, 15, 16};
File f(fpath, "r");
for (size_t i = 0; i < 3; i++) {
CHECK(f.frame_number(i) == frame_numbers[i]);
}
}
TEST_CASE("Compare reading from a numpy file with a raw file") {
auto fpath_raw = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath_raw));
auto fpath_npy = test_data_path() / "jungfrau" / "jungfrau_single_0.npy";
REQUIRE(std::filesystem::exists(fpath_npy));
File raw(fpath_raw, "r");
File npy(fpath_npy, "r");
CHECK(raw.total_frames() == 10);
CHECK(npy.total_frames() == 10);
for (size_t i = 0; i < 10; ++i) {
auto raw_frame = raw.read();
auto npy_frame = npy.read();
CHECK(raw_frame.view<uint16_t>() == npy_frame.view<uint16_t>());
}
}

View File

@ -32,7 +32,7 @@ target_sources(tests PRIVATE ${TestSources} )
#Work around to remove, this is not the way to do it =)
# target_include_directories(tests PRIVATE ${CMAKE_SOURCE_DIR}/include/common)
target_link_libraries(tests PRIVATE core aare_compiler_flags)
target_link_libraries(tests PRIVATE core aare_compiler_flags utils)
catch_discover_tests(tests
# WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/data

View File

@ -37,7 +37,7 @@ class Logger {
std::streambuf *error_buf = std::cerr.rdbuf();
std::ostream *standard_output;
std::ostream *error_output;
LOGGING_LEVEL VERBOSITY_LEVEL = LOGGING_LEVEL::DEBUG;
LOGGING_LEVEL VERBOSITY_LEVEL = LOGGING_LEVEL::INFO;
std::ofstream out_file;