OGS
FileTools.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include "FileTools.h"
5
6#include <boost/algorithm/string/predicate.hpp>
7#include <boost/endian/conversion.hpp>
8#include <boost/interprocess/file_mapping.hpp>
9#include <boost/interprocess/mapped_region.hpp>
10#include <filesystem>
11#include <fstream>
12#include <typeindex>
13#include <unordered_map>
14
15#include "BaseLib/Logging.h"
16#include "Error.h"
17
18namespace BaseLib
19{
23bool IsFileExisting(const std::string& strFilename)
24{
25 return std::filesystem::exists(std::filesystem::path(strFilename));
26}
27
28std::tuple<std::string, std::string::size_type, std::string::size_type>
29getParenthesizedString(std::string const& in,
30 char const open_char,
31 char const close_char,
32 std::string::size_type pos)
33{
34 auto const pos_curly_brace_open = in.find_first_of(open_char, pos);
35 if (pos_curly_brace_open == std::string::npos)
36 {
37 return std::make_tuple("", std::string::npos, std::string::npos);
38 }
39 auto const pos_curly_brace_close =
40 in.find_first_of(close_char, pos_curly_brace_open);
41 if (pos_curly_brace_close == std::string::npos)
42 {
43 return std::make_tuple("", std::string::npos, std::string::npos);
44 }
45 return std::make_tuple(
46 in.substr(pos_curly_brace_open + 1,
47 pos_curly_brace_close - (pos_curly_brace_open + 1)),
48 pos_curly_brace_open, pos_curly_brace_close);
49}
50
51std::string containsKeyword(std::string const& str, std::string const& keyword)
52{
53 auto const position = str.find(keyword);
54 if (position != std::string::npos)
55 {
56 return str.substr(0, position);
57 }
58 return "";
59}
60
61template <typename T>
62bool substituteKeyword(std::string& result,
63 std::string const& parenthesized_string,
64 std::string::size_type const begin,
65 std::string::size_type const end,
66 std::string const& keyword, T& data)
67{
68 std::string precision_specification =
69 containsKeyword(parenthesized_string, keyword);
70
71 if (precision_specification.empty())
72 {
73 return false;
74 }
75
76 if constexpr (std::is_same_v<std::remove_cvref_t<T>, bool>)
77 {
78 if (keyword == "converged")
79 {
80 std::string r = data ? "" : "_not_converged";
81 result.replace(begin, end - begin + 1, r);
82 return true;
83 }
84 }
85
86 std::unordered_map<std::type_index, char> type_specification;
87 type_specification[std::type_index(typeid(int))] = 'd';
88 type_specification[std::type_index(typeid(double))] = 'f'; // default
89 type_specification[std::type_index(typeid(std::string))] = 's';
90
91 auto const& b = precision_specification.back();
92 // see https://fmt.dev/latest/syntax/#format-specification-mini-language
93 if (b == 'e' || b == 'E' || b == 'f' || b == 'F' || b == 'g' || b == 'G')
94 {
95 type_specification[std::type_index(typeid(double))] = b;
96 precision_specification.pop_back();
97 }
98
99 std::string const generated_fmt_string =
100 "{" + precision_specification +
101 type_specification[std::type_index(typeid(data))] + "}";
102 result.replace(
103 begin, end - begin + 1,
104 fmt::vformat(generated_fmt_string, fmt::make_format_args(data)));
105
106 return true;
107}
108
109std::string constructFormattedFileName(std::string const& format_specification,
110 std::string const& mesh_name,
111 int const timestep,
112 double const t,
113 int const iteration,
114 bool const converged)
115{
116 char const open_char = '{';
117 char const close_char = '}';
118 std::string::size_type begin = 0;
119 std::string::size_type end = std::string::npos;
120 std::string result = format_specification;
121
122 while (begin != std::string::npos)
123 {
124 auto length_before_substitution = result.length();
125 // find next parenthesized string
126 std::string str = "";
127 std::tie(str, begin, end) =
128 getParenthesizedString(result, open_char, close_char, begin);
129 if (!substituteKeyword(result, str, begin, end, "timestep", timestep) &&
130 !substituteKeyword(result, str, begin, end, "time", t) &&
131 !substituteKeyword(result, str, begin, end, "iteration",
132 iteration) &&
133 !substituteKeyword(result, str, begin, end, "converged", converged))
134 {
135 substituteKeyword(result, str, begin, end, "meshname", mesh_name);
136 }
137 begin = end - (length_before_substitution - result.length());
138 }
139
140 return result;
141}
142
143double swapEndianness(double const& v)
144{
145 union
146 {
147 double v;
148 char c[sizeof(double)];
149 } a{}, b{};
150
151 a.v = v;
152 for (unsigned short i = 0; i < sizeof(double) / 2; i++)
153 {
154 b.c[i] = a.c[sizeof(double) / 2 - i - 1];
155 }
156
157 for (unsigned short i = sizeof(double) / 2; i < sizeof(double); i++)
158 {
159 b.c[i] = a.c[sizeof(double) + sizeof(double) / 2 - i - 1];
160 }
161
162 return b.v;
163}
164
165std::string dropFileExtension(std::string const& filename)
166{
167 auto const filename_path = std::filesystem::path(filename);
168 return (filename_path.parent_path() / filename_path.stem()).string();
169}
170
171std::string extractBaseName(std::string const& pathname)
172{
173 return std::filesystem::path(pathname).filename().string();
174}
175
176std::string extractBaseNameWithoutExtension(std::string const& pathname)
177{
178 std::string basename = extractBaseName(pathname);
179 return dropFileExtension(basename);
180}
181
182std::string getFileExtension(const std::string& path)
183{
184 return std::filesystem::path(path).extension().string();
185}
186
187bool hasFileExtension(std::string const& extension, std::string const& filename)
188{
189 return boost::iequals(extension, getFileExtension(filename));
190}
191
192std::string extractPath(std::string const& pathname)
193{
194 return std::filesystem::path(pathname).parent_path().string();
195}
196
197std::string joinPaths(std::string const& pathA, std::string const& pathB)
198{
199 return (std::filesystem::path(pathA) /= std::filesystem::path(pathB))
200 .string();
201}
202
203void removeFile(std::string const& filename)
204{
205 bool const success =
206 std::filesystem::remove(std::filesystem::path(filename));
207 if (success)
208 {
209 DBUG("Removed '{:s}'", filename);
210 }
211}
212
213void removeFiles(std::vector<std::string> const& files)
214{
215 for (auto const& file : files)
216 {
217 removeFile(file);
218 }
219}
220
221bool createOutputDirectory(std::string const& dir)
222{
223 if (dir.empty())
224 {
225 return false;
226 }
227
228 std::error_code mkdir_err;
229 if (std::filesystem::create_directories(dir, mkdir_err))
230 {
231 INFO("Output directory {:s} created.", dir);
232 }
233 else if (mkdir_err.value() != 0)
234 {
235 WARN("Could not create output directory {:s}. Error code {:d}, {:s}",
236 dir, mkdir_err.value(), mkdir_err.message());
237 return false;
238 }
239 return true;
240}
241
242std::vector<double> readDoublesFromBinaryFile(
243 const std::string& filename, const std::string& project_directory)
244{
245 std::string path_to_file = BaseLib::joinPaths(project_directory, filename);
246 std::string file_extension = BaseLib::getFileExtension(filename);
247 if (file_extension != ".bin")
248 {
249 OGS_FATAL(
250 "Currently only binary files with extension '.bin' supported. The "
251 "specified file has extension {:s}.",
252 file_extension)
253 }
254 return BaseLib::readBinaryVector<double>(path_to_file);
255}
256
257template <typename T>
258T readBinaryValue(std::istream& in)
259{
260 T v;
261 in.read(reinterpret_cast<char*>(&v), sizeof(T));
262 return v;
263}
264
265// explicit template instantiation
266template float readBinaryValue<float>(std::istream&);
267template double readBinaryValue<double>(std::istream&);
268
269template <typename T>
270std::vector<T> readBinaryVector(std::string const& filename,
271 std::size_t const start_element,
272 std::size_t const num_elements)
273{
274 if (!IsFileExisting(filename))
275 {
276 OGS_FATAL("File {:s} not found", filename);
277 }
278
279 // Determine file size
280 std::uintmax_t file_size = std::filesystem::file_size(filename);
281 std::size_t total_elements = file_size / sizeof(T);
282
283 if (start_element >= total_elements)
284 {
285 OGS_FATAL("Start element is beyond file size");
286 }
287
288 // Calculate the number of elements to read
289 std::size_t const elements_to_read =
290 std::min(num_elements, total_elements - start_element);
291
292 // Calculate offset and size to map
293 std::size_t const offset = start_element * sizeof(T);
294 std::size_t const size_to_map = elements_to_read * sizeof(T);
295
296 // Create a file mapping
297 boost::interprocess::file_mapping file(filename.c_str(),
298 boost::interprocess::read_only);
299
300 // Map the specified region
301 boost::interprocess::mapped_region region(
302 file, boost::interprocess::read_only, offset, size_to_map);
303
304 // Get the address of the mapped region
305 auto* addr = region.get_address();
306
307 // Create vector and copy data
308 std::vector<T> result(elements_to_read);
309 std::memcpy(result.data(), addr, size_to_map);
310
311 if constexpr (std::endian::native != std::endian::little)
312 {
313 boost::endian::endian_reverse_inplace(result);
314 }
315
316 return result;
317}
318
319// explicit template instantiation
320template std::vector<float> readBinaryVector<float>(std::string const&,
321 std::size_t const,
322 std::size_t const);
323template std::vector<double> readBinaryVector<double>(std::string const&,
324 std::size_t const,
325 std::size_t const);
326
327template <typename T>
328void writeValueBinary(std::ostream& out, T const& val)
329{
330 out.write(reinterpret_cast<const char*>(&val), sizeof(T));
331}
332
333// explicit template instantiation
334template void writeValueBinary<std::size_t>(std::ostream&, std::size_t const&);
335
336} // end namespace BaseLib
#define OGS_FATAL(...)
Definition Error.h:19
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
std::string containsKeyword(std::string const &str, std::string const &keyword)
Definition FileTools.cpp:51
void removeFile(std::string const &filename)
std::vector< T > readBinaryVector(std::string const &filename, std::size_t const start_element, std::size_t const num_elements)
std::string constructFormattedFileName(std::string const &format_specification, std::string const &mesh_name, int const timestep, double const t, int const iteration, bool const converged)
std::string getFileExtension(const std::string &path)
void writeValueBinary(std::ostream &out, T const &val)
write value as binary into the given output stream
std::string extractPath(std::string const &pathname)
std::tuple< std::string, std::string::size_type, std::string::size_type > getParenthesizedString(std::string const &in, char const open_char, char const close_char, std::string::size_type pos)
Definition FileTools.cpp:29
T readBinaryValue(std::istream &in)
bool IsFileExisting(const std::string &strFilename)
Returns true if given file exists.
Definition FileTools.cpp:23
template void writeValueBinary< std::size_t >(std::ostream &, std::size_t const &)
template std::vector< double > readBinaryVector< double >(std::string const &, std::size_t const, std::size_t const)
template float readBinaryValue< float >(std::istream &)
std::string extractBaseNameWithoutExtension(std::string const &pathname)
std::string dropFileExtension(std::string const &filename)
std::string joinPaths(std::string const &pathA, std::string const &pathB)
template double readBinaryValue< double >(std::istream &)
std::string extractBaseName(std::string const &pathname)
double swapEndianness(double const &v)
bool createOutputDirectory(std::string const &dir)
bool substituteKeyword(std::string &result, std::string const &parenthesized_string, std::string::size_type const begin, std::string::size_type const end, std::string const &keyword, T &data)
Definition FileTools.cpp:62
std::vector< double > readDoublesFromBinaryFile(const std::string &filename, const std::string &project_directory)
bool hasFileExtension(std::string const &extension, std::string const &filename)
void removeFiles(std::vector< std::string > const &files)
template std::vector< float > readBinaryVector< float >(std::string const &, std::size_t const, std::size_t const)