PolyFEM
Loading...
Searching...
No Matches
ResourceIO.cpp
Go to the documentation of this file.
1#include "ResourceIO.hpp"
2#include "InputLoader.hpp"
3
7
8#include <h5pp/h5pp.h>
9
10#include <algorithm>
11#include <chrono>
12#include <fstream>
13#include <limits>
14#include <regex>
15#include <set>
16#include <sstream>
17#include <type_traits>
18
19namespace fs = std::filesystem;
20
21namespace polyfem::io
22{
23 namespace
24 {
25 // Logical paths are POSIX paths, regardless of the host operating system.
26 std::string normalize_logical(const std::string &path)
27 {
28 const bool absolute = !path.empty() && path.front() == '/';
29 std::vector<std::string> components;
30 std::istringstream input(path);
31 std::string component;
32 while (std::getline(input, component, '/'))
33 {
34 if (component.empty() || component == ".")
35 continue;
36 if (component == ".." && !components.empty() && components.back() != "..")
37 components.pop_back();
38 else if (component != ".." || !absolute)
39 components.push_back(component);
40 }
41 std::string result = absolute ? "/" : "";
42 for (const auto &part : components)
43 {
44 if (!result.empty() && result.back() != '/')
45 result += '/';
46 result += part;
47 }
48 return result.empty() ? "." : result;
49 }
50
51 std::string join_logical(const std::string &base, const std::string &path)
52 {
53 return normalize_logical(!path.empty() && path.front() == '/'
54 ? path
55 : (base.empty() ? "/" : base) + "/" + path);
56 }
57
58 bool is_windows_absolute(const std::string &path)
59 {
60 return path.size() >= 3
61 && ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z'))
62 && path[1] == ':' && (path[2] == '/' || path[2] == '\\');
63 }
64
65 std::string windows_separators(std::string path)
66 {
67 std::replace(path.begin(), path.end(), '\\', '/');
68 return path;
69 }
70
71 std::string normalize_windows_absolute(const std::string &path)
72 {
73 const std::string normalized = windows_separators(path);
74 return normalized.substr(0, 2) + normalize_logical(normalized.substr(2));
75 }
76
77 std::string relative_logical(const std::string &path, const std::string &base)
78 {
79 const auto split = [](const std::string &value) {
80 std::vector<std::string> parts;
81 std::istringstream input(value);
82 std::string part;
83 while (std::getline(input, part, '/'))
84 if (!part.empty())
85 parts.push_back(part);
86 return parts;
87 };
88 const auto target = split(path), root = split(base);
89 size_t common = 0;
90 while (common < target.size() && common < root.size() && target[common] == root[common])
91 ++common;
92 std::string result;
93 for (size_t i = common; i < root.size(); ++i)
94 result += "../";
95 for (size_t i = common; i < target.size(); ++i)
96 result += target[i] + "/";
97 if (!result.empty())
98 result.pop_back();
99 return result.empty() ? "." : result;
100 }
101
102 template <typename T>
103 std::vector<T> matrix_vector(const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> &m)
104 {
105 if (m.size() == 0)
106 return {};
107 return std::vector<T>(m.data(), m.data() + m.size());
108 }
109
110 template <typename T>
111 Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> read_file_matrix(const fs::path &path)
112 {
113 Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> result;
114 if (!io::read_matrix(path.string(), result))
115 log_and_throw_error("Unable to parse matrix resource {}", path.string());
116 return result;
117 }
118
119 bool is_file_dataset(const h5pp::DsetInfo &info)
120 {
121 const hid_t type = info.h5Type.value();
122 // The bundle format reserves rank-one uint8 datasets for file bytes.
123 return H5Tget_class(type) == H5T_STRING
124 || (info.dsetRank.value() == 1 && H5Tget_class(type) == H5T_INTEGER
125 && H5Tget_size(type) == 1 && H5Tget_sign(type) == H5T_SGN_NONE);
126 }
127
128 template <typename T>
129 Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> read_numeric_matrix(const h5pp::DsetInfo &info)
130 {
131 const std::string &path = info.dsetPath.value();
132 const auto &dims = info.dsetDims.value();
133 if (dims.size() > 2)
134 log_and_throw_error("Numeric resource {} must be a scalar, vector or matrix.", path);
135 const hsize_t rows = dims.empty() ? 1 : dims[0];
136 const hsize_t cols = dims.size() < 2 ? 1 : dims[1];
137 const hsize_t limit = std::numeric_limits<Eigen::Index>::max();
138 if (rows > limit || cols > limit || (cols && rows > limit / cols))
139 log_and_throw_error("Numeric resource {} is too large.", path);
140 Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> result(rows, cols);
141 const hid_t type = info.h5Type.value();
142 const H5T_class_t kind = H5Tget_class(type);
143 const auto read = [&](auto scalar, const hid_t memory_type) {
144 using Source = decltype(scalar);
145 std::vector<Source> values(result.size());
146 if (!values.empty() && H5Dread(info.h5Dset.value(), memory_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, values.data()) < 0)
147 log_and_throw_error("Unable to read numeric resource {}.", path);
148 for (Eigen::Index r = 0; r < result.rows(); ++r)
149 for (Eigen::Index c = 0; c < result.cols(); ++c)
150 {
151 const Source value = values[r * result.cols() + c];
152 if constexpr (std::is_integral_v<T> && std::is_integral_v<Source>)
153 {
154 if constexpr (std::is_signed_v<Source>)
155 {
156 if (value < std::numeric_limits<T>::lowest() || value > std::numeric_limits<T>::max())
157 log_and_throw_error("Integer value in {} is out of range for the requested type.", path);
158 }
159 else if (value > uint64_t(std::numeric_limits<T>::max()))
160 log_and_throw_error("Integer value in {} is out of range for the requested type.", path);
161 }
162 result(r, c) = static_cast<T>(value);
163 }
164 };
165 if (kind == H5T_INTEGER && H5Tget_size(type) <= sizeof(uint64_t))
166 {
167 if (H5Tget_sign(type) == H5T_SGN_NONE)
168 read(uint64_t(0), H5T_NATIVE_UINT64);
169 else
170 read(int64_t(0), H5T_NATIVE_INT64);
171 }
172 else if (kind == H5T_FLOAT && std::is_floating_point_v<T>)
173 read(double(0), H5T_NATIVE_DOUBLE);
174 else
175 log_and_throw_error("Numeric resource {} has an incompatible datatype.", path);
176 return result;
177 }
178 } // namespace
179
181 {
182 public:
183 mutable std::mutex mutex;
184 std::set<std::string> paths;
185 };
186
187 ResourceIO::ResourceIO() : access_tracker_(std::make_shared<AccessTracker>()) {}
188
189 void ResourceIO::record_access(const std::string &path) const
190 {
191 const std::string canonical = canonical_path(path);
192 std::lock_guard<std::mutex> lock(access_tracker_->mutex);
193 access_tracker_->paths.insert(canonical);
194 }
195
196 std::vector<std::string> ResourceIO::accessed_resources() const
197 {
198 std::lock_guard<std::mutex> lock(access_tracker_->mutex);
199 return {access_tracker_->paths.begin(), access_tracker_->paths.end()};
200 }
201
202 std::vector<std::string> ResourceIO::glob(const std::string &input_pattern) const
203 {
204 const std::string pattern = normalize_logical(input_pattern);
205 if (pattern.find_first_of("*?") == std::string::npos)
206 return exists(pattern) ? std::vector<std::string>{pattern} : std::vector<std::string>{};
207 const bool recursive = pattern.find("**") != std::string::npos;
208 const auto depth = std::count(pattern.begin(), pattern.end(), '/');
209 std::string expression;
210 expression.reserve(pattern.size() * 2);
211 for (size_t i = 0; i < pattern.size(); ++i)
212 {
213 const char c = pattern[i];
214 if (c == '*' && i + 1 < pattern.size() && pattern[i + 1] == '*')
215 {
216 ++i;
217 if (i + 1 < pattern.size() && pattern[i + 1] == '/')
218 {
219 expression += "(?:[^/]+/)*";
220 ++i;
221 }
222 else
223 expression += ".*";
224 }
225 else if (c == '*')
226 expression += "[^/]*";
227 else if (c == '?')
228 expression += "[^/]";
229 else
230 {
231 if (std::string(".^$|()[]{}+\\").find(c) != std::string::npos)
232 expression += '\\';
233 expression += c;
234 }
235 }
236 const std::regex matcher("^" + expression + "$");
237 const size_t wildcard = pattern.find_first_of("*?");
238 const std::string prefix = wildcard == std::string::npos ? pattern : pattern.substr(0, wildcard);
239 const size_t slash = prefix.rfind('/');
240 const std::string root = slash == std::string::npos ? "." : prefix.substr(0, slash);
241
242 std::vector<std::string> result;
243 std::vector<std::string> pending{root.empty() ? "/" : root};
244 while (!pending.empty())
245 {
246 const std::string current = pending.back();
247 pending.pop_back();
248 for (const std::string &child : list(current))
249 {
250 if (std::regex_match(child, matcher))
251 result.push_back(child);
252 if ((recursive || std::count(child.begin(), child.end(), '/') < depth) && is_group(child))
253 pending.push_back(child);
254 }
255 }
256 std::sort(result.begin(), result.end());
257 return result;
258 }
259
260 std::string ResourceIO::read_string(const std::string &path) const
261 {
262 auto in = open(path, true);
263 return std::string(std::istreambuf_iterator<char>(*in), std::istreambuf_iterator<char>());
264 }
265
266 FileSystemIO::FileSystemIO(const fs::path &root, const fs::path &host_directory)
267 {
268 fs::path candidate = root.empty() ? fs::current_path() : root;
269 std::error_code error;
270 if (fs::exists(candidate, error) && !fs::is_directory(candidate, error))
271 candidate = candidate.parent_path();
272 root_ = fs::absolute(candidate).lexically_normal();
273 host_directory_ = host_directory.empty() ? root_ : fs::absolute(host_directory).lexically_normal();
274 }
275
276 std::unique_ptr<const ResourceIO> FileSystemIO::with_root(const std::string &root) const
277 {
278 auto result = std::make_unique<FileSystemIO>(resolve(root), host_directory_);
279 result->access_tracker_ = access_tracker_;
280 return result;
281 }
282
283 fs::path FileSystemIO::resolve(const std::string &path) const
284 {
285 if (path.empty())
286 return root_;
287 fs::path p(path);
288 return (p.is_absolute() ? p : root_ / p).lexically_normal();
289 }
290
291 bool FileSystemIO::exists(const std::string &path) const { return fs::exists(resolve(path)); }
292 bool FileSystemIO::is_group(const std::string &path) const { return fs::is_directory(resolve(path)); }
293
294 std::vector<std::string> FileSystemIO::list(const std::string &path) const
295 {
296 const fs::path directory = resolve(path);
297 if (!fs::is_directory(directory))
298 return {};
299 std::vector<std::string> result;
300 const bool absolute_input = fs::path(path).is_absolute();
301 for (const fs::directory_entry &entry : fs::directory_iterator(directory))
302 result.push_back(absolute_input ? entry.path().generic_string() : entry.path().lexically_relative(root_).generic_string());
303 std::sort(result.begin(), result.end());
304 return result;
305 }
306
307 std::unique_ptr<std::istream> FileSystemIO::open(const std::string &path, const bool binary) const
308 {
309 record_access(path);
310 auto in = std::make_unique<std::ifstream>(resolve(path), std::ios::in | (binary ? std::ios::binary : std::ios::openmode(0)));
311 if (!*in)
312 log_and_throw_error("Unable to open input resource {}", describe(path));
313 return in;
314 }
315
316 Eigen::MatrixXd FileSystemIO::read_matrix(const std::string &path) const
317 {
318 record_access(path);
319 return read_file_matrix<double>(resolve(path));
320 }
321
322 Eigen::MatrixXi FileSystemIO::read_int_matrix(const std::string &path) const
323 {
324 record_access(path);
325 return read_file_matrix<int>(resolve(path));
326 }
327
328 std::vector<double> FileSystemIO::read_double_vector(const std::string &path) const { return matrix_vector(read_matrix(path)); }
329 std::vector<int> FileSystemIO::read_int_vector(const std::string &path) const { return matrix_vector(read_int_matrix(path)); }
330 std::vector<long> FileSystemIO::read_long_vector(const std::string &path) const
331 {
332 record_access(path);
333 return matrix_vector(read_file_matrix<long>(resolve(path)));
334 }
335
336 bool FileSystemIO::has_attribute(const std::string &, const std::string &) const { return false; }
337 long FileSystemIO::read_integer_attribute(const std::string &, const std::string &) const
338 {
339 log_and_throw_error("Attributes require a structured HDF5 resource");
340 }
341 std::string FileSystemIO::read_string_attribute(const std::string &, const std::string &) const
342 {
343 log_and_throw_error("Attributes require a structured HDF5 resource");
344 }
345 std::array<long, 2> FileSystemIO::read_shape_attribute(const std::string &, const std::string &) const
346 {
347 log_and_throw_error("Attributes require a structured HDF5 resource");
348 }
349
350 fs::path FileSystemIO::materialize(const std::string &path) const
351 {
352 record_access(path);
353 return resolve(path);
354 }
355 std::string FileSystemIO::describe(const std::string &path) const { return resolve(path).string(); }
356
358 {
359 public:
360 explicit Impl(const fs::path &path)
361 : file(path.string(), h5pp::FileAccess::READONLY) {}
362
364 {
365 std::error_code error;
366 if (!temporary_directory.empty())
367 fs::remove_all(temporary_directory, error);
368 }
369
370 h5pp::File file;
371 mutable std::mutex mutex;
372 mutable fs::path temporary_directory;
373 mutable std::map<std::string, fs::path> materialized;
374 };
375
377 const fs::path &file,
378 const std::string &root,
379 const fs::path &host_directory,
380 const std::string &storage_root)
381 : impl_(std::make_shared<Impl>(file)),
382 file_path_(fs::absolute(file).lexically_normal()),
383 root_(!storage_root.empty() && is_windows_absolute(root)
384 ? normalize_windows_absolute(root)
385 : join_logical("/", root)),
386 storage_root_(storage_root.empty() || storage_root == "/"
387 ? std::string()
388 : join_logical("/", storage_root)),
389 host_directory_(host_directory.empty() ? file_path_.parent_path() : host_directory) {}
390
391 HDF5IO::~HDF5IO() = default;
392
393 std::unique_ptr<const ResourceIO> HDF5IO::with_root(const std::string &root) const
394 {
395 auto result = std::make_unique<HDF5IO>(
397 result->impl_ = impl_;
398 result->access_tracker_ = access_tracker_;
399 return result;
400 }
401
402 std::string HDF5IO::logical_resolve(const std::string &path) const
403 {
404 // Mounted checkpoint resources retain the original filesystem namespace.
405 // Ordinary HDF5 bundles still treat 'C:' and backslashes as POSIX names.
406 if (!storage_root_.empty() && is_windows_absolute(root_))
407 {
408 const std::string normalized = windows_separators(path);
409 if (is_windows_absolute(normalized))
410 return normalize_windows_absolute(normalized);
411 if (!normalized.empty() && normalized.front() == '/')
412 return normalize_windows_absolute(root_.substr(0, 2) + normalized);
413 return normalize_windows_absolute(root_ + "/" + normalized);
414 }
415 return join_logical(root_, path);
416 }
417
418 std::string HDF5IO::resolve(const std::string &path) const
419 {
420 const std::string logical = logical_resolve(path);
421 return storage_root_.empty()
422 ? logical
423 : join_logical(storage_root_, logical.front() == '/' ? logical.substr(1) : logical);
424 }
425 bool HDF5IO::exists(const std::string &path) const { return impl_->file.linkExists(resolve(path)); }
426
427 bool HDF5IO::is_group(const std::string &path) const
428 {
429 const std::string target = resolve(path);
430 if (!impl_->file.linkExists(target))
431 return false;
432 const auto info = impl_->file.getLinkInfo(target);
433 return info.h5ObjType.has_value() && info.h5ObjType.value() == H5O_TYPE_GROUP;
434 }
435
436 std::vector<std::string> HDF5IO::list(const std::string &path) const
437 {
438 if (!is_group(path))
439 return {};
440 const std::string parent = resolve(path);
441 const std::string logical_parent = logical_resolve(path);
442 const bool absolute_input = (!path.empty() && path.front() == '/')
443 || (!storage_root_.empty() && is_windows_absolute(root_) && is_windows_absolute(path));
444 std::set<std::string> children;
445 const auto add = [&](const std::vector<std::string> &entries) {
446 for (const std::string &entry : entries)
447 {
448 std::string full = entry;
449 if (full.empty())
450 continue;
451 if (full.front() != '/')
452 full = join_logical(parent, full);
453 const std::string relative = relative_logical(normalize_logical(full), parent);
454 if (relative != "." && relative != ".." && relative.compare(0, 3, "../") != 0)
455 {
456 const std::string child = join_logical(logical_parent, relative.substr(0, relative.find('/')));
457 children.insert(
458 absolute_input ? child : relative_logical(child, root_));
459 }
460 }
461 };
462 add(impl_->file.findGroups("", parent));
463 add(impl_->file.findDatasets("", parent));
464 return {children.begin(), children.end()};
465 }
466
467 std::string HDF5IO::read_string(const std::string &path) const
468 {
469 record_access(path);
470 const std::string key = resolve(path);
471 const auto info = impl_->file.getDatasetInfo(key);
472 if (!is_file_dataset(info))
473 log_and_throw_error("Resource {} is typed numeric data, not a string or byte resource.", describe(path));
474 if (H5Tget_class(info.h5Type.value()) == H5T_STRING)
475 return impl_->file.readDataset<std::string>(key);
476 const std::vector<unsigned char> bytes =
477 impl_->file.readDataset<std::vector<unsigned char>>(key);
478 return std::string(bytes.begin(), bytes.end());
479 }
480
481 std::unique_ptr<std::istream> HDF5IO::open(const std::string &path, const bool) const
482 {
483 return std::make_unique<std::istringstream>(read_string(path));
484 }
485
486 Eigen::MatrixXd HDF5IO::read_matrix(const std::string &path) const
487 {
488 record_access(path);
489 const auto info = impl_->file.getDatasetInfo(resolve(path));
490 return is_file_dataset(info) ? read_file_matrix<double>(materialize(path)) : read_numeric_matrix<double>(info);
491 }
492
493 Eigen::MatrixXi HDF5IO::read_int_matrix(const std::string &path) const
494 {
495 record_access(path);
496 const auto info = impl_->file.getDatasetInfo(resolve(path));
497 return is_file_dataset(info) ? read_file_matrix<int>(materialize(path)) : read_numeric_matrix<int>(info);
498 }
499
500 std::vector<double> HDF5IO::read_double_vector(const std::string &path) const
501 {
502 return matrix_vector(read_matrix(path));
503 }
504
505 std::vector<int> HDF5IO::read_int_vector(const std::string &path) const
506 {
507 return matrix_vector(read_int_matrix(path));
508 }
509
510 std::vector<long> HDF5IO::read_long_vector(const std::string &path) const
511 {
512 record_access(path);
513 const auto info = impl_->file.getDatasetInfo(resolve(path));
514 return matrix_vector(is_file_dataset(info) ? read_file_matrix<long>(materialize(path)) : read_numeric_matrix<long>(info));
515 }
516
517 bool HDF5IO::has_attribute(const std::string &path, const std::string &name) const
518 {
519 const std::string key = resolve(path);
520 return impl_->file.attributeExists(std::string_view(key), std::string_view(name));
521 }
522
523 long HDF5IO::read_integer_attribute(const std::string &path, const std::string &name) const
524 {
525 record_access(path);
526 return impl_->file.readAttribute<long>(resolve(path), name);
527 }
528
529 std::string HDF5IO::read_string_attribute(const std::string &path, const std::string &name) const
530 {
531 record_access(path);
532 return impl_->file.readAttribute<std::string>(resolve(path), name);
533 }
534
535 std::array<long, 2> HDF5IO::read_shape_attribute(const std::string &path, const std::string &name) const
536 {
537 record_access(path);
538 return impl_->file.readAttribute<std::array<long, 2>>(resolve(path), name);
539 }
540
541 fs::path HDF5IO::materialize(const std::string &path) const
542 {
543 record_access(path);
544 const std::string key = resolve(path);
545 std::lock_guard<std::mutex> lock(impl_->mutex);
546 if (const auto it = impl_->materialized.find(key); it != impl_->materialized.end())
547 return it->second;
548 if (impl_->temporary_directory.empty())
549 {
550 const auto stamp = std::chrono::high_resolution_clock::now().time_since_epoch().count();
551 for (size_t attempt = 0;; ++attempt)
552 {
553 const fs::path candidate = fs::temp_directory_path() / fmt::format("polyfem-resources-{}-{}", stamp, attempt);
554 if (fs::create_directory(candidate))
555 {
556 impl_->temporary_directory = candidate;
557 break;
558 }
559 }
560 }
561 // Separate directories preserve the original extension without aliasing
562 // resources that happen to have the same basename.
563 const fs::path directory = impl_->temporary_directory / std::to_string(impl_->materialized.size());
564 fs::create_directory(directory);
565 const fs::path filename = fs::path(key).filename();
566 const fs::path output = directory / (filename.empty() ? fs::path("resource") : filename);
567 std::ofstream file(output, std::ios::binary);
568 const std::string contents = read_string(path);
569 file.write(contents.data(), contents.size());
570 if (!file)
571 log_and_throw_error("Unable to materialize HDF5 resource {}", describe(path));
572 impl_->materialized.emplace(key, output);
573 return output;
574 }
575
576 std::string HDF5IO::describe(const std::string &path) const
577 {
578 return fmt::format("{}:{}", file_path_.string(), resolve(path));
579 }
580
581 namespace
582 {
583 LoadedInput apply_explicit_root(LoadedInput input)
584 {
585 if (input.config.contains("root_path") && input.config["root_path"].is_string()
586 && !input.config["root_path"].get<std::string>().empty())
587 {
588 input.resources = input.resources->with_root(input.config["root_path"]);
589 }
590 input.config.erase("root_path");
591 return input;
592 }
593 } // namespace
594
595 LoadedInput load_json_input(const fs::path &path)
596 {
597 auto resources = std::make_unique<FileSystemIO>(path.parent_path());
598 LoadedInput result{json::parse(resources->read_string(path.filename().string())), std::move(resources)};
599 return apply_explicit_root(std::move(result));
600 }
601
602 LoadedInput load_yaml_input(const fs::path &path)
603 {
604 auto resources = std::make_unique<FileSystemIO>(path.parent_path());
605 LoadedInput result{yaml_string_to_json(resources->read_string(path.filename().string())), std::move(resources)};
606 return apply_explicit_root(std::move(result));
607 }
608
609 LoadedInput load_hdf5_input(const fs::path &path)
610 {
611 auto resources = std::make_unique<HDF5IO>(path);
612 std::string config_path = "/config";
613 if (!resources->exists(config_path))
614 {
615 config_path = "/json";
616 if (!resources->exists(config_path))
617 log_and_throw_error("HDF5 input {} contains neither /config nor /json", path.string());
618 logger().warn("HDF5 input {} uses deprecated /json; rename it to /config.", path.string());
619 }
620 LoadedInput result{json::parse(resources->read_string(config_path)), std::move(resources)};
621 return apply_explicit_root(std::move(result));
622 }
623} // namespace polyfem::io
std::vector< Eigen::Triplet< double > > entries
std::filesystem::path host_directory_
bool has_attribute(const std::string &path, const std::string &name) const override
std::filesystem::path resolve(const std::string &path) const
std::filesystem::path root_
bool exists(const std::string &path) const override
long read_integer_attribute(const std::string &path, const std::string &name) const override
std::unique_ptr< const ResourceIO > with_root(const std::string &root) const override
std::string read_string_attribute(const std::string &path, const std::string &name) const override
std::vector< std::string > list(const std::string &path) const override
std::vector< int > read_int_vector(const std::string &path) const override
std::filesystem::path materialize(const std::string &path) const override
bool is_group(const std::string &path) const override
Eigen::MatrixXd read_matrix(const std::string &path) const override
std::vector< long > read_long_vector(const std::string &path) const override
std::unique_ptr< std::istream > open(const std::string &path, bool binary) const override
Eigen::MatrixXi read_int_matrix(const std::string &path) const override
std::vector< double > read_double_vector(const std::string &path) const override
const std::filesystem::path & host_directory() const override
Host directory used only as the base for ordinary filesystem outputs.
std::string describe(const std::string &path) const override
FileSystemIO(const std::filesystem::path &root, const std::filesystem::path &host_directory={})
std::array< long, 2 > read_shape_attribute(const std::string &path, const std::string &name) const override
std::map< std::string, fs::path > materialized
Impl(const fs::path &path)
std::vector< int > read_int_vector(const std::string &path) const override
std::vector< long > read_long_vector(const std::string &path) const override
std::filesystem::path file_path_
bool has_attribute(const std::string &path, const std::string &name) const override
std::unique_ptr< std::istream > open(const std::string &path, bool binary) const override
std::string read_string(const std::string &path) const override
long read_integer_attribute(const std::string &path, const std::string &name) const override
std::string logical_resolve(const std::string &path) const
std::filesystem::path materialize(const std::string &path) const override
std::shared_ptr< Impl > impl_
Eigen::MatrixXd read_matrix(const std::string &path) const override
std::filesystem::path host_directory_
std::vector< std::string > list(const std::string &path) const override
bool is_group(const std::string &path) const override
bool exists(const std::string &path) const override
HDF5IO(const std::filesystem::path &file, const std::string &root="/", const std::filesystem::path &host_directory={}, const std::string &storage_root="")
std::string storage_root_
std::vector< double > read_double_vector(const std::string &path) const override
std::unique_ptr< const ResourceIO > with_root(const std::string &root) const override
std::array< long, 2 > read_shape_attribute(const std::string &path, const std::string &name) const override
std::string describe(const std::string &path) const override
Eigen::MatrixXi read_int_matrix(const std::string &path) const override
std::string resolve(const std::string &path) const
std::string read_string_attribute(const std::string &path, const std::string &name) const override
std::unique_ptr< const ResourceIO > resources
std::vector< std::string > accessed_resources() const
A snapshot of canonical paths consumed so far, including solver setup.
void record_access(const std::string &path) const
virtual std::vector< std::string > list(const std::string &path) const =0
std::vector< std::string > glob(const std::string &pattern) const
Glob normalized logical paths using POSIX '*' and '?' wildcards.
virtual std::string read_string(const std::string &path) const
virtual std::string canonical_path(const std::string &path) const =0
Absolute resource identity, independent of the active reader root.
std::shared_ptr< AccessTracker > access_tracker_
virtual bool exists(const std::string &path) const =0
virtual std::unique_ptr< std::istream > open(const std::string &path, bool binary) const =0
virtual bool is_group(const std::string &path) const =0
list dims
Definition eigs.py:80
str base
Definition p_bases.py:438
LoadedInput load_json_input(const std::filesystem::path &path)
LoadedInput load_hdf5_input(const std::filesystem::path &path)
LoadedInput load_yaml_input(const std::filesystem::path &path)
bool read_matrix(const std::string &path, Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &mat)
Reads a matrix from a file. Determines the file format based on the path's extension.
Definition MatrixIO.cpp:19
json yaml_string_to_json(const std::string &yaml_str)
Convert YAML string to JSON.
int find(const Eigen::VectorXi &vec, int x)
Definition NCMesh2D.cpp:341
std::vector< std::string > split(const std::string &str, const std::string &delimiters=" ")
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73