Skip to content

Commit 958ba69

Browse files
authored
Merge pull request #851 from wildmeshing/dzint/tetwild_msh_converter
Dzint/tetwild msh converter
2 parents 1107b6b + 55263cc commit 958ba69

File tree

8 files changed

+249
-11
lines changed

8 files changed

+249
-11
lines changed

applications/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ add_application(marching ON)
2727
add_application(procedural ON)
2828
add_application(multimesh OFF)
2929
add_application(isotropic_remeshing OFF)
30+
add_application(tetwild_msh_converter ON)
3031
add_application(tetwild_simplification ON)
3132
add_application(triwild ON)
3233
add_application(tetwild ON)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
wmtk_add_application(tetwild_msh_converter_app
2+
tetwild_msh_converter_main.cpp
3+
tetwild_msh_converter_spec.hpp
4+
)
5+
6+
target_link_libraries(tetwild_msh_converter_app PRIVATE
7+
wmtk::input)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Tetwild MSH Converter
2+
3+
This application converts an MSH file produced by [Tetwild](https://github.com/daniel-zint/TetWild) into a VTU file.
4+
If the MSH has an `in_out` attribute, it will convert it to a `winding_number` attribute.
5+
6+
#### Parameters
7+
8+
As this application is extremely simple, it does not require the standard JSON input. Instead, it just requires an input file name and optionally an output file name.
9+
10+
```
11+
-i Input MSH file
12+
-j Output VTU file (optional, by default the same as the input)
13+
```
14+
15+
#### Examples of usage
16+
17+
```
18+
./tetwild_msh_converter -i sphere.msh
19+
```
20+
21+
This will create a `sphere_tets.vtu`.
22+
23+
```
24+
./tetwild_msh_converter -i sphere.msh -o sphere_converted.vtu
25+
```
26+
27+
This will create a `sphere_converted_tets.vtu`.
28+
29+
#### JSON parameter file
30+
31+
While a JSON parameter file is not necessary, it is still possible to be used with that, too. For details, look at `tetwild_msh_converter_spec.json`.
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#include <jse/jse.h>
2+
#include <CLI/CLI.hpp>
3+
#include <filesystem>
4+
#include <nlohmann/json.hpp>
5+
6+
#include <wmtk/io/ParaviewWriter.hpp>
7+
#include <wmtk/utils/Logger.hpp>
8+
9+
#include <wmtk/components/input/input.hpp>
10+
#include <wmtk/components/utils/json_utils.hpp>
11+
#include <wmtk/components/utils/resolve_path.hpp>
12+
13+
#include "tetwild_msh_converter_spec.hpp"
14+
15+
using namespace wmtk;
16+
using namespace wmtk::components;
17+
namespace fs = std::filesystem;
18+
19+
int main(int argc, char* argv[])
20+
{
21+
opt_logger().set_level(spdlog::level::off);
22+
CLI::App app{argv[0]};
23+
24+
app.ignore_case();
25+
26+
fs::path json_input_file;
27+
fs::path input_file = "";
28+
fs::path output_file;
29+
30+
auto cli_group = app.add_option_group("subgroup");
31+
32+
CLI::Option* cli_json_spec_file_option =
33+
cli_group->add_option("-j, --json", json_input_file, "json specification file")
34+
->check(CLI::ExistingFile);
35+
CLI::Option* cli_input_file_option =
36+
cli_group->add_option("-i, --input", input_file, "input MSH file")
37+
->check(CLI::ExistingFile);
38+
39+
cli_group->require_option(1); // --j xor --i
40+
41+
CLI::Option* cli_output_file_option =
42+
app.add_option("-o, --output", output_file, "output VTU file")
43+
->needs(cli_input_file_option);
44+
45+
CLI11_PARSE(app, argc, argv);
46+
47+
// json spec file
48+
nlohmann::json j;
49+
if (!json_input_file.empty()) {
50+
using wmtk::components::utils::resolve_paths;
51+
52+
std::ifstream ifs(json_input_file);
53+
j = nlohmann::json::parse(ifs);
54+
55+
jse::JSE spec_engine;
56+
bool r = spec_engine.verify_json(j, tetwild_msh_converter_spec);
57+
if (!r) {
58+
wmtk::logger().error("{}", spec_engine.log2str());
59+
return 1;
60+
} else {
61+
j = spec_engine.inject_defaults(j, tetwild_msh_converter_spec);
62+
}
63+
64+
input_file = resolve_paths(json_input_file, {j["input_path"], j["input"]});
65+
output_file = std::string(j["output"]);
66+
}
67+
68+
if (output_file.empty()) {
69+
output_file = input_file;
70+
}
71+
output_file.replace_extension();
72+
73+
74+
std::shared_ptr<Mesh> mesh;
75+
try {
76+
mesh = wmtk::components::input::input(input_file, false, {"in_out"});
77+
} catch (const std::exception&) {
78+
// try again but without in_out attribute
79+
mesh = wmtk::components::input::input(input_file);
80+
}
81+
Mesh& m = *mesh;
82+
wmtk::logger().info("mesh has {} vertices", m.get_all(PrimitiveType::Vertex).size());
83+
84+
// add winding_number attribute
85+
if (m.has_attribute<double>("in_out", m.top_simplex_type())) {
86+
auto wn_handle = m.register_attribute<double>("winding_number", m.top_simplex_type(), 1);
87+
auto wn_acc = m.create_accessor<double>(wn_handle);
88+
89+
auto in_out_handle = m.get_attribute_handle<double>("in_out", PrimitiveType::Tetrahedron);
90+
auto in_out_acc = m.create_accessor<double>(in_out_handle);
91+
92+
for (const Tuple& t : m.get_all(m.top_simplex_type())) {
93+
wn_acc.scalar_attribute(t) = in_out_acc.scalar_attribute(t);
94+
}
95+
96+
auto pos_handle = m.get_attribute_handle<double>("vertices", PrimitiveType::Vertex);
97+
m.clear_attributes({pos_handle, wn_handle});
98+
}
99+
100+
// output
101+
{
102+
logger().info("Write mesh '{}'", output_file.string());
103+
104+
const bool edge = m.top_simplex_type() == PrimitiveType::Edge;
105+
const bool tri = m.top_simplex_type() == PrimitiveType::Triangle;
106+
const bool tet = m.top_simplex_type() == PrimitiveType::Tetrahedron;
107+
108+
wmtk::io::ParaviewWriter writer(output_file, "vertices", m, false, edge, tri, tet);
109+
m.serialize(writer);
110+
}
111+
112+
if (!json_input_file.empty()) {
113+
const std::string report = j["report"];
114+
if (!report.empty()) {
115+
nlohmann::json out_json;
116+
out_json["stats"]["vertices"] = m.get_all(PrimitiveType::Vertex).size();
117+
out_json["stats"]["edges"] = m.get_all(PrimitiveType::Edge).size();
118+
out_json["stats"]["triangles"] = m.get_all(PrimitiveType::Triangle).size();
119+
out_json["stats"]["tets"] = m.get_all(PrimitiveType::Tetrahedron).size();
120+
121+
out_json["input"] = j;
122+
123+
std::ofstream ofs(report);
124+
ofs << std::setw(4) << out_json;
125+
}
126+
}
127+
128+
129+
return 0;
130+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#pragma once
2+
#include <nlohmann/json.hpp>
3+
namespace {
4+
5+
nlohmann::json tetwild_msh_converter_spec = R"(
6+
[
7+
{
8+
"pointer": "/",
9+
"type": "object",
10+
"required": ["input"],
11+
"optional": ["output", "report", "input_path"]
12+
},
13+
{
14+
"pointer": "/input",
15+
"type": "string",
16+
"doc": "Input MSH file"
17+
},
18+
{
19+
"pointer": "/output",
20+
"type": "string",
21+
"default": "",
22+
"doc": "Output VTU file. By default, the same as the input file name but with .vtu extension."
23+
},
24+
{
25+
"pointer": "/report",
26+
"type": "string",
27+
"default": ""
28+
},
29+
{
30+
"pointer": "/input_path",
31+
"type": "string",
32+
"default": "",
33+
"doc": " The folder in which the input file is located. By default, this path will be set to the folder of the JSON spec file."
34+
}
35+
]
36+
)"_json;
37+
38+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[
2+
{
3+
"pointer": "/",
4+
"type": "object",
5+
"required": ["input"],
6+
"optional": ["output", "report", "input_path"]
7+
},
8+
{
9+
"pointer": "/input",
10+
"type": "string",
11+
"doc": "Input MSH file"
12+
},
13+
{
14+
"pointer": "/output",
15+
"type": "string",
16+
"default": "",
17+
"doc": "Output VTU file. By default, the same as the input file name but with .vtu extension."
18+
},
19+
{
20+
"pointer": "/report",
21+
"type": "string",
22+
"default": ""
23+
},
24+
{
25+
"pointer": "/input_path",
26+
"type": "string",
27+
"default": "",
28+
"doc": " The folder in which the input file is located. By default, this path will be set to the folder of the JSON spec file."
29+
}
30+
]

src/wmtk/io/MshReader.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,8 @@ void MshReader::extract_element_attribute(
317317
}
318318
}
319319

320-
auto MshReader::generate(
321-
const std::optional<std::vector<std::vector<std::string>>>& extra_attributes_opt)
322-
-> std::shared_ptr<Mesh>
320+
auto MshReader::generate(const std::optional<std::vector<std::vector<std::string>>>&
321+
extra_attributes_opt) -> std::shared_ptr<Mesh>
323322
{
324323
std::shared_ptr<Mesh> res;
325324
switch (get_mesh_dimension()) {
@@ -384,7 +383,7 @@ void MshReader::validate<3>()
384383
// swap col 0 and 1 of S
385384
S.col(0).swap(S.col(1));
386385
wmtk::logger().info(
387-
"Input tet orientation is inverted, swapping col 0 and 1 of TV matirx.");
386+
"Input tet orientation is inverted, swapping col 0 and 1 of TV matrix.");
388387
}
389388
}
390389

