Chi-Tech
chi_meshcontinuum_utilities.cc
Go to the documentation of this file.
1#include "chi_meshcontinuum.h"
2#include "mesh/Cell/cell.h"
3
6
8
9#include "chi_runtime.h"
10#include "chi_log.h"
11
12#include "chi_mpi.h"
13
14#include <algorithm>
15
16// ###################################################################
17/**Populates a face histogram.
18 *
19 * \param master_tolerance Multiple histograms will only be attempted
20 * if the ratio of the maximum dofs-per-face to the average dofs-per-face
21 * is greater than this value. Default 1.2.
22 *
23 * \param slave_tolerance While traversing a sorted list of dofs-per-face,
24 * a new bin will only be generated when the ratio of the listed dofs-per-face
25 * to a running bin average exceeds this value. Defualt 1.1.
26 *
27 * The function populates face_categories which is a structure containing
28 * pairs. Pair.first is the max dofs-per-face for the category and Pair.second
29 * is the number of faces in this category.
30 *
31 * */
32std::shared_ptr<chi_mesh::GridFaceHistogram>
34 double slave_tolerance) const
35{
36 std::vector<std::pair<size_t, size_t>> face_categories_list;
37 //================================================== Fill histogram
38 std::vector<size_t> face_size_histogram;
39 for (const auto& cell : local_cells)
40 for (const auto& face : cell.faces_)
41 face_size_histogram.push_back(face.vertex_ids_.size());
42
43 std::stable_sort(face_size_histogram.begin(), face_size_histogram.end());
44
45 //================================================== Determine total face dofs
46 size_t total_face_dofs_count = 0;
47 for (auto face_size : face_size_histogram)
48 total_face_dofs_count += face_size;
49
50 //================================================== Compute average and ratio
51 size_t smallest_face = face_size_histogram.front();
52 size_t largest_face = face_size_histogram.back();
53 size_t total_num_faces = face_size_histogram.size();
54 double average_dofs_per_face =
55 (double)total_face_dofs_count / (double)total_num_faces;
56
57 std::stringstream outstr;
58 outstr << "\nSmallest face = " << smallest_face;
59 outstr << "\nLargest face = " << largest_face;
60 outstr << "\nTotal face dofs = " << total_face_dofs_count;
61 outstr << "\nTotal faces = " << face_size_histogram.size();
62 outstr << "\nAverage dofs/face = " << average_dofs_per_face;
63 outstr << "\nMax to avg ratio = "
64 << (double)largest_face / average_dofs_per_face;
65 Chi::log.LogAllVerbose2() << outstr.str();
66
67 //================================================== Determine number of bins
68 size_t last_bin_num_faces = total_num_faces;
69 if (((double)largest_face / average_dofs_per_face) > master_tolerance)
70 {
72 << "The ratio of max face dofs to average face dofs "
73 << "is larger than " << master_tolerance
74 << ", therefore a binned histogram "
75 << "will be constructed.";
76
77 //====================================== Build categories
78 size_t running_total_face_dofs = 0;
79 size_t running_face_count = 0;
80 size_t running_face_size = face_size_histogram[0];
81
82 double running_average = (double)face_size_histogram[0];
83
84 for (size_t f = 0; f < total_num_faces; ++f)
85 {
86 if (((double)face_size_histogram[f] / running_average) > slave_tolerance)
87 {
88 face_categories_list.emplace_back(running_face_size,
89 running_face_count);
90 running_total_face_dofs = 0;
91 running_face_count = 0;
92 }
93
94 running_face_size = face_size_histogram[f];
95 running_total_face_dofs += face_size_histogram[f];
96 running_face_count++;
97 running_average =
98 (double)running_total_face_dofs / double(running_face_count);
99 last_bin_num_faces = running_face_count;
100 }
101 }
102 face_categories_list.emplace_back(largest_face, last_bin_num_faces);
103
104 //================================================== Verbose print bins
105 outstr.str(std::string());
106 outstr << "A total of " << face_categories_list.size()
107 << " bins were created:\n";
108
109 size_t bin_counter = -1;
110 for (auto bins : face_categories_list)
111 {
112 outstr << "Bin " << ++bin_counter << ": " << bins.second
113 << " faces with max face dofs " << bins.first << "\n";
114 }
115
116 Chi::log.LogAllVerbose2() << outstr.str();
117
118 return std::make_shared<GridFaceHistogram>(face_categories_list);
119}
120
121// ###################################################################
122/**Check whether a cell is local by attempting to find the key in
123 * the native index map.*/
124bool chi_mesh::MeshContinuum::IsCellLocal(uint64_t cell_global_index) const
125{
126 auto native_index = global_cell_id_to_local_id_map_.find(cell_global_index);
127
128 if (native_index != global_cell_id_to_local_id_map_.end()) return true;
129
130 return false;
131}
132
133// ###################################################################
134/**Check whether a cell is a boundary by checking if the key is
135 * found in the native or foreign cell maps.*/
137{
138 switch (cell.Type())
139 {
140 case CellType::POINT:
141 case CellType::GHOST:
142 return 0;
143 case CellType::SLAB:
144 return 1;
148 return 2;
151 case CellType::WEDGE:
154 return 3;
155 default:
156 throw std::logic_error("chi_mesh::MeshContinuum::GetCellDimension: "
157 "Dimension mapping unavailable for cell type.");
158 }
159 return false;
160}
161
162// ###################################################################
163/**General map vertices*/
165 const chi_mesh::CellFace& cur_face, std::vector<short>& dof_mapping) const
166{
167 const int associated_face = cur_face.GetNeighborAssociatedFace(*this);
168 //======================================== Check face validity
169 ChiLogicalErrorIf(not cur_face.has_neighbor_,
170 "Invalid cell index encountered in call to "
171 "MeshContinuum::FindAssociatedVertices. Index "
172 "points to a boundary");
173
174 auto& adj_cell = cells[cur_face.neighbor_id_];
175
176 dof_mapping.reserve(cur_face.vertex_ids_.size());
177
178 const auto& adj_face = adj_cell.faces_[associated_face];
179
180 for (auto cfvid : cur_face.vertex_ids_)
181 {
182 bool found = false;
183 short afv = 0;
184 for (auto afvid : adj_face.vertex_ids_)
185 {
186 if (cfvid == afvid)
187 {
188 dof_mapping.push_back((short)afv);
189 found = true;
190 break;
191 }
192 afv++;
193 }
194
195 if (!found)
196 {
198 << "Face DOF mapping failed in call to "
199 << "MeshContinuum::FindAssociatedVertices. Could not find a matching"
200 "node."
201 << cur_face.neighbor_id_ << " " << cur_face.centroid_.PrintS();
202 Chi::Exit(EXIT_FAILURE);
203 }
204 }
205}
206
207// ###################################################################
208/**General map vertices*/
210 const chi_mesh::CellFace& cur_face, std::vector<short>& dof_mapping) const
211{
212 //======================================== Check face validity
213 ChiLogicalErrorIf(not cur_face.has_neighbor_,
214 "Invalid cell index encountered in call to "
215 "MeshContinuum::FindAssociatedVertices. Index "
216 "points to a boundary");
217
218 auto& adj_cell = cells[cur_face.neighbor_id_];
219
220 dof_mapping.reserve(cur_face.vertex_ids_.size());
221
222 for (auto cfvid : cur_face.vertex_ids_)
223 {
224 bool found = false;
225 short acv = 0;
226 for (auto acvid : adj_cell.vertex_ids_)
227 {
228 if (cfvid == acvid)
229 {
230 dof_mapping.push_back(acv);
231 found = true;
232 break;
233 }
234 ++acv;
235 }
236
237 if (!found)
238 {
240 << "Face DOF mapping failed in call to "
241 << "MeshContinuum::FindAssociatedVertices. Could not find a matching"
242 "node."
243 << cur_face.neighbor_id_ << " " << cur_face.centroid_.PrintS();
244 Chi::Exit(EXIT_FAILURE);
245 }
246 }
247}
248
249// ###################################################################
250/**Given the current cell, cell A, and its adjacent cell, cell B, with
251 * cell B adjacent to A at the `f`-th face of cell A. Will determine the
252 * `af`-th index of the face on cell B that interface with the `f`-th face
253 * of cell A.*/
255 const chi_mesh::Cell& adj_cell,
256 unsigned int f)
257{
258 const auto& ccface = cur_cell.faces_[f]; // current cell face
259 std::set<uint64_t> ccface_vids;
260 for (auto vid : ccface.vertex_ids_)
261 ccface_vids.insert(vid);
262
263 size_t fmap;
264 bool map_found = false;
265 for (size_t af = 0; af < adj_cell.faces_.size(); af++)
266 {
267 const auto& acface = adj_cell.faces_[af]; // adjacent cell face
268
269 std::set<uint64_t> acface_vids;
270 for (auto vid : acface.vertex_ids_)
271 acface_vids.insert(vid);
272
273 if (acface_vids == ccface_vids)
274 {
275 fmap = af;
276 map_found = true;
277 break;
278 }
279 } // for adj faces
280
281 if (not map_found)
282 throw std::logic_error(
283 "chi_mesh::MeshContinuum::MapCellFace: Mapping failure.");
284
285 return fmap;
286}
287
288// ###################################################################
289/**Given a global-id of a cell, will return the local-id if the
290 * cell is local, otherwise will throw logic_error.*/
291size_t
293{
294 return global_cell_id_to_local_id_map_.at(global_id);
295}
296
297// ###################################################################
298/**Computes the centroid from nodes specified by the given list.*/
300 const std::vector<uint64_t>& list) const
301{
302 if (list.empty())
303 {
304 Chi::log.LogAllError() << "ComputeCentroidFromListOfNodes, empty list";
305 Chi::Exit(EXIT_FAILURE);
306 }
307 chi_mesh::Vector3 centroid;
308 for (auto node_id : list)
309 centroid = centroid + vertices[node_id];
310
311 return centroid / double(list.size());
312}
313
314// ###################################################################
315/**Counts the number of cells within a logical volume across all
316 * partitions.*/
318 const chi_mesh::LogicalVolume& log_vol) const
319{
320 size_t local_count = 0;
321 for (const auto& cell : local_cells)
322 if (log_vol.Inside(cell.centroid_)) ++local_count;
323
324 size_t global_count = 0;
325
326 MPI_Allreduce(&local_count, // sendbuf
327 &global_count, // recvbuf
328 1, // count
329 MPI_UNSIGNED_LONG_LONG, // datatype
330 MPI_SUM, // op
331 Chi::mpi.comm); // communicator
332
333 return global_count;
334}
335
336// ###################################################################
337/**Checks whether a point is within a cell.*/
339 const chi_mesh::Cell& cell, const chi_mesh::Vector3& point) const
340{
341 const auto& grid_ref = *this;
342 typedef chi_mesh::Vector3 Vec3;
343 auto InsideTet =
344 [](const Vec3& point, const Vec3& v0, const Vec3& v1, const Vec3& v2)
345 {
346 const auto& v01 = v1 - v0;
347 const auto& v02 = v2 - v0;
348
349 const auto n = v01.Cross(v02).Normalized();
350 const auto c = (v0 + v1 + v2) / 3.0;
351
352 const auto pc = point - c;
353
354 if (pc.Dot(n) > 0.0) return true;
355 else
356 return false;
357 };
358
359 bool inside = true;
360 if (cell.Type() == chi_mesh::CellType::SLAB)
361 {
362 const auto& v0 = grid_ref.vertices[cell.vertex_ids_[0]];
363 const auto& v1 = grid_ref.vertices[cell.vertex_ids_[1]];
364
365 const auto v01 = v1 - v0;
366 const auto v0p = point - v0;
367
368 const double v0p_dot_v01 = v0p.Dot(v01);
369
370 if (not(v0p_dot_v01 >= 0 and v0p_dot_v01 < v01.Norm())) inside = false;
371 } // slab
372
373 else if (cell.Type() == chi_mesh::CellType::POLYGON)
374 {
375 for (const auto& face : cell.faces_)
376 {
377 const auto& vcp = point - face.centroid_;
378
379 if (vcp.Dot(face.normal_) > 0)
380 {
381 inside = false;
382 break;
383 }
384 } // for face
385 } // polygon
386
387 else if (cell.Type() == chi_mesh::CellType::POLYHEDRON)
388 {
389 inside = false;
390 // form tetra hedrons
391 const auto& vcc = cell.centroid_;
392 for (const auto& face : cell.faces_)
393 {
394 const auto& vfc = face.centroid_;
395
396 const size_t num_sides = face.vertex_ids_.size();
397 for (size_t s = 0; s < num_sides; ++s)
398 {
399 const size_t sp1 = (s < (num_sides - 1)) ? s + 1 : 0;
400 const auto& v0 = grid_ref.vertices[face.vertex_ids_[s]];
401 const auto& v1 = vfc;
402 const auto& v2 = grid_ref.vertices[face.vertex_ids_[sp1]];
403 const auto& v3 = vcc;
404
405 typedef std::tuple<Vec3, Vec3, Vec3> TetFace;
406
407 std::vector<TetFace> tet_faces;
408 tet_faces.emplace_back(v0, v1, v2);
409 tet_faces.emplace_back(v0, v2, v3);
410 tet_faces.emplace_back(v1, v3, v2);
411 tet_faces.emplace_back(v0, v3, v1);
412
413 bool inside_tet = true;
414 for (const auto& tet_face : tet_faces)
415 {
416 if (not InsideTet(point,
417 std::get<0>(tet_face),
418 std::get<1>(tet_face),
419 std::get<2>(tet_face)))
420 {
421 inside_tet = false;
422 break;
423 }
424 } // for triangular tet_face
425 if (inside_tet)
426 {
427 inside = true;
428 break;
429 }
430 } // for side
431 if (inside) break;
432 } // for face
433 } // polyhedron
434 else
435 throw std::logic_error("chi_mesh::MeshContinuum::CheckPointInsideCell: "
436 "Unsupported cell-type encountered.");
437
438 return inside;
439}
440
441// ###################################################################
442/**Gets and orthogonal mesh interface object.*/
443std::array<size_t, 3> chi_mesh::MeshContinuum::GetIJKInfo() const
444{
445 const std::string fname = "GetIJKInfo";
446 if (not(this->Attributes() & MeshAttributes::ORTHOGONAL))
447 throw std::logic_error(fname + " can only be run on orthogonal meshes.");
448
449 return {ortho_attributes.Nx, ortho_attributes.Ny, ortho_attributes.Nz};
450}
451
452// ###################################################################
453/**Provides a mapping from cell ijk indices to global ids.*/
456{
457 const std::string fname = "MakeIJKToGlobalIDMapping";
458 if (not(this->Attributes() & MeshAttributes::ORTHOGONAL))
459 throw std::logic_error(fname + " can only be run on orthogonal meshes.");
460
461 const auto ijk_info = this->GetIJKInfo();
462 const auto Nx = static_cast<int64_t>(ijk_info[0]);
463 const auto Ny = static_cast<int64_t>(ijk_info[1]);
464 const auto Nz = static_cast<int64_t>(ijk_info[2]);
465
466 chi_data_types::NDArray<uint64_t> m_ijk_to_i({Nx, Ny, Nz});
467 for (int i = 0; i < Nx; ++i)
468 for (int j = 0; j < Ny; ++j)
469 for (int k = 0; k < Nz; ++k)
470 m_ijk_to_i(i, j, k) =
471 static_cast<uint64_t>(m_ijk_to_i.MapNDtoLin(i, j, k));
472
473 return m_ijk_to_i;
474}
475
476// ###################################################################
477/**Determines the bounding box size of each cell and returns it as
478 * a list of 3-component vectors, one Vec3 for each cell.*/
479std::vector<chi_mesh::Vector3>
481{
482 std::vector<chi_mesh::Vector3> cell_ortho_sizes(local_cells.size());
483 for (const auto& cell : local_cells)
484 {
485 chi_mesh::Vector3 vmin = vertices[cell.vertex_ids_.front()];
486 chi_mesh::Vector3 vmax = vmin;
487
488 for (const auto vid : cell.vertex_ids_)
489 {
490 const auto& vertex = vertices[vid];
491 vmin.x = std::min(vertex.x, vmin.x);
492 vmin.y = std::min(vertex.y, vmin.y);
493 vmin.z = std::min(vertex.z, vmin.z);
494
495 vmax.x = std::max(vertex.x, vmax.x);
496 vmax.y = std::max(vertex.y, vmax.y);
497 vmax.z = std::max(vertex.z, vmax.z);
498 }
499
500 cell_ortho_sizes[cell.local_id_] = vmax - vmin;
501 } // for cell
502
503 return cell_ortho_sizes;
504}
505
506// ###################################################################
507/**Makes a bndry id given a name. If the bndry name already exists,
508 * the associated bndry id will be returned. Other the id will be set
509 * to one more than the maximum boundary id.*/
510uint64_t
511chi_mesh::MeshContinuum::MakeBoundaryID(const std::string& boundary_name) const
512{
513 if (boundary_id_map_.empty()) return 0;
514
515 for (const auto& [id, name] : boundary_id_map_)
516 if (boundary_name == name) return id;
517
518 uint64_t max_id = 0;
519 for (const auto& [id, name] : boundary_id_map_)
520 max_id = std::max(id, max_id);
521
522 return max_id + 1;
523}
524
525std::pair<chi_mesh::Vector3, chi_mesh::Vector3>
527{
528 chi_mesh::Vector3 xyz_min;
529 chi_mesh::Vector3 xyz_max;
530
531 auto Vec3Min =
532 [](const chi_mesh::Vector3& xyz_A, const chi_mesh::Vector3& xyz_B)
533 {
534 return chi_mesh::Vector3(std::min(xyz_A.x, xyz_B.x),
535 std::min(xyz_A.y, xyz_B.y),
536 std::min(xyz_A.z, xyz_B.z));
537 };
538 auto Vec3Max =
539 [](const chi_mesh::Vector3& xyz_A, const chi_mesh::Vector3& xyz_B)
540 {
541 return chi_mesh::Vector3(std::max(xyz_A.x, xyz_B.x),
542 std::max(xyz_A.y, xyz_B.y),
543 std::max(xyz_A.z, xyz_B.z));
544 };
545
546 bool initialized = false;
547 for (const auto& cell : local_cells)
548 {
549 for (const uint64_t vid : cell.vertex_ids_)
550 {
551 const auto& vertex = vertices[vid];
552 if (not initialized)
553 {
554 xyz_min = vertex;
555 xyz_max = vertex;
556 initialized = true;
557 }
558 xyz_min = Vec3Min(xyz_min, vertex);
559 xyz_max = Vec3Max(xyz_max, vertex);
560 }
561 }
562 return {xyz_min, xyz_max};
563}
#define ChiLogicalErrorIf(condition, message)
static void Exit(int error_code)
Definition: chi_runtime.cc:342
static chi::ChiLog & log
Definition: chi_runtime.h:81
static chi::MPI_Info & mpi
Definition: chi_runtime.h:78
LogStream LogAllError()
Definition: chi_log.h:239
LogStream LogAllVerbose2()
Definition: chi_log.h:242
bool has_neighbor_
Flag indicating whether face has a neighbor.
Definition: cell.h:41
uint64_t neighbor_id_
Otherwise contains boundary_id.
Definition: cell.h:42
std::vector< uint64_t > vertex_ids_
Definition: cell.h:38
Vertex centroid_
The face centroid.
Definition: cell.h:40
int GetNeighborAssociatedFace(const chi_mesh::MeshContinuum &grid) const
Definition: cell.cc:128
std::vector< CellFace > faces_
Definition: cell.h:82
CellType Type() const
Definition: cell.h:98
Vertex centroid_
Definition: cell.h:78
std::vector< uint64_t > vertex_ids_
Definition: cell.h:81
virtual bool Inside(const chi_mesh::Vector3 &point) const
Definition: LogicalVolume.h:20
bool CheckPointInsideCell(const chi_mesh::Cell &cell, const chi_mesh::Vector3 &point) const
chi_mesh::Vector3 ComputeCentroidFromListOfNodes(const std::vector< uint64_t > &list) const
std::shared_ptr< GridFaceHistogram > MakeGridFaceHistogram(double master_tolerance=100.0, double slave_tolerance=1.1) const
LocalCellHandler local_cells
void FindAssociatedCellVertices(const chi_mesh::CellFace &cur_face, std::vector< short > &dof_mapping) const
std::array< size_t, 3 > GetIJKInfo() const
std::vector< chi_mesh::Vector3 > MakeCellOrthoSizes() const
chi_data_types::NDArray< uint64_t > MakeIJKToGlobalIDMapping() const
static int GetCellDimension(const chi_mesh::Cell &cell)
bool IsCellLocal(uint64_t cell_global_index) const
std::pair< chi_mesh::Vector3, chi_mesh::Vector3 > GetLocalBoundingBox() const
uint64_t MakeBoundaryID(const std::string &boundary_name) const
size_t MapCellGlobalID2LocalID(uint64_t global_id) const
static size_t MapCellFace(const chi_mesh::Cell &cur_cell, const chi_mesh::Cell &adj_cell, unsigned int f)
size_t CountCellsInLogicalVolume(const chi_mesh::LogicalVolume &log_vol) const
void FindAssociatedVertices(const chi_mesh::CellFace &cur_face, std::vector< short > &dof_mapping) const
VectorN< 3 > Vector3
@ ORTHOGONAL
Definition: chi_mesh.h:75
std::string PrintS() const
double x
Element-0.
Vector3 Cross(const Vector3 &that) const
Vector3 Normalized() const
Vector3 Dot(const chi_mesh::TensorRank2Dim3 &that) const
double y
Element-1.
double z
Element-2.