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