Wildmeshing Toolkit
Cache.cpp
Go to the documentation of this file.
1 #include "Cache.hpp"
2 
3 #include <chrono>
4 #include <exception>
5 #include <fstream>
6 #include <iostream>
7 #include <nlohmann/json.hpp>
8 #include <sstream>
9 #include <wmtk/io/HDF5Writer.hpp>
10 #include <wmtk/io/MeshReader.hpp>
11 #include <wmtk/utils/Logger.hpp>
12 
13 #include <filesystem>
14 
15 namespace fs = std::filesystem;
16 
18 {
19  return std::chrono::duration_cast<std::chrono::nanoseconds>(
20  std::chrono::system_clock::now().time_since_epoch())
21  .count();
22 }
23 
24 std::string number_to_hex(int64_t l)
25 {
26  return fmt::format("{0:x}", l);
27 }
28 
29 namespace wmtk::io {
30 
32  : m_cache_dir(std::move(o.m_cache_dir))
33  , m_file_paths(std::move(o.m_file_paths))
34  , m_multimeshes(std::move(o.m_multimeshes))
35  , m_delete_cache(o.m_delete_cache)
36 {
37  // make sure that the other cache doesn't use delete semantics anymore
38  o.m_delete_cache = false;
39 }
41 {
42  m_cache_dir = std::move(o.m_cache_dir);
43  m_file_paths = std::move(o.m_file_paths);
44  m_multimeshes = std::move(o.m_multimeshes);
45  m_delete_cache = o.m_delete_cache;
46  // make sure that the other cache doesn't use delete semantics anymore
47  o.m_delete_cache = false;
48  return *this;
49 }
50 
51 std::filesystem::path Cache::create_unique_directory(
52  const std::string& prefix,
53  const std::filesystem::path& location,
54  size_t max_tries)
55 {
56  const fs::path tmp = location.empty() ? std::filesystem::temp_directory_path() : location;
57 
58  const std::string timestamp = number_to_hex(nanoseconds_timestamp());
59 
60  fs::path unique_dir;
61  for (size_t i = 0; i < max_tries; ++i) {
62  unique_dir = tmp / (prefix + "_" + timestamp + "_" + number_to_hex(i));
63 
64  if (std::filesystem::create_directory(unique_dir)) {
65  return unique_dir;
66  }
67  }
68 
69  throw std::runtime_error("Could not generate a unique directory.");
70 }
71 
72 Cache::Cache(const std::string& prefix, const std::filesystem::path location, bool delete_cache)
73  : m_cache_dir(location)
74  , m_delete_cache(delete_cache)
75 {
76  m_cache_dir = create_unique_directory(prefix, location);
77 }
78 
80 {
81  if (m_delete_cache) {
82  const size_t max_tries = 1000;
83  for (size_t i = 0; fs::exists(m_cache_dir) && i < max_tries; ++i) {
84  fs::remove_all(m_cache_dir);
85  }
86 
87  if (fs::exists(m_cache_dir)) {
88  const std::string path = fs::absolute(m_cache_dir).string();
89  wmtk::logger().warn("Could not remove cache folder {}", path);
90  }
91  }
92 }
93 
94 const std::filesystem::path& Cache::create_unique_file(
95  const std::string& filename,
96  const std::string& extension,
97  size_t max_tries)
98 {
99  const std::string timestamp = number_to_hex(nanoseconds_timestamp());
100 
101  for (size_t i = 0; i < max_tries; ++i) {
102  const fs::path p =
103  m_cache_dir / (filename + "_" + timestamp + "_" + number_to_hex(i) + extension);
104 
105  if (fs::exists(p)) {
106  continue;
107  }
108 
109  // try to touch the file
110  std::ofstream ofs(p);
111  if (ofs.is_open()) {
112  m_file_paths[filename] = p;
113  ofs.close();
114  return m_file_paths[filename];
115  }
116  ofs.close();
117  }
118 
119  throw std::runtime_error("Could not generate a unique file.");
120 }
121 
122 const std::filesystem::path& Cache::get_file_path(const std::string& filename)
123 {
124  const auto it = m_file_paths.find(filename);
125 
126  if (it == m_file_paths.end()) {
127  // filename does not exist yet --> create it
128  return create_unique_file(filename, "");
129  } else {
130  return it->second;
131  }
132 }
133 
134 std::filesystem::path Cache::get_file_path(const std::string& filename) const
135 {
136  const auto it = m_file_paths.find(filename);
137 
138  if (it == m_file_paths.end()) {
139  // filename does not exist yet --> create it
140  throw std::runtime_error("File with name '" + filename + "' does not exist in cache");
141  } else {
142  return it->second;
143  }
144 }
145 
146 std::filesystem::path Cache::get_cache_path() const
147 {
148  return m_cache_dir;
149 }
150 std::vector<int64_t> Cache::absolute_multi_mesh_id(const std::string& name) const
151 {
152  auto mm_name = name.substr(0, name.find('.'));
153  return m_multimeshes.at(mm_name).get_id_from_path(name);
154 }
155 
156 void Cache::load_multimesh(const std::string& name) const
157 {
158  auto mm_name = name.substr(0, name.find('.'));
159  if (m_multimeshes.find(mm_name) == m_multimeshes.end()) {
160  m_multimeshes.emplace(
161  mm_name,
162  CachedMultiMesh(mm_name, std::map<std::string, std::vector<int64_t>>{}));
163  }
164  auto& cmm = m_multimeshes.at(mm_name);
165  if (!bool(cmm.get_root())) {
166  const fs::path p = get_file_path(mm_name);
167  cmm.load(p);
168  }
169 }
170 
171 std::shared_ptr<Mesh> Cache::read_mesh(const std::string& name) const
172 {
173  auto mm_name = name.substr(0, name.find('.'));
174  load_multimesh(mm_name);
175  return m_multimeshes.at(mm_name).get_from_path(name);
176 }
178 {
179  for (auto& pr : m_multimeshes) {
180  pr.second.flush();
181  }
182 }
183 
184 std::vector<std::string> Cache::mesh_names()
185 {
186  std::vector<std::string> names;
187  for (const auto& fp : m_file_paths) {
188  names.emplace_back(fp.first);
189  }
190 
191  return names;
192 }
193 
195  const Mesh& m,
196  const std::string& name,
197  const std::map<std::string, std::vector<int64_t>>& multimesh_names)
198 {
199  const auto it = m_file_paths.find(name);
200 
201  fs::path p;
202 
203  if (it == m_file_paths.end()) {
204  // file does not exist yet --> create it
205  p = create_unique_file(name, ".hdf5");
206  m_file_paths[name] = p;
207  } else {
208  p = it->second;
209  }
210 
211  std::map<std::string, std::vector<int64_t>> mm_names = multimesh_names;
212  if (m_multimeshes.find(name) != m_multimeshes.end()) {
213  mm_names = m_multimeshes.at(name).get_multimesh_names();
214  mm_names.insert(multimesh_names.begin(), multimesh_names.end());
215  m_multimeshes.at(name) = CachedMultiMesh(
216  name,
217  mm_names,
218  const_cast<Mesh&>(m).get_multi_mesh_root().shared_from_this());
219  } else {
220  m_multimeshes.emplace(
221  name,
223  name,
224  multimesh_names,
225  const_cast<Mesh&>(m).get_multi_mesh_root().shared_from_this()));
226  }
227 
228  HDF5Writer writer(p);
229  m.serialize(writer, &m);
230 }
231 
232 bool Cache::export_cache(const std::filesystem::path& export_location)
233 {
234  if (fs::exists(export_location)) {
235  return false;
236  }
237 
238  fs::path cache_content_path;
239 
240  // create a json with all cached names
241  {
242  nlohmann::json cache_content;
243  for (const auto& [first, second] : m_file_paths) {
244  cache_content[first] = fs::relative(second, m_cache_dir).string();
245  }
246 
247  cache_content_path = create_unique_file(m_cache_content_name, ".json");
248  std::ofstream o(cache_content_path);
249  o << std::setw(4) << cache_content << std::endl;
250  o.close();
251  }
252 
253  // copy folder to export location
254  fs::copy(m_cache_dir, export_location, fs::copy_options::recursive);
255 
256  // delete json
257  fs::remove(cache_content_path);
259 
260  return true;
261 }
262 
263 bool Cache::import_cache(const std::filesystem::path& import_location)
264 {
265  if (!fs::exists(import_location)) {
266  return false;
267  }
268  if (!m_file_paths.empty()) {
269  return false;
270  }
271 
272  // remove current directory
273  fs::remove_all(m_cache_dir);
274  // copy import
275  fs::copy(import_location, m_cache_dir, fs::copy_options::recursive);
276 
277  // find json
278  fs::path cache_content_path;
279  for (const auto& f : fs::directory_iterator(m_cache_dir)) {
280  const fs::path p = f.path();
281  if (p.stem().string().rfind(m_cache_content_name, 0) == 0) {
282  cache_content_path = p;
283  break;
284  }
285  }
286 
287  if (cache_content_path.empty()) {
288  return false;
289  }
290 
291  // read json
292  {
293  std::ifstream i(cache_content_path);
294  const nlohmann::json cache_content = nlohmann::json::parse(i);
295 
296  std::map<std::string, std::string> map_paths =
297  cache_content.get<std::map<std::string, std::string>>();
298 
299  // make file paths absolute
300  for (auto& [first, second] : map_paths) {
301  m_file_paths[first] = m_cache_dir / second;
302  }
303  }
304 
305  // delete json
306  fs::remove(cache_content_path);
307 
308  return true;
309 }
310 
311 bool Cache::equals(const Cache& o)
312 {
313  // check file names
314  if (m_file_paths.size() != o.m_file_paths.size() ||
315  !std::equal(
316  m_file_paths.begin(),
317  m_file_paths.end(),
318  m_file_paths.begin(),
319  [](const auto& a, const auto& b) { return a.first == b.first; })) {
320  wmtk::logger().info("File name list is unequal.");
321  return false;
322  }
323 
324  // check files for equality
325  for (const auto& [file_name, path1] : m_file_paths) {
326  const auto& path2 = o.m_file_paths.at(file_name);
327 
328  std::shared_ptr<Mesh> mesh_ptr_1 = wmtk::read_mesh(path1);
329  std::shared_ptr<Mesh> mesh_ptr_2 = wmtk::read_mesh(path2);
330 
331  if (!(*mesh_ptr_1 == *mesh_ptr_2)) {
332  wmtk::logger().info("Mesh {} is unequal.", file_name);
333  return false;
334  }
335  }
336 
337  return true;
338 }
339 
340 } // namespace wmtk::io
int64_t nanoseconds_timestamp()
Definition: Cache.cpp:17
std::string number_to_hex(int64_t l)
Definition: Cache.cpp:24
void serialize(MeshWriter &writer, const Mesh *local_root=nullptr) const
Definition: Mesh.cpp:93
void flush_multimeshes()
Unsets the mesh held by each cached mm - useful for debugging whether cache loading works.
Definition: Cache.cpp:177
std::vector< std::string > mesh_names()
Get all names of the meshes stored in cache.
Definition: Cache.cpp:184
const std::filesystem::path & get_file_path(const std::string &filename)
Get the path where the file with the given name is stored.
Definition: Cache.cpp:122
void write_mesh(const Mesh &m, const std::string &name, const std::map< std::string, std::vector< int64_t >> &multimesh_names={})
Write a mesh to cache.
Definition: Cache.cpp:194
std::filesystem::path get_cache_path() const
Get the path of the cache folder.
Definition: Cache.cpp:146
const std::filesystem::path & create_unique_file(const std::string &filename, const std::string &extension, size_t max_tries=10000)
Create a file with the given name in the cache without overwriting any file with the same name.
Definition: Cache.cpp:94
bool equals(const Cache &o)
Compare two caches for equality.
Definition: Cache.cpp:311
std::map< std::string, std::filesystem::path > m_file_paths
Definition: Cache.hpp:157
std::map< std::string, CachedMultiMesh > m_multimeshes
Definition: Cache.hpp:158
bool m_delete_cache
Definition: Cache.hpp:159
static std::filesystem::path create_unique_directory(const std::string &prefix, const std::filesystem::path &location="", size_t max_tries=10000)
Create a unique directory in the given location.
Definition: Cache.cpp:51
Cache & operator=(Cache &&)
Definition: Cache.cpp:40
void load_multimesh(const std::string &name) const
Definition: Cache.cpp:156
bool import_cache(const std::filesystem::path &import_location)
Import a cache from the given location.
Definition: Cache.cpp:263
static const std::string m_cache_content_name
Definition: Cache.hpp:161
Cache(const std::string &prefix, const std::filesystem::path directory="", bool delete_cache=true)
This class creates and maintains a cache folder for storing temporary data.
Definition: Cache.cpp:72
std::vector< int64_t > absolute_multi_mesh_id(const std::string &name) const
Definition: Cache.cpp:150
bool export_cache(const std::filesystem::path &export_location)
Export the cache to the given location.
Definition: Cache.cpp:232
std::shared_ptr< Mesh > read_mesh(const std::string &name) const
Load a mesh from cache.
Definition: Cache.cpp:171
std::filesystem::path m_cache_dir
Definition: Cache.hpp:156
Definition: autodiff.h:995
spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:58
std::shared_ptr< Mesh > read_mesh(const std::filesystem::path &filename, const bool ignore_z_if_zero, const std::vector< std::string > &tetrahedron_attributes)
Definition: MeshReader.cpp:11
nlohmann::json json
Definition: input.cpp:9