src/wmtk/io/ParaviewWriter.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,15 @@ ParaviewWriter::ParaviewWriter(
150150
m_writers[2].init(filename.string() + "_faces.vtu", vertices_name, cells[2], m_enabled[2]);
151151
m_writers[3].init(filename.string() + "_tets.vtu", vertices_name, cells[3], m_enabled[3]);
152152

153-
paraviewo::VTMWriter vtm;
154-
if (m_enabled[0]) vtm.add_dataset("verts", "mesh", filename.string() + "_verts.vtu");
155-
if (m_enabled[1]) vtm.add_dataset("edges", "mesh", filename.string() + "_edges.vtu");
156-
if (m_enabled[2]) vtm.add_dataset("faces", "mesh", filename.string() + "_faces.vtu");
157-
if (m_enabled[3]) vtm.add_dataset("tets", "mesh", filename.string() + "_tets.vtu");
158-
159-
vtm.save(filename.string() + ".vtm");
153+
if (m_enabled[0] + m_enabled[1] + m_enabled[2] + m_enabled[3] > 1) {
154+
paraviewo::VTMWriter vtm;
155+
if (m_enabled[0]) vtm.add_dataset("verts", "mesh", filename.string() + "_verts.vtu");
156+
if (m_enabled[1]) vtm.add_dataset("edges", "mesh", filename.string() + "_edges.vtu");
157+
if (m_enabled[2]) vtm.add_dataset("faces", "mesh", filename.string() + "_faces.vtu");
158+
if (m_enabled[3]) vtm.add_dataset("tets", "mesh", filename.string() + "_tets.vtu");
159+
160+
vtm.save(filename.string() + ".vtm");
161+
}
160162
}
161163

162164
void ParaviewWriter::write(

0 commit comments

Comments
 (0)