Serac  0.1
Serac is an implicit thermal strucural mechanics simulation code.
mesh_utils.cpp
1 // Copyright (c) 2019-2024, Lawrence Livermore National Security, LLC and
2 // other Serac Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
8 
9 #include <fstream>
10 
11 #include "axom/core.hpp"
12 #include "axom/fmt.hpp"
13 
16 
17 namespace serac {
18 
19 mfem::Mesh buildMeshFromFile(const std::string& mesh_file)
20 {
21  // Open the mesh
22  std::string msg = axom::fmt::format("Opening mesh file: '{0}'", mesh_file);
23  SLIC_INFO_ROOT(msg);
24 
25  // Ensure correctness
26  serac::logger::flush();
27  if (!axom::utilities::filesystem::pathExists(mesh_file)) {
28  msg = axom::fmt::format("Given mesh file does not exist: '{0}'", mesh_file);
29  SLIC_ERROR_ROOT(msg);
30  }
31 
32  // This inherits from std::ifstream, and will work the same way as a std::ifstream,
33  // but is required for Exodus meshes
34  mfem::named_ifgzstream imesh(mesh_file);
35 
36  if (!imesh) {
37  serac::logger::flush();
38  std::string err_msg = axom::fmt::format("Can not open mesh file: '{0}'", mesh_file);
39  SLIC_ERROR_ROOT(err_msg);
40  }
41 
42  return mfem::Mesh{imesh, 1, 1, true};
43 }
44 
50 void squish(mfem::Mesh& mesh)
51 {
52  int num_vertices = mesh.GetNV();
53  int dim = mesh.SpaceDimension();
54 
55  mfem::Vector vertices;
56  mesh.GetVertices(vertices);
57  mfem::Vector vertex(dim);
58  for (int i = 0; i < num_vertices; i++) {
59  for (int d = 0; d < dim; d++) {
60  vertex(d) = vertices[d * num_vertices + i];
61  }
62 
63  double L1_norm = vertex.Norml1();
64  double L2_norm = vertex.Norml2();
65  vertex *= (L2_norm < 1.0e-6) ? 0.0 : (L1_norm / L2_norm);
66 
67  for (int d = 0; d < dim; d++) {
68  vertices[d * num_vertices + i] = vertex(d);
69  }
70  }
71  mesh.SetVertices(vertices);
72 }
73 
74 mfem::Mesh buildDiskMesh(int approx_number_of_elements)
75 {
76  static constexpr int dim = 2;
77  static constexpr int num_elems = 4;
78  static constexpr int num_vertices = 5;
79  static constexpr int num_boundary_elements = 4;
80 
81  static constexpr double vertices[num_vertices][dim] = {{0, 0}, {1, 0}, {0, 1}, {-1, 0}, {0, -1}};
82  static constexpr int triangles[num_elems][3] = {{1, 2, 0}, {2, 3, 0}, {3, 4, 0}, {4, 1, 0}};
83  static constexpr int segments[num_elems][2] = {{1, 2}, {2, 3}, {3, 4}, {4, 1}};
84 
85  auto mesh = mfem::Mesh(dim, num_vertices, num_elems, num_boundary_elements);
86 
87  for (auto vertex : vertices) {
88  mesh.AddVertex(vertex);
89  }
90  for (auto triangle : triangles) {
91  mesh.AddTriangle(triangle);
92  }
93  for (auto segment : segments) {
94  mesh.AddBdrSegment(segment);
95  }
96  mesh.FinalizeTriMesh();
97 
98  while (mesh.GetNE() < (0.5 * approx_number_of_elements)) {
99  mesh.UniformRefinement();
100  }
101 
102  squish(mesh);
103 
104  return mesh;
105 }
106 
107 mfem::Mesh buildBallMesh(int approx_number_of_elements)
108 {
109  static constexpr int dim = 3;
110  static constexpr int num_elems = 8;
111  static constexpr int num_vertices = 7;
112  static constexpr int num_boundary_elements = 8;
113 
114  static constexpr double vertices[num_vertices][dim] = {{0, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, 0, -1},
115  {0, 0, 1}, {0, -1, 0}, {1, 0, 0}};
116  static constexpr int triangles[num_elems][3] = {{4, 5, 6}, {4, 6, 2}, {4, 2, 1}, {4, 1, 5},
117  {5, 1, 3}, {5, 3, 6}, {3, 1, 2}, {6, 3, 2}};
118  static constexpr int tetrahedra[num_elems][4] = {{0, 4, 5, 6}, {0, 4, 6, 2}, {0, 4, 2, 1}, {0, 4, 1, 5},
119  {0, 5, 1, 3}, {0, 5, 3, 6}, {0, 3, 1, 2}, {0, 6, 3, 2}};
120 
121  auto mesh = mfem::Mesh(dim, num_vertices, num_elems, num_boundary_elements);
122 
123  for (auto vertex : vertices) {
124  mesh.AddVertex(vertex);
125  }
126  for (auto tetrahedron : tetrahedra) {
127  mesh.AddTet(tetrahedron);
128  }
129  for (auto triangle : triangles) {
130  mesh.AddBdrTriangle(triangle);
131  }
132 
133  mesh.FinalizeTetMesh();
134 
135  while (mesh.GetNE() < (0.25 * approx_number_of_elements)) {
136  mesh.UniformRefinement();
137  }
138 
139  squish(mesh);
140 
141  return mesh;
142 }
143 
144 mfem::Mesh buildRectangleMesh(int elements_in_x, int elements_in_y, double size_x, double size_y)
145 {
146  return mfem::Mesh::MakeCartesian2D(elements_in_x, elements_in_y, mfem::Element::QUADRILATERAL, true, size_x, size_y);
147 }
148 
149 mfem::Mesh buildCuboidMesh(int elements_in_x, int elements_in_y, int elements_in_z, double size_x, double size_y,
150  double size_z)
151 {
152  return mfem::Mesh::MakeCartesian3D(elements_in_x, elements_in_y, elements_in_z, mfem::Element::HEXAHEDRON, size_x,
153  size_y, size_z);
154 }
155 
156 mfem::Mesh buildCylinderMesh(int radial_refinement, int elements_lengthwise, double radius, double height)
157 {
158  static constexpr int dim = 2;
159  static constexpr int num_vertices = 17;
160  static constexpr int num_elems = 12;
161  static constexpr int num_boundary_elements = 8;
162 
163  // a == 1.0 produces a mesh of a cylindrical "core" surrounded by
164  // a cylindrical "shell", but makes some of the elements in the "core" section
165  // nearly degenerate
166  //
167  // a > 1 makes the "core" section no longer a cylinder, but its elements
168  // are no longer nearly degenerate
169  constexpr double a = 1.3;
170  static constexpr double vertices[num_vertices][dim] = {{0.0000000000000000, 0.0000000000000000},
171  {0.5773502691896258, 0.0000000000000000},
172  {0.4082482904638630 * a, 0.4082482904638630 * a},
173  {0.0000000000000000, 0.5773502691896258},
174  {-0.4082482904638630 * a, 0.4082482904638630 * a},
175  {-0.5773502691896258, 0.0000000000000000},
176  {-0.4082482904638630 * a, -0.4082482904638630 * a},
177  {0.0000000000000000, -0.5773502691896258},
178  {0.4082482904638630 * a, -0.4082482904638630 * a},
179  {1.000000000000000, 0.0000000000000000},
180  {0.7071067811865475, 0.7071067811865475},
181  {0.0000000000000000, 1.000000000000000},
182  {-0.707106781186548, 0.7071067811865475},
183  {-1.000000000000000, 0.0000000000000000},
184  {-0.707106781186548, -0.707106781186548},
185  {0.0000000000000000, -1.000000000000000},
186  {0.7071067811865475, -0.707106781186548}};
187 
188  static constexpr int elems[num_elems][4] = {{0, 1, 2, 3}, {0, 3, 4, 5}, {0, 5, 6, 7}, {0, 7, 8, 1},
189  {1, 9, 10, 2}, {2, 10, 11, 3}, {3, 11, 12, 4}, {4, 12, 13, 5},
190  {5, 13, 14, 6}, {6, 14, 15, 7}, {7, 15, 16, 8}, {8, 16, 9, 1}};
191 
192  static constexpr int boundary_elems[num_boundary_elements][2] = {{9, 10}, {10, 11}, {11, 12}, {12, 13},
193  {13, 14}, {14, 15}, {15, 16}, {16, 9}};
194 
195  auto mesh = mfem::Mesh(dim, num_vertices, num_elems, num_boundary_elements);
196 
197  for (auto vertex : vertices) {
198  mesh.AddVertex(vertex);
199  }
200  for (auto elem : elems) {
201  mesh.AddQuad(elem);
202  }
203  for (auto boundary_elem : boundary_elems) {
204  mesh.AddBdrSegment(boundary_elem);
205  }
206 
207  for (int i = 0; i < radial_refinement; i++) {
208  mesh.UniformRefinement();
209  }
210 
211  // the coarse mesh is actually a filled octagon
212  // this deforms the vertices slightly to make it
213  // into filled disk instead
214  {
215  int n = mesh.GetNV();
216 
217  mfem::Vector new_vertices;
218  mesh.GetVertices(new_vertices);
219  mfem::Vector vertex(dim);
220  for (int i = 0; i < n; i++) {
221  for (int d = 0; d < dim; d++) {
222  vertex(d) = new_vertices[d * n + i];
223  }
224 
225  // stretch the octagonal shape into a circle of the appropriate radius
226  double theta = atan2(vertex(1), vertex(0));
227  double phi = fmod(theta + M_PI, M_PI_4);
228  vertex *= radius * (cos(phi) + (-1.0 + sqrt(2.0)) * sin(phi));
229 
230  for (int d = 0; d < dim; d++) {
231  new_vertices[d * n + i] = vertex(d);
232  }
233  }
234  mesh.SetVertices(new_vertices);
235  }
236 
237  return mfem::Mesh(*mfem::Extrude2D(&mesh, elements_lengthwise, height));
238 }
239 
241 mfem::Mesh buildRing(int radial_refinement, double inner_radius, double outer_radius, double total_angle, int sectors)
242 {
243  using index_type = int;
244  using size_type = std::vector<index_type>::size_type;
245 
246  static constexpr int dim = 2;
247 
248  SLIC_ASSERT_MSG(total_angle > 0., "only positive angles supported");
249 
250  // ensure total_angle is (0, 2 * pi]
251  total_angle = std::min(total_angle, 2. * M_PI);
252  const double angle = total_angle / sectors;
253 
254  auto num_elems = static_cast<size_type>(sectors);
255  auto num_vertices_ring = static_cast<size_type>((total_angle == 2. * M_PI) ? sectors : sectors + 1);
256  auto num_vertices = num_vertices_ring * 2;
257  auto num_boundary_elements = num_elems * 2;
258 
259  SLIC_ERROR_ROOT_IF(outer_radius <= inner_radius,
260  "Outer radius is smaller than inner radius while building a cylinder mesh.");
261 
262  std::vector<std::vector<double>> vertices(static_cast<size_type>(num_vertices), std::vector<double>(dim, 0.));
263  for (size_type i = 0; i < num_vertices_ring; i++) {
264  double s = sin(angle * static_cast<double>(i));
265  double c = cos(angle * static_cast<double>(i));
266  vertices[i][0] = inner_radius * c;
267  vertices[i][1] = inner_radius * s;
268 
269  vertices[i + num_vertices_ring][0] = outer_radius * c;
270  vertices[i + num_vertices_ring][1] = outer_radius * s;
271  }
272 
273  std::vector<std::vector<index_type>> elems(static_cast<size_type>(num_elems), std::vector<index_type>(4, 0));
274  std::vector<std::vector<index_type>> boundary_elems(static_cast<size_type>(num_boundary_elements),
275  std::vector<index_type>(2, 0));
276  for (size_type i = 0; i < num_elems; i++) {
277  elems[i][0] = static_cast<index_type>(i);
278  elems[i][1] = static_cast<index_type>(num_vertices_ring + i);
279  elems[i][2] = static_cast<index_type>(num_vertices_ring + (i + 1) % (num_vertices_ring));
280  elems[i][3] = static_cast<index_type>((i + 1) % num_vertices_ring);
281 
282  // inner boundary
283  boundary_elems[i][0] = elems[i][3];
284  boundary_elems[i][1] = elems[i][0];
285 
286  // outer boundary
287  boundary_elems[i + num_elems][0] = elems[i][1];
288  boundary_elems[i + num_elems][1] = elems[i][2];
289  }
290 
291  mfem::Mesh mesh(dim, static_cast<int>(num_vertices), static_cast<int>(num_elems),
292  static_cast<int>(num_boundary_elements));
293 
294  for (auto vertex : vertices) {
295  mesh.AddVertex(vertex.data());
296  }
297  for (auto elem : elems) {
298  mesh.AddQuad(elem[0], elem[1], elem[2], elem[3]);
299  }
300  for (auto boundary_elem : boundary_elems) {
301  mesh.AddBdrSegment(boundary_elem[0], boundary_elem[1]);
302  }
303 
304  for (int i = 0; i < radial_refinement; i++) {
305  mesh.UniformRefinement();
306  }
307 
308  // the coarse mesh is actually a filled octagon
309  // this deforms the vertices slightly to make it
310  // into filled disk instead
311  {
312  int n = mesh.GetNV();
313 
314  mfem::Vector new_vertices;
315  mesh.GetVertices(new_vertices);
316  mfem::Vector vertex(dim);
317  for (int i = 0; i < n; i++) {
318  for (int d = 0; d < dim; d++) {
319  vertex(d) = new_vertices[d * n + i];
320  }
321 
322  // stretch the polygonal shape into a cylinder
323  // phi is the angle to the closest multiple of a sector angle
324  double theta = atan2(vertex(1), vertex(0));
325  double phi = fmod(theta + 2. * M_PI, angle);
326 
327  // this calculation assumes the 0 <= phi <= angle
328  // the distance from the center of the cylinder to the midpoint of the radial edge is known
329  // the midpoint can also be used to form a right triangle to phi where
330  // the angle is given by abs(0.5 * angle - phi)
331  double factor = cos(fabs(0.5 * angle - phi)) / cos(0.5 * angle);
332  vertex *= factor;
333 
334  for (int d = 0; d < dim; d++) {
335  new_vertices[d * n + i] = vertex(d);
336  }
337  }
338  mesh.SetVertices(new_vertices);
339  }
340 
341  return mesh;
342 }
343 
344 mfem::Mesh buildRingMesh(int radial_refinement, double inner_radius, double outer_radius, double total_angle,
345  int sectors)
346 {
347  return buildRing(radial_refinement, inner_radius, outer_radius, total_angle, sectors);
348 }
349 
350 mfem::Mesh buildHollowCylinderMesh(int radial_refinement, int elements_lengthwise, double inner_radius,
351  double outer_radius, double height, double total_angle, int sectors)
352 {
353  auto mesh = buildRing(radial_refinement, inner_radius, outer_radius, total_angle, sectors);
354  return mfem::Mesh(*mfem::Extrude2D(&mesh, elements_lengthwise, height));
355 }
356 
358 mfem::Mesh build_hollow_quarter_cylinder(std::size_t radial_divisions, std::size_t angular_divisions,
359  std::size_t vertical_divisions, double inner_radius, double outer_radius,
360  double height)
361 {
362  constexpr int dim = 3;
363 
364  // start with a structured mesh of a cube
365  mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(static_cast<int>(radial_divisions), static_cast<int>(angular_divisions),
366  static_cast<int>(vertical_divisions), mfem::Element::HEXAHEDRON);
367 
368  int num_vertices = mesh.GetNV();
369  mfem::Vector new_vertices;
370  mesh.GetVertices(new_vertices);
371  mfem::Vector vertex(dim);
372  for (int i = 0; i < num_vertices; i++) {
373  for (int d = 0; d < dim; d++) {
374  vertex(d) = new_vertices[d * num_vertices + i];
375  }
376 
377  // transform the vertices to make it into a cylindrical shell
378  double r = inner_radius + (outer_radius - inner_radius) * vertex[0];
379  double theta = vertex[1] * M_PI_2;
380  vertex(0) = r * cos(theta);
381  vertex(1) = r * sin(theta);
382  vertex(2) = vertex(2) * height;
383 
384  for (int d = 0; d < dim; d++) {
385  new_vertices[d * num_vertices + i] = vertex(d);
386  }
387  }
388  mesh.SetVertices(new_vertices);
389 
390  return mesh;
391 }
392 namespace mesh {
393 
394 void InputOptions::defineInputFileSchema(axom::inlet::Container& container)
395 {
396  // Refinement levels
397  container.addInt("ser_ref_levels", "Number of times to refine the mesh uniformly in serial.").defaultValue(0);
398  container.addInt("par_ref_levels", "Number of times to refine the mesh uniformly in parallel.").defaultValue(0);
399 
400  // Types of meshes we support
401  container.addString("type", "Type of mesh").required().validValues({"ball", "box", "disk", "file"});
402 
403  // `file` type mesh options
404  container.addString("mesh", "Path to Mesh file");
405 
406  // `box` type mesh generation options
407  auto& elements = container.addStruct("elements");
408  // TODO: Can these be specified as required if elements is defined?
409  elements.addInt("x", "x-dimension");
410  elements.addInt("y", "y-dimension");
411  elements.addInt("z", "z-dimension");
412 
413  auto& size = container.addStruct("size");
414  // TODO: Can these be specified as required if elements is defined?
415  size.addDouble("x", "Size in the x-dimension");
416  size.addDouble("y", "Size in the y-dimension");
417  size.addDouble("z", "Size in the z-dimension");
418 
419  // `ball` and `disk` mesh generation options
420  container.addInt("approx_elements", "Approximate number of elements in an n-ball mesh");
421 }
422 
423 std::unique_ptr<mfem::ParMesh> buildParallelMesh(const InputOptions& options, const MPI_Comm comm)
424 {
425  std::optional<mfem::Mesh> serial_mesh;
426 
427  if (const auto file_opts = std::get_if<FileInputOptions>(&options.extra_options)) {
428  SLIC_ERROR_ROOT_IF(file_opts->absolute_mesh_file_name.empty(),
429  "Absolute path to mesh file was not configured, did you forget to call findMeshFilePath?");
430  serial_mesh.emplace(buildMeshFromFile(file_opts->absolute_mesh_file_name));
431  } else if (const auto box_opts = std::get_if<BoxInputOptions>(&options.extra_options)) {
432  const auto& elems = box_opts->elements;
433  const auto& sizes = box_opts->overall_size;
434  if (elems.size() == 2) {
435  serial_mesh.emplace(buildRectangleMesh(elems.at(0), elems.at(1), sizes.at(0), sizes.at(1)));
436  } else {
437  serial_mesh.emplace(
438  buildCuboidMesh(elems.at(0), elems.at(1), elems.at(2), sizes.at(0), sizes.at(1), sizes.at(2)));
439  }
440  } else if (const auto ball_opts = std::get_if<NBallInputOptions>(&options.extra_options)) {
441  if (ball_opts->dimension == 2) {
442  serial_mesh.emplace(buildDiskMesh(ball_opts->approx_elements));
443  } else {
444  serial_mesh.emplace(buildBallMesh(ball_opts->approx_elements));
445  }
446  }
447 
448  SLIC_ERROR_ROOT_IF(!serial_mesh, "Mesh input options were invalid");
449  return refineAndDistribute(std::move(*serial_mesh), options.ser_ref_levels, options.par_ref_levels, comm);
450 }
451 
452 std::unique_ptr<mfem::ParMesh> refineAndDistribute(mfem::Mesh&& serial_mesh, const int refine_serial,
453  const int refine_parallel, const MPI_Comm comm)
454 {
455  // Serial refinement first
456  for (int lev = 0; lev < refine_serial; lev++) {
457  serial_mesh.UniformRefinement();
458  }
459 
460  // Then create the parallel mesh and apply parallel refinement
461  auto parallel_mesh = std::make_unique<mfem::ParMesh>(comm, serial_mesh);
462  for (int lev = 0; lev < refine_parallel; lev++) {
463  parallel_mesh->UniformRefinement();
464  }
465 
466  parallel_mesh->EnsureNodes();
467  parallel_mesh->ExchangeFaceNbrData();
468 
469  return parallel_mesh;
470 }
471 
472 } // namespace mesh
473 } // namespace serac
474 
476 {
477  int ser_ref = base["ser_ref_levels"];
478  int par_ref = base["par_ref_levels"];
479 
480  // This is for cuboid/rectangular meshes
481  std::string mesh_type = base["type"];
482  if (mesh_type == "box") {
483  auto elements_input = base["elements"];
484  bool z_present = elements_input.contains("z");
485 
486  std::vector<int> elements(z_present ? 3 : 2);
487  elements[0] = elements_input["x"];
488  elements[1] = elements_input["y"];
489  if (z_present) elements[2] = elements_input["z"];
490 
491  std::vector<double> overall_size;
492  if (base.contains("size")) {
493  auto size_input = base["size"];
494  overall_size.push_back(size_input["x"]);
495  overall_size.push_back(size_input["y"]);
496 
497  if (size_input.contains("z")) {
498  overall_size.push_back(size_input["z"]);
499  }
500  } else {
501  overall_size = std::vector<double>(elements.size(), 1.);
502  }
503 
504  return {serac::mesh::BoxInputOptions{elements, overall_size}, ser_ref, par_ref};
505  } else if (mesh_type == "disk" || mesh_type == "ball") {
506  int approx_elements = base["approx_elements"];
507  int dim = 3;
508  if (mesh_type == "disk") {
509  dim = 2;
510  }
511  return {serac::mesh::NBallInputOptions{approx_elements, dim}, ser_ref, par_ref};
512  } else if (mesh_type == "file") { // This is for file-based meshes
513  std::string mesh_path = base["mesh"];
514  return {serac::mesh::FileInputOptions{mesh_path}, ser_ref, par_ref};
515  }
516 
517  // If it reaches here, we haven't found a supported type
518  serac::logger::flush();
519  std::string err_msg = axom::fmt::format("Specified type not supported: '{0}'", mesh_type);
520  SLIC_ERROR_ROOT(err_msg);
521  return {};
522 }
This file contains the all the necessary functions and macros required for logging as well as a helpe...
This file contains helper functions for importing and managing various mesh objects.
std::unique_ptr< mfem::ParMesh > buildParallelMesh(const InputOptions &options, const MPI_Comm comm)
Constructs an MFEM parallel mesh from a set of input options.
Definition: mesh_utils.cpp:423
std::unique_ptr< mfem::ParMesh > refineAndDistribute(mfem::Mesh &&serial_mesh, const int refine_serial, const int refine_parallel, const MPI_Comm comm)
Finalizes a serial mesh into a refined parallel mesh.
Definition: mesh_utils.cpp:452
Accelerator functionality.
Definition: serac.cpp:38
SERAC_HOST_DEVICE auto atan2(dual< gradient_type > y, dual< gradient_type > x)
implementation of atan2 for dual numbers
Definition: dual.hpp:311
mfem::Mesh buildHollowCylinderMesh(int radial_refinement, int elements_lengthwise, double inner_radius, double outer_radius, double height, double total_angle, int sectors)
Constructs a 3D MFEM mesh of a hollow cylinder.
Definition: mesh_utils.cpp:350
SERAC_HOST_DEVICE auto sin(dual< gradient_type > a)
implementation of sine for dual numbers
Definition: dual.hpp:295
mfem::Mesh buildBallMesh(int approx_number_of_elements)
Constructs a 3D MFEM mesh of a unit ball, centered at the origin.
Definition: mesh_utils.cpp:107
mfem::Mesh buildRing(int radial_refinement, double inner_radius, double outer_radius, double total_angle, int sectors)
Constructs a 2D MFEM mesh of a ring.
Definition: mesh_utils.cpp:241
SERAC_HOST_DEVICE auto cos(dual< gradient_type > a)
implementation of cosine for dual numbers
Definition: dual.hpp:287
SERAC_HOST_DEVICE auto min(dual< gradient_type > a, double b)
Implementation of min for dual numbers.
Definition: dual.hpp:256
SERAC_HOST_DEVICE auto sqrt(dual< gradient_type > x)
implementation of square root for dual numbers
Definition: dual.hpp:279
constexpr SERAC_HOST_DEVICE int size(const tensor< T, n... > &)
returns the total number of stored values in a tensor
Definition: tensor.hpp:1851
mfem::Mesh buildMeshFromFile(const std::string &mesh_file)
Constructs an MFEM mesh from a file.
Definition: mesh_utils.cpp:19
mfem::Mesh buildCylinderMesh(int radial_refinement, int elements_lengthwise, double radius, double height)
Constructs a 3D MFEM mesh of a cylinder.
Definition: mesh_utils.cpp:156
mfem::Mesh buildRectangleMesh(int elements_in_x, int elements_in_y, double size_x, double size_y)
Constructs a 2D MFEM mesh of a rectangle.
Definition: mesh_utils.cpp:144
void squish(mfem::Mesh &mesh)
a transformation from the unit disk/sphere (in L1 norm) to a unit disk/sphere (in L2 norm)
Definition: mesh_utils.cpp:50
mfem::Mesh buildRingMesh(int radial_refinement, double inner_radius, double outer_radius, double total_angle, int sectors)
Constructs a 2D MFEM mesh of a ring.
Definition: mesh_utils.cpp:344
mfem::Mesh buildCuboidMesh(int elements_in_x, int elements_in_y, int elements_in_z, double size_x, double size_y, double size_z)
Constructs a 3D MFEM mesh of a cuboid.
Definition: mesh_utils.cpp:149
mfem::Mesh buildDiskMesh(int approx_number_of_elements)
Constructs a 2D MFEM mesh of a unit disk, centered at the origin.
Definition: mesh_utils.cpp:74
mfem::Mesh build_hollow_quarter_cylinder(std::size_t radial_divisions, std::size_t angular_divisions, std::size_t vertical_divisions, double inner_radius, double outer_radius, double height)
Constructs an MFEM mesh of a hollow cylinder restricted to the first orthant.
Definition: mesh_utils.cpp:358
serac::mesh::InputOptions operator()(const axom::inlet::Container &base)
Returns created object from Inlet container.
Definition: mesh_utils.cpp:475
Input options for generated meshes.
Input options for meshes read from files.
Container for the mesh input options.
Definition: mesh_utils.hpp:27
int ser_ref_levels
The number of serial refinement levels.
Definition: mesh_utils.hpp:45
static void defineInputFileSchema(axom::inlet::Container &container)
Input file parameters for mesh generation.
Definition: mesh_utils.cpp:394
std::variant< FileInputOptions, BoxInputOptions, NBallInputOptions > extra_options
The mesh input options (either file or generated)
Definition: mesh_utils.hpp:39
int par_ref_levels
The number of parallel refinement levels.
Definition: mesh_utils.hpp:51
Input options for generated meshes.
Helper functions for exiting Serac cleanly.