Chi-Tech
ExtruderMeshGenerator.cc
Go to the documentation of this file.
2
3#include "ChiObjectFactory.h"
4
5#include "chi_log.h"
6
7namespace chi_mesh
8{
9
11
13
15{
17
18 // clang-format off
20 "A collection of parameters defining an extrusion layer.");
21 // clang-format on
22 params.SetDocGroup("doc_MeshGenerators");
23
25 "h", 1.0, "Layer height. Cannot be specified if \"z\" is specified.");
26 params.AddOptionalParameter("n", 1, "Number of sub-layers");
27 params.AddOptionalParameter("z",
28 0.0,
29 "The z-coordinate at the top of the layer. "
30 "Cannot be specified if \"n\" is specified.");
31
32 using namespace chi_data_types;
33 params.ConstrainParameterRange("n", AllowableRangeLowLimit::New(0, false));
34
35 return params;
36}
37
39{
41
42 // clang-format off
44"Extrudes 2D geometry. Extrusion layers are"
45" specified using an \\ref chi_mesh__ExtrusionLayer specification which takes "
46"either pairs of"
47" parameters: Pair A = \"n\" and \"z\", or Pair B = \"n\" and \"h\". When pair "
48"A is used then the z-levels will be computed automatically. Vice versa, when "
49"pair B is used then the h-levels will be computed automatically. Layers can be"
50" specified with a mixture of Pair A and Pair B. For example: Two main layers,"
51" one specified using a height, and the other specified using a z-level.");
52 // clang-format on
53 params.SetDocGroup("MeshGenerator");
54
55 params.AddRequiredParameterArray("layers", "A list of layers");
56 params.LinkParameterToBlock("layers", "chi_mesh::ExtrusionLayer");
57
58 params.AddOptionalParameter("top_boundary_name",
59 "ZMAX",
60 "The name to associate with the top boundary.");
62 "bottom_boundary_name",
63 "ZMIN",
64 "The name to associate with the bottom boundary.");
65
66 return params;
67}
68
70 : MeshGenerator(params),
71 top_boundary_name_(params.GetParamValue<std::string>("top_boundary_name")),
72 bottom_boundary_name_(
73 params.GetParamValue<std::string>("bottom_boundary_name"))
74{
75 const auto& layers_param = params.GetParam("layers");
76
77 double current_z_level = 0.0;
78 for (const auto& layer_block : layers_param)
79 {
80 auto valid_params = ExtrusionLayer::GetInputParameters();
81 valid_params.SetErrorOriginScope("ExtruderMeshGenerator:\"layers\"");
82 valid_params.AssignParameters(layer_block);
83
84 const int h_and_z_config =
85 int(layer_block.Has("h")) + int(layer_block.Has("z"));
86
87 if (h_and_z_config != 1)
88 ChiInvalidArgument("For an ExtrusionLayer either \"h\" or \"z\" must"
89 "be specified and also not both.");
90
91 auto n = valid_params.GetParamValue<uint32_t>("n");
92 double h;
93 if (layer_block.Has("h")) h = valid_params.GetParamValue<double>("h");
94 else
95 {
96 double z = valid_params.GetParamValue<double>("z");
97 ChiInvalidArgumentIf(z <= current_z_level,
98 "For extrusion layers, the \"z\" coordinates must "
99 "be monotonically increasing.");
100 h = z - current_z_level;
101 }
102
103 current_z_level += h;
104
105 layers_.push_back(ExtrusionLayer{h, n});
106
108 << "Layer " << layer_block.Name() << " height=" << h
109 << " num_sub_layers=" << n << " top-z=" << current_z_level;
110 } // layer_block in layers_param
111}
112
113// ##################################################################
114std::unique_ptr<UnpartitionedMesh>
116 std::unique_ptr<UnpartitionedMesh> input_umesh)
117{
118 Chi::log.Log0Verbose1() << "ExtruderMeshGenerator::GenerateUnpartitionedMesh";
119 const chi_mesh::Vector3 khat(0.0, 0.0, 1.0);
120
122 not(input_umesh->GetMeshAttributes() & DIMENSION_2),
123 "Input mesh is not 2D. A 2D mesh is required for extrusion");
124
125 const auto& template_vertices = input_umesh->GetVertices();
126 const auto& template_cells = input_umesh->GetRawCells();
127
128 const size_t num_template_vertices = template_vertices.size();
129 const size_t num_template_cells = template_cells.size();
130
131 ChiLogicalErrorIf(template_vertices.empty(), "Input mesh has no vertices.");
132 ChiLogicalErrorIf(template_cells.empty(), "Input mesh has no cells.");
133
134 //============================================= Check cells
135 for (const auto& template_cell_ptr : template_cells)
136 {
137 const auto& template_cell = *template_cell_ptr;
138 if (template_cell.type != chi_mesh::CellType::POLYGON)
139 throw std::logic_error("ExtruderMeshGenerator: "
140 "Template cell error. Not of base type POLYGON");
141
142 // Check cell not inverted
143 const auto& v0 = template_cell.centroid;
144 const auto& v1 = template_vertices[template_cell.vertex_ids[0]];
145 const auto& v2 = template_vertices[template_cell.vertex_ids[1]];
146
147 auto v01 = v1 - v0;
148 auto v02 = v2 - v0;
149
150 if (v01.Cross(v02).Dot(khat) < 0.0)
151 throw std::logic_error("Extruder attempting to extrude a template"
152 " cell with a normal pointing downward. This"
153 " causes erratic behavior and needs to be"
154 " corrected.");
155 }
156
157 auto umesh = std::make_unique<UnpartitionedMesh>();
158
159 //============================================= Update boundary maps
160 auto& umesh_bndry_map = umesh->GetMeshOptions().boundary_id_map;
161 umesh_bndry_map = input_umesh->GetMeshOptions().boundary_id_map;
162
163 const uint64_t zmax_bndry_id = umesh->MakeBoundaryID(top_boundary_name_);
164 umesh_bndry_map[zmax_bndry_id] = top_boundary_name_;
165 const uint64_t zmin_bndry_id = umesh->MakeBoundaryID(bottom_boundary_name_);
166 umesh_bndry_map[zmin_bndry_id] = bottom_boundary_name_;
167
168 //============================================= Setup z-levels
169 double current_z = 0.0;
170 std::vector<double> z_levels = {current_z};
171 for (const auto& layer : layers_)
172 {
173 const double dz = layer.height_ / layer.num_sub_layers_;
174 for (uint32_t i = 0; i < layer.num_sub_layers_; ++i)
175 z_levels.push_back(current_z += dz);
176 }
177
178 //============================================= Build vertices
179 typedef chi_mesh::Vector3 Vec3;
180 auto& extruded_vertices = umesh->GetVertices();
181 for (const double z_level : z_levels)
182 for (const auto& template_vertex : template_vertices)
183 extruded_vertices.push_back(
184 Vec3(template_vertex.x, template_vertex.y, z_level));
185
186 //============================================= Build cells
187 size_t k = 0;
188 for (const auto& layer : layers_)
189 {
190 for (uint32_t n = 0; n < layer.num_sub_layers_; ++n)
191 {
192 size_t tc_counter = 0;
193 for (const auto& template_cell : template_cells)
194 {
195 //================================== Determine cell sub-type
196 CellType extruded_subtype;
197 // clang-format off
198 switch (template_cell->sub_type)
199 {
200 case CellType::TRIANGLE: extruded_subtype = CellType::WEDGE; break;
201 case CellType::QUADRILATERAL: extruded_subtype = CellType::HEXAHEDRON; break;
202 default: extruded_subtype = CellType::POLYHEDRON;
203 }
204 // clang-format on
205
206 //================================== Create new cell
207 auto new_cell_ptr = new UnpartitionedMesh::LightWeightCell(
208 CellType::POLYHEDRON, extruded_subtype);
209 auto& new_cell = *new_cell_ptr;
210
211 new_cell.material_id = template_cell->material_id;
212
213 //================================== Build vertices
214 const size_t tc_num_verts = template_cell->vertex_ids.size();
215 new_cell.vertex_ids.reserve(2 * tc_num_verts);
216 for (const auto tc_vid : template_cell->vertex_ids)
217 new_cell.vertex_ids.push_back(tc_vid + k * num_template_vertices);
218 for (const auto tc_vid : template_cell->vertex_ids)
219 new_cell.vertex_ids.push_back(tc_vid +
220 (k + 1) * num_template_vertices);
221
222 //================================== Create side faces
223 for (const auto& tc_face : template_cell->faces)
224 {
226
227 // clang-format off
228 new_face.vertex_ids.resize(4, -1);
229 new_face.vertex_ids[0] = tc_face.vertex_ids[0] + k * num_template_vertices;
230 new_face.vertex_ids[1] = tc_face.vertex_ids[1] + k * num_template_vertices;
231 new_face.vertex_ids[2] = tc_face.vertex_ids[1] + (k + 1) * num_template_vertices;
232 new_face.vertex_ids[3] = tc_face.vertex_ids[0] + (k + 1) * num_template_vertices;
233 // clang-format on
234
235 if (tc_face.has_neighbor)
236 {
237 new_face.neighbor = num_template_cells * k + tc_face.neighbor;
238 new_face.has_neighbor = true;
239 }
240 else
241 {
242 new_face.neighbor = tc_face.neighbor;
243 new_face.has_neighbor = false;
244 }
245
246 new_cell.faces.push_back(std::move(new_face));
247 } // for tc face
248
249 //================================== Create top and bottom faces
250 // Top face
251 {
253
254 new_face.vertex_ids.reserve(template_cell->vertex_ids.size());
255 for (auto vid : template_cell->vertex_ids)
256 new_face.vertex_ids.push_back(vid +
257 (k + 1) * num_template_vertices);
258
259 if (k == (z_levels.size()-2))
260 {
261 new_face.neighbor = zmax_bndry_id;
262 new_face.has_neighbor = false;
263 }
264 else
265 {
266 new_face.neighbor = num_template_cells * (k+1) + tc_counter;
267 new_face.has_neighbor = true;
268 }
269
270 new_cell.faces.push_back(std::move(new_face));
271 }
272
273 // Bottom face
274 {
276
277 new_face.vertex_ids.reserve(template_cell->vertex_ids.size());
278 auto& vs = template_cell->vertex_ids;
279 for (auto vid = vs.rbegin(); vid != vs.rend(); ++vid)
280 new_face.vertex_ids.push_back((*vid) + k * num_template_vertices);
281
282 if (k == 0)
283 {
284 new_face.neighbor = zmin_bndry_id;
285 new_face.has_neighbor = false;
286 }
287 else
288 {
289 new_face.neighbor = num_template_cells * (k-1) + tc_counter;
290 new_face.has_neighbor = true;
291 }
292
293 new_cell.faces.push_back(std::move(new_face));
294 }
295 umesh->GetRawCells().push_back(new_cell_ptr);
296
297 ++tc_counter;
298 } // for template cell
299 ++k;
300 } // for sub-layer n
301 } // for layer
302
303 umesh->SetAttributes(DIMENSION_3 | EXTRUDED);
304
305 umesh->ComputeCentroidsAndCheckQuality();
306 umesh->BuildMeshConnectivity();
307
309 << "ExtruderMeshGenerator::GenerateUnpartitionedMesh Done";
310 return umesh;
311}
312
313} // namespace chi_mesh
#define ChiLogicalErrorIf(condition, message)
#define ChiInvalidArgumentIf(condition, message)
#define ChiInvalidArgument(message)
static chi::ChiLog & log
Definition: chi_runtime.h:81
LogStream Log0Verbose1()
Definition: chi_log.h:234
void SetDocGroup(const std::string &doc_group)
void AddRequiredParameterArray(const std::string &name, const std::string &doc_string)
void ConstrainParameterRange(const std::string &param_name, AllowableRangePtr allowable_range)
void AddOptionalParameter(const std::string &name, T value, const std::string &doc_string)
void SetGeneralDescription(const std::string &description)
void LinkParameterToBlock(const std::string &param_name, const std::string &block_name)
ParameterBlock & GetParam(const std::string &param_name)
std::unique_ptr< UnpartitionedMesh > GenerateUnpartitionedMesh(std::unique_ptr< UnpartitionedMesh > input_umesh) override
static chi::InputParameters GetInputParameters()
ExtruderMeshGenerator(const chi::InputParameters &params)
std::vector< ExtrusionLayer > layers_
static chi::InputParameters GetInputParameters()
RegisterChiObject(chi_mesh, BooleanLogicalVolume)
CellType
Definition: cell.h:12
@ EXTRUDED
Definition: chi_mesh.h:76
@ DIMENSION_2
Definition: chi_mesh.h:73
@ DIMENSION_3
Definition: chi_mesh.h:74
RegisterChiObjectParametersOnly(chi_mesh, ExtrusionLayer)
static chi::InputParameters GetInputParameters()