Wildmeshing Toolkit
integration_test.cpp
Go to the documentation of this file.
1 #include <catch2/catch_test_macros.hpp>
3 #include <polysolve/Utils.hpp>
4 
5 #include <wmtk/utils/Logger.hpp>
6 
8 
10 
11 #include <filesystem>
12 #include <fstream>
13 #include <iostream>
15 
17 using namespace wmtk;
18 
20 
21 namespace {
22 
23 bool load_json(const std::string& json_file, json& out)
24 {
25  std::ifstream file(json_file);
26 
27  if (!file.is_open()) return false;
28 
29  file >> out;
30 
31  return true;
32 }
33 
34 
35 bool contains_results(const json& in_args)
36 {
37  if (!in_args.contains("tests")) {
38  return false;
39  }
40 
41  const auto& tests = in_args["tests"];
42  for (const auto& type : {"meshes", "vertices", "edges", "faces", "tetrahedra"}) {
43  if (!(tests.contains(type) && (tests[type].is_number() || tests[type].is_array()))) {
44  spdlog::info(
45  "Test must have type {} = {} and test is an (array type = {} or number type {})",
46  tests.contains(type),
47  type,
48  tests[type].is_number(),
49  tests[type].is_array());
50  return false;
51  }
52  }
53  return true;
54 }
55 
56 bool missing_tests_data(const json& j)
57 {
58  return !j.contains("tests") || !j.at("tests").contains("meshes");
59 }
60 
61 IntegrationTestResult authenticate_json(const std::string& json_file, const bool compute_validation)
62 {
63  json in_args;
64  if (!load_json(json_file, in_args)) {
65  wmtk::logger().error("unable to open {} file", json_file);
67  }
68 
69  in_args["root_path"] = json_file;
70 
72  auto cache = wmtk::components::run_components(in_args, true);
73 
74  if (!compute_validation) {
75  if (missing_tests_data(in_args)) {
76  wmtk::logger().error("JSON file missing \"tests\" or meshes key.");
78  }
79 
80  REQUIRE(contains_results(in_args));
81 
82  wmtk::logger().info("Authenticating...");
83 
84  const std::vector<std::string> meshes = in_args["tests"]["meshes"];
85 
86  if (in_args["tests"].contains("skip_check") && in_args["tests"]["skip_check"]) {
87  wmtk::logger().warn("Skpping checks for {}", json_file);
89  }
90 
91  const auto vertices = in_args["tests"]["vertices"];
92  const auto edges = in_args["tests"]["edges"];
93  const auto faces = in_args["tests"]["faces"];
94  const auto tetrahedra = in_args["tests"]["tetrahedra"];
95  if (meshes.size() != vertices.size() || meshes.size() != edges.size() ||
96  meshes.size() != faces.size() || meshes.size() != tetrahedra.size()) {
97  wmtk::logger().error(
98  "JSON size missmatch between meshes and vertices, edges, faces, or tetrahedra.");
100  }
101 
102  for (int64_t i = 0; i < meshes.size(); ++i) {
103  const std::shared_ptr<Mesh> mesh = cache.read_mesh(meshes[i]);
104 
105  const int64_t expected_vertices = vertices[i];
106  const int64_t expected_edges = edges[i];
107  const int64_t expected_faces = faces[i];
108  const int64_t expected_tetrahedra = tetrahedra[i];
109 
110  const int64_t n_vertices = mesh->get_all(PrimitiveType::Vertex).size();
111  const int64_t n_edges = mesh->get_all(PrimitiveType::Edge).size();
112  const int64_t n_faces = mesh->get_all(PrimitiveType::Triangle).size();
113  const int64_t n_tetrahedra = mesh->get_all(PrimitiveType::Tetrahedron).size();
114 
115  if (n_vertices != expected_vertices) {
116  wmtk::logger().error(
117  "Violating Authenticate in mesh `{}` for vertices {} != {}",
118  meshes[i],
119  n_vertices,
120  expected_vertices);
122  }
123  if (n_edges != expected_edges) {
124  wmtk::logger().error(
125  "Violating Authenticate in mesh `{}` for edges {} != {}",
126  meshes[i],
127  n_edges,
128  expected_edges);
130  }
131  if (n_faces != expected_faces) {
132  wmtk::logger().error(
133  "Violating Authenticate in mesh `{}` for faces {} != {}",
134  meshes[i],
135  n_faces,
136  expected_faces);
138  }
139  if (n_tetrahedra != expected_tetrahedra) {
140  wmtk::logger().error(
141  "Violating Authenticate in mesh `{}` for tetrahedra {} != {}",
142  meshes[i],
143  n_tetrahedra,
144  expected_tetrahedra);
146  }
147  }
148 
149  wmtk::logger().info("Authenticated ✅");
150  } else {
151  if (contains_results(in_args)) {
152  wmtk::logger().error(
153  "JSON file contains results even though the test was run in `computation "
154  "mode`. Set DO_VALIDATION to false to compare with saved results or remove "
155  "results from JSON to re-compute them.");
157  }
158 
159  wmtk::logger().warn("Appending JSON...");
160 
161  const std::vector<std::string> meshes = cache.mesh_names();
162  in_args["tests"]["meshes"] = meshes;
163 
164  std::vector<int64_t> expected_vertices, expected_edges, expected_faces, expected_tetrahedra;
165 
166  for (int64_t i = 0; i < meshes.size(); ++i) {
167  const std::shared_ptr<Mesh> mesh = cache.read_mesh(meshes[i]);
168 
169 
170  const int64_t n_vertices = mesh->get_all(PrimitiveType::Vertex).size();
171  const int64_t n_edges = mesh->get_all(PrimitiveType::Edge).size();
172  const int64_t n_faces = mesh->get_all(PrimitiveType::Triangle).size();
173  const int64_t n_tetrahedra = mesh->get_all(PrimitiveType::Tetrahedron).size();
174 
175  expected_vertices.push_back(n_vertices);
176  expected_edges.push_back(n_edges);
177  expected_faces.push_back(n_faces);
178  expected_tetrahedra.push_back(n_tetrahedra);
179  }
180 
181  in_args["tests"]["vertices"] = expected_vertices;
182  in_args["tests"]["edges"] = expected_edges;
183  in_args["tests"]["faces"] = expected_faces;
184  in_args["tests"]["tetrahedra"] = expected_tetrahedra;
185  in_args.erase("root_path");
186  in_args.erase("settings");
187 
188  std::ofstream file(json_file);
189  file << in_args;
190  }
191 
193 }
194 } // namespace
195 namespace {
196 #if defined(NDEBUG)
197 std::string tagsrun = "[integration]";
198 #else
199 std::string tagsrun = "[.][integration]";
200 #endif
201 } // namespace
202 
203 #define WMTK_INTEGRATION_BODY(NAME, DO_VALIDATION) \
204  { \
205  std::string path = std::string("unit_test/") + NAME + ".json"; \
206  bool compute_validation = DO_VALIDATION; \
207  wmtk::logger().info("Processing {}", NAME); \
208  auto flag = authenticate_json(WMTK_DATA_DIR "/" + path, compute_validation); \
209  REQUIRE(flag == IntegrationTestResult::Success); \
210  }
211 
212 #define WMTK_INTEGRATION(NAME, DO_VALIDATION) \
213  TEST_CASE(std::string("integration_") + NAME, tagsrun) \
214  WMTK_INTEGRATION_BODY(NAME, DO_VALIDATION)
215 
216 
217 WMTK_INTEGRATION("input", false);
218 WMTK_INTEGRATION("to_points", false);
219 WMTK_INTEGRATION("delaunay", false);
220 WMTK_INTEGRATION("insertion", false);
221 WMTK_INTEGRATION("insertion_open", false);
222 WMTK_INTEGRATION("multimesh", false);
223 WMTK_INTEGRATION("multimesh_boundary_2d", false);
224 WMTK_INTEGRATION("multimesh_boundary_3d", false);
225 WMTK_INTEGRATION("isotropic_remeshing", false);
226 WMTK_INTEGRATION("isotropic_remeshing_mm", false);
227 WMTK_INTEGRATION("disk_fan_mm", false);
228 WMTK_INTEGRATION("grid", false);
229 // WMTK_INTEGRATION("wildmeshing_2d", true);
230 WMTK_INTEGRATION("wildmeshing_3d", false);
231 // WMTK_INTEGRATION("marching", false);
232 
233 
234 TEST_CASE("integration_benchmark", "[.][benchmark][integration]")
235 {
236  double total_time = 0;
237  int count = 0;
238  double prod = 1;
239 
240  nlohmann::json js;
241  auto run = [&](const std::string& name) {
242  double time = 0;
243  {
244  POLYSOLVE_SCOPED_STOPWATCH("Benchmark " + name, time, logger());
245  WMTK_INTEGRATION_BODY(name, false)
246  }
247  js[name] = time;
248  prod *= time;
249  count++;
250  };
251  // run("wildmeshing_2d_timing");
252  {
253  POLYSOLVE_SCOPED_STOPWATCH("Benchmark total time", total_time, logger());
254  run("wildmeshing_3d");
255  run("isotropic_remeshing_mm_timing");
256  }
257  js["total_time"] = total_time;
258  js["geometric_mean_time"] = std::pow(prod, 1.0 / count);
259 
260  fmt::print("{}\n", js.dump());
261 }
IntegrationTestResult
@ Fail
@ InvalidInput
@ Success
TEST_CASE("integration_benchmark", "[.][benchmark][integration]")
nlohmann::json json
#define WMTK_INTEGRATION(NAME, DO_VALIDATION)
#define WMTK_INTEGRATION_BODY(NAME, DO_VALIDATION)
wmtk::io::Cache run_components(const nlohmann::json &json_input_file, bool strict)
std::vector< Tuple > vertices(const Mesh &m, const Simplex &simplex)
std::vector< Tuple > edges(const Mesh &m, const Simplex &simplex)
SimplexCollection faces(const Mesh &mesh, const Simplex &simplex, const bool sort_and_clean)
Returns all faces of a simplex.
Definition: faces.cpp:10
void set_random_seed(uint64_t val)
Definition: random_seed.hpp:35
Definition: Accessor.hpp:6
spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:58
Rational pow(const Rational &x, int p)
Definition: Rational.cpp:166
nlohmann::json json
Definition: input.cpp:9