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