Chi-Tech
SplitFileMeshGenerator.cc
Go to the documentation of this file.
2
4#include "utils/chi_utils.h"
5
9
10#include "chi_log.h"
11#include "utils/chi_timer.h"
12
13#include "ChiObjectFactory.h"
14
15#include <filesystem>
16
17#define scint static_cast<int>
18
19namespace chi_mesh
20{
21
23
24// ##################################################################
26{
28
30 "Generates the mesh only on location 0, thereafter partitions the mesh"
31 " but instead of broadcasting the mesh to other locations it creates binary"
32 " mesh files for each location.");
33 params.SetDocGroup("doc_MeshGenerators");
34
36 "num_partitions",
37 0,
38 "The number of partitions to generate. If zero will "
39 "default to the number of MPI processes. Is "
40 "ignored if the number of MPI processes > 1.");
41
43 "split_mesh_dir_path",
44 "SplitMesh",
45 "Path of the directory to be created for containing the split meshes.");
46
47 params.AddOptionalParameter("split_file_prefix",
48 "split_mesh",
49 "Prefix to use for all split mesh files");
50
52 "read_only",
53 false,
54 "Controls whether the split mesh is recreated or just read.");
55
57 "verbosity_level",
58 1,
59 "Verbosity level. 1 will report each 10% complete. 2 will print each part "
60 "and the number of local cells it wrote.");
61
62 return params;
63}
64
65// ##################################################################
67 const chi::InputParameters& params)
68 : MeshGenerator(params),
69 num_parts_(params.GetParamValue<int>("num_partitions")),
70 split_mesh_dir_path_(
71 params.GetParamValue<std::string>("split_mesh_dir_path")),
72 split_file_prefix_(params.GetParamValue<std::string>("split_file_prefix")),
73 read_only_(params.GetParamValue<bool>("read_only")),
74 verbosity_level_(params.GetParamValue<int>("verbosity_level"))
75{
76}
77
78// ##################################################################
80{
81 const int num_mpi = Chi::mpi.process_count;
82 const int num_parts = num_mpi == 1 ? num_parts_ : num_mpi;
83
84 if (Chi::mpi.location_id == 0 and (not read_only_))
85 {
86 //======================================== Execute all input generators
87 // Note these could be empty
88 std::unique_ptr<UnpartitionedMesh> current_umesh = nullptr;
89 for (auto mesh_generator_ptr : inputs_)
90 {
91 auto new_umesh =
92 mesh_generator_ptr->GenerateUnpartitionedMesh(std::move(current_umesh));
93 current_umesh = std::move(new_umesh);
94 }
95
96 //======================================== Generate final umesh
97 current_umesh = GenerateUnpartitionedMesh(std::move(current_umesh));
98
99 Chi::log.Log() << "Writing split-mesh with " << num_parts << " parts";
100 const auto cell_pids = PartitionMesh(*current_umesh, num_parts);
101 WriteSplitMesh(cell_pids, *current_umesh, num_parts);
102 Chi::log.Log() << "Split-mesh with " << num_parts
103 << " parts successfully created";
104 } // if home location
105
106 // Other locations wait here for files to be written
108
109 if (Chi::mpi.process_count == num_parts)
110 {
111 Chi::log.Log() << "Reading split-mesh";
112 auto mesh_info = ReadSplitMesh();
113
114 auto grid_ptr = SetupLocalMesh(mesh_info);
115
116 auto new_mesher =
117 std::make_shared<chi_mesh::VolumeMesher>(VolumeMesherType::UNPARTITIONED);
118 new_mesher->SetContinuum(grid_ptr);
119
121
122 auto& cur_hndlr = chi_mesh::GetCurrentHandler();
123 cur_hndlr.SetVolumeMesher(new_mesher);
124 Chi::log.Log() << "Done reading split-mesh files";
125 }
126 else
127 {
129 << "After creating a split-mesh with mpi-processes < "
130 "num_parts the program will now auto terminate. This is not an error "
131 "and is the default behavior for the SplitFileMeshGenerator.\n"
133 Chi::Exit(EXIT_SUCCESS);
134 }
135
137}
138
139// ##################################################################
141 const std::vector<int64_t>& cell_pids,
142 const UnpartitionedMesh& umesh,
143 int num_parts)
144{
145 const std::filesystem::path dir_path =
146 std::filesystem::absolute(split_mesh_dir_path_);
147
148 const auto parent_path = dir_path.parent_path();
149 ChiInvalidArgumentIf(not std::filesystem::exists(parent_path),
150 "Parent path " + parent_path.string() +
151 " does not exist");
152
153 bool root_dir_created = true;
154 if (not std::filesystem::exists(dir_path))
155 root_dir_created = std::filesystem::create_directories(dir_path);
156
157 ChiLogicalErrorIf(not root_dir_created,
158 "Failed to create directory " + dir_path.string());
159
160 const auto& vertex_subs = umesh.GetVertextCellSubscriptions();
161 const auto& raw_cells = umesh.GetRawCells();
162 const auto& raw_vertices = umesh.GetVertices();
163
164 auto& t_write =
165 Chi::log.CreateOrGetTimingBlock("FileMeshGenerator::WriteSplitMesh");
166 auto& t_sorting = Chi::log.CreateOrGetTimingBlock(
167 "Sorting data", "FileMeshGenerator::WriteSplitMesh");
168 auto& t_cells = Chi::log.CreateOrGetTimingBlock(
169 "WriteCells", "FileMeshGenerator::WriteSplitMesh");
170 auto& t_verts = Chi::log.CreateOrGetTimingBlock(
171 "WriteVerts", "FileMeshGenerator::WriteSplitMesh");
172 auto& t_serialize = Chi::log.CreateOrGetTimingBlock("Serialize");
173
174 uint64_t aux_counter = 0;
175 for (int pid = 0; pid < num_parts; ++pid)
176 {
177 t_write.TimeSectionBegin();
178 const std::filesystem::path file_path = dir_path.string() + "/" +
179 split_file_prefix_ + "_" +
180 std::to_string(pid) + ".cmesh";
181
182 std::ofstream ofile(file_path.string(),
183 std::ios_base::binary | std::ios_base::out);
184
185 ChiLogicalErrorIf(not ofile.is_open(),
186 "Failed to open " + file_path.string());
187
188 // Appropriate cells and vertices to the current part being writting
189 t_sorting.TimeSectionBegin();
190
191 std::vector<uint64_t> local_cells_needed;
192 std::set<uint64_t> cells_needed;
193 std::set<uint64_t> vertices_needed;
194 {
195 local_cells_needed.reserve(raw_cells.size() / num_parts);
196 {
197 uint64_t cell_global_id = 0;
198 for (auto cell_pid : cell_pids)
199 {
200 if (cell_pid == pid) local_cells_needed.push_back(cell_global_id);
201 ++cell_global_id;
202 }
203 }
204
205 for (uint64_t cell_global_id : local_cells_needed)
206 {
207 cells_needed.insert(cell_global_id);
208
209 const auto& raw_cell = *raw_cells[cell_global_id];
210
211 for (uint64_t vid : raw_cell.vertex_ids)
212 {
213 vertices_needed.insert(vid);
214 for (uint64_t ghost_gid : vertex_subs[vid])
215 {
216 if (ghost_gid == cell_global_id) continue;
217 cells_needed.insert(ghost_gid);
218
219 const auto& ghost_raw_cell = *raw_cells[ghost_gid];
220 for (uint64_t gvid : ghost_raw_cell.vertex_ids)
221 vertices_needed.insert(gvid);
222 }
223 }
224 }
225 }
226 t_sorting.TimeSectionEnd();
227
228 if (verbosity_level_ >= 2)
229 Chi::log.Log() << "Writing part " << pid
230 << " num_local_cells=" << local_cells_needed.size();
231
232 //================================================ Write mesh attributes
233 // and general info
234 const auto& mesh_options = umesh.GetMeshOptions();
235
236 chi::WriteBinaryValue(ofile, num_parts); // int
237
238 chi::WriteBinaryValue(ofile, scint(umesh.GetMeshAttributes())); // int
239 chi::WriteBinaryValue(ofile, mesh_options.ortho_Nx); // size_t
240 chi::WriteBinaryValue(ofile, mesh_options.ortho_Ny); // size_t
241 chi::WriteBinaryValue(ofile, mesh_options.ortho_Nz); // size_t
242
243 chi::WriteBinaryValue(ofile, raw_vertices.size()); // size_t
244
245 //================================================ Write the boundary map
246 const auto& bndry_map = mesh_options.boundary_id_map;
247 chi::WriteBinaryValue(ofile, bndry_map.size()); // size_t
248 for (const auto& [bid, bname] : bndry_map)
249 {
250 chi::WriteBinaryValue(ofile, bid); // uint64_t
251 const size_t num_chars = bname.size();
252 chi::WriteBinaryValue(ofile, num_chars); // size_t
253 ofile.write(bname.data(), scint(num_chars)); // characters
254 }
255
256 //================================================ Write how many cells
257 // and vertices in file
258 chi::WriteBinaryValue(ofile, cells_needed.size()); // size_t
259 chi::WriteBinaryValue(ofile, vertices_needed.size()); // size_t
260
261 //================================================ Write cells
262 const size_t BUFFER_SIZE = 4096 * 2;
263 chi_data_types::ByteArray serial_data;
264 serial_data.Data().reserve(BUFFER_SIZE * 2);
265 for (const auto& cell_global_id : cells_needed)
266 {
267 t_cells.TimeSectionBegin();
268 t_serialize.TimeSectionBegin();
269 const auto& cell = *raw_cells[cell_global_id];
270 serial_data.Write(static_cast<int>(cell_pids[cell_global_id])); // int
271 serial_data.Write(cell_global_id); // uint64_t
272 SerializeCell(cell, serial_data);
273 t_serialize.TimeSectionEnd();
274 if (serial_data.Size() > BUFFER_SIZE)
275 {
276 ofile.write((char*)serial_data.Data().data(),
277 scint(serial_data.Size()));
278 const size_t cap = serial_data.Data().capacity();
279 serial_data.Clear();
280 serial_data.Data().reserve(cap);
281 }
282 t_cells.TimeSectionEnd();
283 }
284 if (serial_data.Size() > 0)
285 {
286 ofile.write((char*)serial_data.Data().data(), scint(serial_data.Size()));
287 serial_data.Clear();
288 }
289
290 //================================================ Write vertices
291 t_verts.TimeSectionBegin();
292 for (const uint64_t vid : vertices_needed)
293 {
294 serial_data.Write(vid); // uint64_t
295 serial_data.Write(raw_vertices[vid]);
296 if (serial_data.Size() > BUFFER_SIZE)
297 {
298 ofile.write((char*)serial_data.Data().data(),
299 scint(serial_data.Size()));
300 serial_data.Clear();
301 }
302 }
303 if (serial_data.Size() > 0)
304 {
305 ofile.write((char*)serial_data.Data().data(), scint(serial_data.Size()));
306 serial_data.Clear();
307 }
308 t_verts.TimeSectionEnd();
309
310 ofile.close();
311 t_write.TimeSectionEnd();
312
313 const double fraction_complete =
314 static_cast<double>(pid) / static_cast<double>(num_parts);
315 if (fraction_complete >= static_cast<double>(aux_counter + 1) * 0.1)
316 {
317 if (verbosity_level_ >= 1)
319 << " Surpassing part " << pid << " of " << num_parts
320 << " (" << (aux_counter + 1) * 10 << "%)";
321 ++aux_counter;
322 }
323 } // for p
324}
325
326// ##################################################################
329 chi_data_types::ByteArray& serial_buffer)
330{
331 serial_buffer.Write(cell.type);
332 serial_buffer.Write(cell.sub_type);
333 serial_buffer.Write(cell.centroid);
334 serial_buffer.Write(cell.material_id);
335 serial_buffer.Write(cell.vertex_ids.size());
336 for (uint64_t vid : cell.vertex_ids)
337 serial_buffer.Write(vid);
338 serial_buffer.Write(cell.faces.size());
339 for (const auto& face : cell.faces)
340 {
341 serial_buffer.Write(face.vertex_ids.size());
342 for (uint64_t vid : face.vertex_ids)
343 serial_buffer.Write(vid);
344 serial_buffer.Write(face.has_neighbor);
345 serial_buffer.Write(face.neighbor);
346 }
347}
348
350{
351 const int pid = Chi::mpi.location_id;
352 const std::filesystem::path dir_path =
353 std::filesystem::absolute(split_mesh_dir_path_);
354 const std::filesystem::path file_path = dir_path.string() + "/" +
355 split_file_prefix_ + "_" +
356 std::to_string(pid) + ".cmesh";
357
358 SplitMeshInfo info_block;
359 auto& cells = info_block.cells_;
360 auto& vertices = info_block.vertices_;
361 std::ifstream ifile(file_path, std::ios_base::binary | std::ios_base::in);
362
363 ChiLogicalErrorIf(not ifile.is_open(),
364 "Failed to open " + file_path.string());
365
366 //================================================== Read mesh attributes
367 // and general info
368 const size_t file_num_parts = chi::ReadBinaryValue<int>(ifile);
369
370 ChiLogicalErrorIf(Chi::mpi.process_count != file_num_parts,
371 "Split mesh files with prefix \"" + split_file_prefix_ +
372 "\" has been created with " +
373 std::to_string(file_num_parts) +
374 " parts but is now being read with " +
375 std::to_string(Chi::mpi.process_count) + " processes.");
376
377 info_block.mesh_attributes_ = chi::ReadBinaryValue<int>(ifile);
378 info_block.ortho_Nx_ = chi::ReadBinaryValue<size_t>(ifile);
379 info_block.ortho_Ny_ = chi::ReadBinaryValue<size_t>(ifile);
380 info_block.ortho_Nz_ = chi::ReadBinaryValue<size_t>(ifile);
381
382 info_block.num_global_vertices_ = chi::ReadBinaryValue<size_t>(ifile);
383
384 //================================================== Read boundary map
385 const size_t num_bndries = chi::ReadBinaryValue<size_t>(ifile);
386 for (size_t b = 0; b < num_bndries; ++b)
387 {
388 const uint64_t bid = chi::ReadBinaryValue<uint64_t>(ifile);
389 const size_t num_chars = chi::ReadBinaryValue<size_t>(ifile);
390 std::string bname(num_chars, ' ');
391 ifile.read(bname.data(), static_cast<int>(num_chars));
392
393 info_block.boundary_id_map_.insert(std::make_pair(bid, bname));
394 }
395
396 //================================================ Write how many cells
397 // and vertices in file
398 const size_t num_cells = chi::ReadBinaryValue<size_t>(ifile);
399 const size_t num_vertices = chi::ReadBinaryValue<size_t>(ifile);
400
401 //================================================== Read the cells
402 for (size_t c = 0; c < num_cells; ++c)
403 {
404 const int cell_pid = chi::ReadBinaryValue<int>(ifile);
405 const uint64_t cell_gid = chi::ReadBinaryValue<uint64_t>(ifile);
406 const CellType cell_type = chi::ReadBinaryValue<CellType>(ifile);
407 const CellType cell_sub_type = chi::ReadBinaryValue<CellType>(ifile);
408
409 UnpartitionedMesh::LightWeightCell new_cell(cell_type, cell_sub_type);
410
411 new_cell.centroid = chi::ReadBinaryValue<chi_mesh::Vector3>(ifile);
412 new_cell.material_id = chi::ReadBinaryValue<int>(ifile);
413
414 const size_t num_vids = chi::ReadBinaryValue<size_t>(ifile);
415 for (size_t v = 0; v < num_vids; ++v)
416 new_cell.vertex_ids.push_back(chi::ReadBinaryValue<uint64_t>(ifile));
417
418 const size_t num_faces = chi::ReadBinaryValue<size_t>(ifile);
419 for (size_t f = 0; f < num_faces; ++f)
420 {
422 const size_t num_face_vids = chi::ReadBinaryValue<size_t>(ifile);
423 for (size_t v = 0; v < num_face_vids; ++v)
424 new_face.vertex_ids.push_back(chi::ReadBinaryValue<uint64_t>(ifile));
425
426 new_face.has_neighbor = chi::ReadBinaryValue<bool>(ifile);
427 new_face.neighbor = chi::ReadBinaryValue<uint64_t>(ifile);
428
429 new_cell.faces.push_back(std::move(new_face));
430 } // for f
431
432 cells.insert(
433 std::make_pair(CellPIDGID(cell_pid, cell_gid), std::move(new_cell)));
434 } // for cell c
435
436 //================================================== Read the vertices
437 for (size_t v = 0; v < num_vertices; ++v)
438 {
439 const uint64_t vid = chi::ReadBinaryValue<uint64_t>(ifile);
440 const chi_mesh::Vector3 vertex =
441 chi::ReadBinaryValue<chi_mesh::Vector3>(ifile);
442 vertices.insert(std::make_pair(vid, vertex));
443 } // for vertex v
444
445 ifile.close();
446
447 return info_block;
448}
449
450std::shared_ptr<MeshContinuum>
452{
453 auto grid_ptr = chi_mesh::MeshContinuum::New();
454
455 grid_ptr->GetBoundaryIDMap() = mesh_info.boundary_id_map_;
456
457 auto& cells = mesh_info.cells_;
458 auto& vertices = mesh_info.vertices_;
459
460 for (const auto& [vid, vertex] : vertices)
461 grid_ptr->vertices.Insert(vid, vertex);
462
463 for (const auto& [pidgid, raw_cell] : cells)
464 {
465 const auto& [cell_pid, cell_global_id] = pidgid;
466 auto cell = SetupCell(
467 raw_cell, cell_global_id, cell_pid, STLVertexListHelper(vertices));
468
469 grid_ptr->cells.push_back(std::move(cell));
470 }
471
473 *grid_ptr,
474 static_cast<MeshAttributes>(mesh_info.mesh_attributes_),
475 {mesh_info.ortho_Nx_, mesh_info.ortho_Ny_, mesh_info.ortho_Nz_});
476
477 grid_ptr->SetGlobalVertexCount(mesh_info.num_global_vertices_);
478
479 ComputeAndPrintStats(*grid_ptr);
480
481 return grid_ptr;
482}
483
484} // namespace chi_mesh
#define scint
#define ChiLogicalErrorIf(condition, message)
#define ChiInvalidArgumentIf(condition, message)
static chi::Timer program_timer
Definition: chi_runtime.h:79
static void Exit(int error_code)
Definition: chi_runtime.cc:342
static chi::ChiLog & log
Definition: chi_runtime.h:81
static int current_mesh_handler
Definition: chi_runtime.h:84
static chi::MPI_Info & mpi
Definition: chi_runtime.h:78
LogStream Log0Warning()
Definition: chi_log.h:231
LogStream Log(LOG_LVL level=LOG_0)
Definition: chi_log.cc:35
void SetDocGroup(const std::string &doc_group)
void AddOptionalParameter(const std::string &name, T value, const std::string &doc_string)
void SetGeneralDescription(const std::string &description)
void Barrier() const
Definition: mpi_info.cc:38
const int & process_count
Total number of processes.
Definition: mpi_info.h:27
const int & location_id
Current process rank.
Definition: mpi_info.h:26
std::string GetTimeString() const
Definition: chi_timer.cc:38
void TimeSectionBegin()
Definition: TimingLog.cc:78
std::string MakeGraphString()
Definition: TimingLog.cc:112
TimingBlock & CreateOrGetTimingBlock(const std::string &name, const std::string &parent_name="")
Definition: TimingLog.cc:53
TimingBlock & GetTimingBlock(const std::string &name)
Definition: TimingLog.cc:63
std::vector< std::byte > & Data()
Definition: byte_array.h:135
void Write(const T &value)
Definition: byte_array.h:34
static std::shared_ptr< MeshContinuum > New()
static void ComputeAndPrintStats(const chi_mesh::MeshContinuum &grid)
static void SetGridAttributes(chi_mesh::MeshContinuum &grid, MeshAttributes new_attribs, std::array< size_t, 3 > ortho_cells_per_dimension)
std::vector< MeshGenerator * > inputs_
static std::unique_ptr< chi_mesh::Cell > SetupCell(const UnpartitionedMesh::LightWeightCell &raw_cell, uint64_t global_id, uint64_t partition_id, const VertexListHelper &vertices)
std::vector< int64_t > PartitionMesh(const UnpartitionedMesh &input_umesh, int num_partitions)
virtual std::unique_ptr< UnpartitionedMesh > GenerateUnpartitionedMesh(std::unique_ptr< UnpartitionedMesh > input_umesh)
static chi::InputParameters GetInputParameters()
static std::shared_ptr< MeshContinuum > SetupLocalMesh(SplitMeshInfo &mesh_info)
static void SerializeCell(const UnpartitionedMesh::LightWeightCell &cell, chi_data_types::ByteArray &serial_buffer)
SplitFileMeshGenerator(const chi::InputParameters &params)
std::pair< int, uint64_t > CellPIDGID
static chi::InputParameters GetInputParameters()
void WriteSplitMesh(const std::vector< int64_t > &cell_pids, const UnpartitionedMesh &umesh, int num_parts)
const std::vector< chi_mesh::Vertex > & GetVertices() const
std::vector< LightWeightCell * > & GetRawCells()
const std::vector< std::set< uint64_t > > & GetVertextCellSubscriptions() const
size_t PushNewHandlerAndGetIndex()
RegisterChiObject(chi_mesh, BooleanLogicalVolume)
CellType
Definition: cell.h:12
MeshAttributes
Definition: chi_mesh.h:70
MeshHandler & GetCurrentHandler()
void WriteBinaryValue(std::ofstream &output_file, T value)
Definition: chi_utils.h:79
std::map< CellPIDGID, UnpartitionedMesh::LightWeightCell > cells_
std::map< uint64_t, std::string > boundary_id_map_
std::map< uint64_t, chi_mesh::Vector3 > vertices_