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