Wildmeshing Toolkit
Loading...
Searching...
No Matches
DataWriter.hpp
1#pragma once
2#include <paraviewo/VTUWriter.hpp>
3#include <set>
4#include <stdexcept>
5#include <wmtk/MeshData.hpp>
6
7namespace wmtk::io {
8// An output observer shared by the legacy exporters and their in-memory adapters.
9// File calls delegate to the original writer, retaining its formatting and filenames.
11{
12 MeshMap* output;
13 paraviewo::VTUWriter file;
14 MeshAttributes points, fields;
15
16public:
17 explicit DataWriter(MeshMap* meshes = nullptr)
18 : output(meshes)
19 {}
20 void add_field(const std::string& name, const Eigen::MatrixXd& data)
21 {
22 if (output)
23 points[name].values = DataMatrix<double>(data);
24 else
25 file.add_field(name, data);
26 }
27 void add_integer_field(const std::string& name, const DataMatrix<int64_t>& values)
28 {
29 if (output)
30 points[name].values = values;
31 else {
32 MeshAttribute field;
33 field.values = values;
34 file.add_field(name, field.doubles());
35 }
36 }
37 void add_cell_field(const std::string& name, const Eigen::MatrixXd& data)
38 {
39 if (output)
40 fields[name].values = DataMatrix<double>(data);
41 else
42 file.add_cell_field(name, data);
43 }
44 void add_integer_cell_field(const std::string& name, const DataMatrix<int64_t>& values)
45 {
46 if (output)
47 fields[name].values = values;
48 else {
49 MeshAttribute field;
50 field.values = values;
51 file.add_cell_field(name, field.doubles());
52 }
53 }
54 // A cell field in its own type, with its column labels: the memory output keeps both; the
55 // file, as every field there, has Float64 and no labels.
56 void add_cell_field(const std::string& name, const MeshAttribute& field)
57 {
58 if (output)
59 fields[name] = field;
60 else
61 file.add_cell_field(name, field.doubles());
62 }
63 bool write_mesh(
64 const std::string& path,
65 const Eigen::MatrixXd& V,
66 const Eigen::MatrixXi& C,
67 paraviewo::CellType type)
68 {
69 if (!output) return file.write_mesh(path, V, C, type);
70 std::vector<paraviewo::CellElement> cells(C.rows());
71 for (Eigen::Index i = 0; i < C.rows(); ++i) {
72 cells[i].ctype = type;
73 for (Eigen::Index j = 0; j < C.cols(); ++j) cells[i].vertices.push_back(C(i, j));
74 }
75 return write_mesh(path, V, cells);
76 }
77 bool write_mesh(
78 const std::string& path,
79 const Eigen::MatrixXd& V,
80 const std::vector<paraviewo::CellElement>& cells)
81 {
82 if (!output) return file.write_mesh(path, V, cells);
83 MeshData data;
84 data.V = DataMatrix<double>::Zero(V.rows(), 3);
85 data.V.leftCols(V.cols()) = V;
86 data.vertex_attributes = points;
87 std::map<CellType, std::vector<size_t>> rows;
88 for (size_t i = 0; i < cells.size(); ++i) {
89 const auto& c = cells[i];
90 const auto type = static_cast<CellType>(
91 paraviewo::paraview_tags::VTKTag(int(c.vertices.size()), c.ctype));
92 auto& block = data.cells[type].connectivity;
93 block.conservativeResize(block.rows() + 1, cell_size(type));
94 for (size_t j = 0; j < c.vertices.size(); ++j)
95 block(block.rows() - 1, j) = c.vertices[j];
96 rows[type].push_back(i);
97 }
98 // Each block its rows of every cell field, in the field's own type, with its labels.
99 for (const auto& [type, indices] : rows)
100 for (const auto& [name, a] : fields) {
101 MeshAttribute& field = data.cells[type].attributes[name];
102 field.labels = a.labels;
103 field.values = std::visit(
104 [&indices](const auto& values) -> AttributeValues {
105 std::decay_t<decltype(values)> sub(indices.size(), values.cols());
106 for (size_t r = 0; r < indices.size(); ++r)
107 sub.row(r) = values.row(indices[r]);
108 return sub;
109 },
110 a.values);
111 }
112 data.validate();
113 output->insert_or_assign(path, std::move(data));
114 // Like VTUWriter::write_mesh, which clears its fields after every write.
115 points.clear();
116 fields.clear();
117 return true;
118 }
119};
120// Export arrays are sometimes indexed by reserved slots. Remove dead slots without
121// consolidating or otherwise mutating the algorithm's mesh, and keep every field aligned.
122inline void compact_vertices(MeshData& data, const std::vector<size_t>& rows)
123{
124 DataMatrix<double> V(rows.size(), 3);
125 std::vector<int64_t> remap(data.V.rows(), -1);
126 for (size_t i = 0; i < rows.size(); ++i) {
127 V.row(i) = data.V.row(rows[i]);
128 remap[rows[i]] = i;
129 }
130 data.V = std::move(V);
131 for (auto& [name, a] : data.vertex_attributes)
132 a.values = std::visit(
133 [&](const auto& values) -> AttributeValues {
134 using M = std::decay_t<decltype(values)>;
135 M compact(rows.size(), values.cols());
136 for (size_t i = 0; i < rows.size(); ++i) compact.row(i) = values.row(rows[i]);
137 return compact;
138 },
139 a.values);
140 for (auto& [type, block] : data.cells)
141 for (Eigen::Index i = 0; i < block.connectivity.size(); ++i) {
142 const auto v = remap.at(block.connectivity.data()[i]);
143 if (v < 0) throw std::runtime_error("Export references an inactive vertex");
144 block.connectivity.data()[i] = v;
145 }
146 data.validate();
147}
148template <typename Mesh>
149void compact_slot_outputs(const Mesh& mesh, MeshMap& outputs)
150{
151 std::vector<size_t> rows;
152 for (const auto& v : mesh.get_vertices()) rows.push_back(v.vid(mesh));
153 for (auto& [name, data] : outputs) compact_vertices(data, rows);
154}
155// The result key of each memory export. The exporters keep their legacy file naming,
156// <stem><suffix>.vtu, and the memory calls use the stem "mesh". An empty key drops an export
157// that the caller returns in another form. The .msh among them are the meshes a history names
158// its points by: <output>_history_input.msh, simwild's and tetwild's, where the surface history
159// (and the volume history) starts, and tetwild's <output>_history_output.msh, the mesh before
160// its filter, where the volume history ends.
161using OutputKeys = std::map<std::string, std::string>;
162inline OutputKeys default_output_keys()
163{
164 return {
165 {"mesh.vtu", "mesh"},
166 {"mesh_surf.vtu", "surface"},
167 {"mesh_edge.vtu", "edges"},
168 {"mesh_off.vtu", "offset"},
169 {"mesh_front.vtu", "offset"},
170 {"mesh_offset_faces.vtu", "offset"},
171 {"mesh_history_input.msh", "history_input"},
172 {"mesh_history_output.msh", "history_output"}};
173}
174// Rename the exports into public result keys. An export missing from `keys`, or two exports
175// with the same key, is an error rather than a silently dropped output.
176inline MeshMap named_outputs(MeshMap raw, const OutputKeys& keys = default_output_keys())
177{
178 MeshMap result;
179 for (auto& [path, data] : raw) {
180 const auto key = keys.find(path);
181 if (key == keys.end()) throw std::runtime_error("Unexpected memory export: " + path);
182 if (key->second.empty()) continue;
183 if (!result.emplace(key->second, std::move(data)).second)
184 throw std::runtime_error("Two memory exports map to the result key " + key->second);
185 }
186 return result;
187}
188} // namespace wmtk::io
Definition DataWriter.hpp:11
std::set< Tet > keys(const Tets &T)
The tets as a set of sorted tets.
Definition volume_complex.cpp:41
Definition MeshData.hpp:28
Definition MeshData.hpp:48