Loading src/tools/field_parser.h +38 −22 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ #include <algorithm> #include <cmath> #include <filesystem> #include <fstream> #include <iostream> #include <map> Loading Loading @@ -180,35 +181,48 @@ namespace allpix { * @param units Optional units to convert the field from after reading from file. Only used by some formats. * @return Field data object read from file or internal cache * * @throws std::runtime_error if the file format is unknown or invalid field dimensions are detected * @throws std::filesystem::filesystem_error if the provided path does not exist * * The type of the field data file to be read is deducted automatically from the file content */ FieldData<T> getByFileName(const std::string& file_name, const std::string& units = std::string()) { // Search in cache (NOTE: the path reached here is always a canonical name) auto iter = field_map_.find(file_name); FieldData<T> getByFileName(const std::filesystem::path& file_name, const std::string& units = std::string()) { auto path = std::filesystem::canonical(file_name); // Search in cache auto iter = field_map_.find(path); if(iter != field_map_.end()) { LOG(INFO) << "Using cached field data"; return iter->second; } // Deduce the file format auto file_type = guess_file_type(file_name); auto file_type = guess_file_type(path); LOG(DEBUG) << "Assuming file type \"" << (file_type == FileType::APF ? "APF" : "INIT") << "\""; FieldData<T> field_data; switch(file_type) { case FileType::INIT: if(units.empty()) { LOG(WARNING) << "No field units provided, interpreting field data in internal units, this might lead to " "unexpected results."; } return parse_init_file(file_name, units); field_data = parse_init_file(path, units); break; case FileType::APF: if(!units.empty()) { LOG(DEBUG) << "Units will be ignored, APF file content is interpreted in internal units."; } return parse_apf_file(file_name); field_data = parse_apf_file(path); break; default: throw std::runtime_error("unknown file format"); } // Store the parsed field data for further reference: field_map_[path] = field_data; return field_data; } private: Loading @@ -220,7 +234,7 @@ namespace allpix { * This helper function checks the first 256 characters of a file for the occurrence of a nullbyte. * For binary files it is very unlikely not to have at least one. This approach is also used e.g. by diff */ bool file_is_binary(const std::string& path) const { bool file_is_binary(const std::filesystem::path& path) const { std::ifstream file(path); for(size_t i = 0; i < 256; i++) { if(file.get() == '\0') { Loading @@ -237,7 +251,7 @@ namespace allpix { * * This function checks if the file contains binary data to interpret it as APF formator INIT format otherwise. */ FileType guess_file_type(const std::string& path) const { FileType guess_file_type(const std::filesystem::path& path) const { return (file_is_binary(path) ? FileType::APF : FileType::INIT); } Loading @@ -247,9 +261,9 @@ namespace allpix { * itself as well as the field size. * @param file_name File name (as canonical path) of the input file to be parsed */ FieldData<T> parse_apf_file(const std::string& file_name) { FieldData<T> parse_apf_file(const std::filesystem::path& file_name) { std::ifstream file(file_name, std::ios::binary); FieldData<double> field_data; FieldData<T> field_data; // Parse the file with cereal, scope ensures flushing: try { Loading Loading @@ -298,7 +312,7 @@ namespace allpix { * @param file_name File name (as canonical path) of the input file to be parsed * @param units Units to convert the values of the field data from */ FieldData<T> parse_init_file(const std::string& file_name, const std::string& units) { FieldData<T> parse_init_file(const std::filesystem::path& file_name, const std::string& units) { // Load file std::ifstream file(file_name); std::string header; Loading Loading @@ -362,16 +376,12 @@ namespace allpix { } LOG_PROGRESS(INFO, "read_init") << "Reading field data: finished."; FieldData<T> field_data( return FieldData<T>( header, std::array<size_t, 3>{{xsize, ysize, zsize}}, std::array<T, 3>{{xpixsz, ypixsz, thickness}}, field); // Store the parsed field data for further reference: field_map_[file_name] = field_data; return field_data; } size_t N_; std::map<std::string, FieldData<T>> field_map_; std::map<std::filesystem::path, FieldData<T>> field_map_; }; /** Loading Loading @@ -399,11 +409,16 @@ namespace allpix { * @param file_name File name (as canonical path) of the output file to be created * @param file_type Type of file (file format) to be produced * @param units Optional units to convert the field into before writing. Only used by some formats. * @throws std::runtime_error if the file format is unknown or invalid field dimensions are detected * @throws std::filesystem::filesystem_error if the provided path does not exist */ void writeFile(const FieldData<T>& field_data, const std::string& file_name, const std::filesystem::path& file_name, const FileType& file_type, const std::string& units = std::string()) { auto path = std::filesystem::weakly_canonical(file_name); auto dimensions = field_data.getDimensions(); if(field_data.getData()->size() != N_ * dimensions[0] * dimensions[1] * dimensions[2]) { throw std::runtime_error("invalid field dimensions"); Loading @@ -414,13 +429,13 @@ namespace allpix { if(units.empty()) { LOG(WARNING) << "No field units provided, writing field data in internal units."; } write_init_file(field_data, file_name, units); write_init_file(field_data, path, units); break; case FileType::APF: if(!units.empty()) { LOG(WARNING) << "Units will be ignored, APF file content is written in internal units."; } write_apf_file(field_data, file_name); write_apf_file(field_data, path); break; default: throw std::runtime_error("unknown file format"); Loading @@ -435,7 +450,7 @@ namespace allpix { * @param field_data Field data object to store * @param file_name File name (as canonical path) of the output file to be created */ void write_apf_file(const FieldData<T>& field_data, const std::string& file_name) { void write_apf_file(const FieldData<T>& field_data, const std::filesystem::path& file_name) { std::ofstream file(file_name, std::ios::binary); // Write the file with cereal: Loading @@ -455,7 +470,8 @@ namespace allpix { * @param file_name File name (as canonical path) of the output file to be created * @param units Units to convert the values of the field data to. */ void write_init_file(const FieldData<T>& field_data, const std::string& file_name, const std::string& units) { void write_init_file(const FieldData<T>& field_data, const std::filesystem::path& file_name, const std::string& units) { std::ofstream file(file_name); LOG(TRACE) << "Writing INIT file \"" << file_name << "\""; Loading Loading
src/tools/field_parser.h +38 −22 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ #include <algorithm> #include <cmath> #include <filesystem> #include <fstream> #include <iostream> #include <map> Loading Loading @@ -180,35 +181,48 @@ namespace allpix { * @param units Optional units to convert the field from after reading from file. Only used by some formats. * @return Field data object read from file or internal cache * * @throws std::runtime_error if the file format is unknown or invalid field dimensions are detected * @throws std::filesystem::filesystem_error if the provided path does not exist * * The type of the field data file to be read is deducted automatically from the file content */ FieldData<T> getByFileName(const std::string& file_name, const std::string& units = std::string()) { // Search in cache (NOTE: the path reached here is always a canonical name) auto iter = field_map_.find(file_name); FieldData<T> getByFileName(const std::filesystem::path& file_name, const std::string& units = std::string()) { auto path = std::filesystem::canonical(file_name); // Search in cache auto iter = field_map_.find(path); if(iter != field_map_.end()) { LOG(INFO) << "Using cached field data"; return iter->second; } // Deduce the file format auto file_type = guess_file_type(file_name); auto file_type = guess_file_type(path); LOG(DEBUG) << "Assuming file type \"" << (file_type == FileType::APF ? "APF" : "INIT") << "\""; FieldData<T> field_data; switch(file_type) { case FileType::INIT: if(units.empty()) { LOG(WARNING) << "No field units provided, interpreting field data in internal units, this might lead to " "unexpected results."; } return parse_init_file(file_name, units); field_data = parse_init_file(path, units); break; case FileType::APF: if(!units.empty()) { LOG(DEBUG) << "Units will be ignored, APF file content is interpreted in internal units."; } return parse_apf_file(file_name); field_data = parse_apf_file(path); break; default: throw std::runtime_error("unknown file format"); } // Store the parsed field data for further reference: field_map_[path] = field_data; return field_data; } private: Loading @@ -220,7 +234,7 @@ namespace allpix { * This helper function checks the first 256 characters of a file for the occurrence of a nullbyte. * For binary files it is very unlikely not to have at least one. This approach is also used e.g. by diff */ bool file_is_binary(const std::string& path) const { bool file_is_binary(const std::filesystem::path& path) const { std::ifstream file(path); for(size_t i = 0; i < 256; i++) { if(file.get() == '\0') { Loading @@ -237,7 +251,7 @@ namespace allpix { * * This function checks if the file contains binary data to interpret it as APF formator INIT format otherwise. */ FileType guess_file_type(const std::string& path) const { FileType guess_file_type(const std::filesystem::path& path) const { return (file_is_binary(path) ? FileType::APF : FileType::INIT); } Loading @@ -247,9 +261,9 @@ namespace allpix { * itself as well as the field size. * @param file_name File name (as canonical path) of the input file to be parsed */ FieldData<T> parse_apf_file(const std::string& file_name) { FieldData<T> parse_apf_file(const std::filesystem::path& file_name) { std::ifstream file(file_name, std::ios::binary); FieldData<double> field_data; FieldData<T> field_data; // Parse the file with cereal, scope ensures flushing: try { Loading Loading @@ -298,7 +312,7 @@ namespace allpix { * @param file_name File name (as canonical path) of the input file to be parsed * @param units Units to convert the values of the field data from */ FieldData<T> parse_init_file(const std::string& file_name, const std::string& units) { FieldData<T> parse_init_file(const std::filesystem::path& file_name, const std::string& units) { // Load file std::ifstream file(file_name); std::string header; Loading Loading @@ -362,16 +376,12 @@ namespace allpix { } LOG_PROGRESS(INFO, "read_init") << "Reading field data: finished."; FieldData<T> field_data( return FieldData<T>( header, std::array<size_t, 3>{{xsize, ysize, zsize}}, std::array<T, 3>{{xpixsz, ypixsz, thickness}}, field); // Store the parsed field data for further reference: field_map_[file_name] = field_data; return field_data; } size_t N_; std::map<std::string, FieldData<T>> field_map_; std::map<std::filesystem::path, FieldData<T>> field_map_; }; /** Loading Loading @@ -399,11 +409,16 @@ namespace allpix { * @param file_name File name (as canonical path) of the output file to be created * @param file_type Type of file (file format) to be produced * @param units Optional units to convert the field into before writing. Only used by some formats. * @throws std::runtime_error if the file format is unknown or invalid field dimensions are detected * @throws std::filesystem::filesystem_error if the provided path does not exist */ void writeFile(const FieldData<T>& field_data, const std::string& file_name, const std::filesystem::path& file_name, const FileType& file_type, const std::string& units = std::string()) { auto path = std::filesystem::weakly_canonical(file_name); auto dimensions = field_data.getDimensions(); if(field_data.getData()->size() != N_ * dimensions[0] * dimensions[1] * dimensions[2]) { throw std::runtime_error("invalid field dimensions"); Loading @@ -414,13 +429,13 @@ namespace allpix { if(units.empty()) { LOG(WARNING) << "No field units provided, writing field data in internal units."; } write_init_file(field_data, file_name, units); write_init_file(field_data, path, units); break; case FileType::APF: if(!units.empty()) { LOG(WARNING) << "Units will be ignored, APF file content is written in internal units."; } write_apf_file(field_data, file_name); write_apf_file(field_data, path); break; default: throw std::runtime_error("unknown file format"); Loading @@ -435,7 +450,7 @@ namespace allpix { * @param field_data Field data object to store * @param file_name File name (as canonical path) of the output file to be created */ void write_apf_file(const FieldData<T>& field_data, const std::string& file_name) { void write_apf_file(const FieldData<T>& field_data, const std::filesystem::path& file_name) { std::ofstream file(file_name, std::ios::binary); // Write the file with cereal: Loading @@ -455,7 +470,8 @@ namespace allpix { * @param file_name File name (as canonical path) of the output file to be created * @param units Units to convert the values of the field data to. */ void write_init_file(const FieldData<T>& field_data, const std::string& file_name, const std::string& units) { void write_init_file(const FieldData<T>& field_data, const std::filesystem::path& file_name, const std::string& units) { std::ofstream file(file_name); LOG(TRACE) << "Writing INIT file \"" << file_name << "\""; Loading