MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
mesh-bounding-boxes.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11//
12// ---------------------------------------------------------------------
13// Bounding Boxes Miniapp: Construct Bounding Boxes of Quad/Hex Meshes
14// ---------------------------------------------------------------------
15//
16// This miniapp computes bounding boxes for each element in a given mesh, and
17// also computes the bounds on the determinant of the Jacobian of the
18// transformation for each element. The bounding approach is based on the
19// method described in:
20//
21// (1) Section 3 of Mittal et al., "General Field Evaluation in High-Order
22// Meshes on GPUs"
23// and
24// (2) Dzanic et al., "A method for bounding high-order finite element
25// functions: Applications to mesh validity and bounds-preserving limiters".
26//
27//
28// Compile with: make mesh-bounding-boxes
29//
30// Sample runs:
31// mpirun -np 4 mesh-bounding-boxes -m ../../data/klein-bottle.mesh
32// mpirun -np 4 mesh-bounding-boxes -m ../gslib/triple-pt-1.mesh
33// mpirun -np 4 mesh-bounding-boxes -m ../../data/star-surf.mesh
34// mpirun -np 4 mesh-bounding-boxes -m ../../data/fichera-q2.mesh
35
36#include "mfem.hpp"
37#include <iostream>
38#include <fstream>
39
40using namespace mfem;
41using namespace std;
42
43Mesh MakeBoundingBoxMesh(Mesh &mesh, GridFunction &nodal_bb_gf);
44void VisualizeBB(Mesh &mesh, char *title, int pos_x, int pos_y);
45void VisualizeField(ParMesh &pmesh, ParGridFunction &input,
46 char *title, int pos_x, int pos_y);
47
48int main (int argc, char *argv[])
49{
50 // 0. Initialize MPI and HYPRE.
51 Mpi::Init(argc, argv);
53
54 // Set the method's default parameters.
55 const char *mesh_file = "../../data/klein-bottle.mesh";
56 int mesh_poly_deg = 2;
57 bool visualization = true;
58 bool visit = false;
59 bool jacobian = true;
60
61 // Parse command-line options.
62 OptionsParser args(argc, argv);
63 args.AddOption(&mesh_file, "-m", "--mesh",
64 "Mesh file to use.");
65 args.AddOption(&mesh_poly_deg, "-o", "--order",
66 "Polynomial degree of mesh finite element space.");
67 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
68 "--no-visualization",
69 "Enable or disable GLVis visualization.");
70 args.AddOption(&visit, "-visit", "--visit", "-no-visit",
71 "--no-visit",
72 "Enable or disable VisIt output.");
73 args.AddOption(&jacobian, "-jac", "--jacobian", "-no-jac",
74 "--no-jacobian",
75 "Compute bounds on determinant of mesh Jacobian");
76 args.ParseCheck();
77
78 // Initialize and refine the starting mesh.
79 Mesh mesh(mesh_file, 1, 1, false);
80 const int rdim = mesh.Dimension();
81 const int sdim = mesh.SpaceDimension();
82
83 ParMesh pmesh(MPI_COMM_WORLD, mesh);
84 if (pmesh.GetNodes() == NULL) { pmesh.SetCurvature(mesh_poly_deg); }
85 else { mesh_poly_deg = pmesh.GetNodes()->FESpace()->GetMaxElementOrder(); }
86 mesh.Clear();
87
88 // Setup finite element space and gridfunction to store bounding box
89 // x/y/z min & max for each element.
90 L2_FECollection fec_pc(0, rdim);
91 ParFiniteElementSpace fes_l2_bb(&pmesh, &fec_pc, sdim*2, Ordering::byVDIM);
92 ParGridFunction nodal_bb(&fes_l2_bb);
93 Array<int> vdofs;
94
95 GridFunction *nodes = pmesh.GetNodes();
96 int nelem = pmesh.GetNE();
97
98 // Compute bounds on nodal positions and save in nodal_bb gridfunction.
99 Vector lower, upper;
100 nodes->GetElementBounds(lower, upper, 2, -1);
101 for (int e = 0; e < nelem; e++)
102 {
103 fes_l2_bb.GetElementVDofs(e, vdofs);
104 Vector lower_upper(vdofs.Size());
105 for (int d = 0; d < sdim; d++)
106 {
107 lower_upper(d) = lower(e + d*nelem);
108 lower_upper(d+sdim) = upper(e + d*nelem);
109 }
110 nodal_bb.SetSubVector(vdofs, lower_upper);
111 }
112
113 // Make a mesh of bounding boxes to output.
114 Mesh pmesh_ser = pmesh.GetSerialMesh(0);
115 GridFunction nodal_bb_ser = nodal_bb.GetSerialGridFunction(0, pmesh_ser);
116 Mesh meshbb = MakeBoundingBoxMesh(pmesh_ser, nodal_bb_ser);
117
118 // Output in GLVis and VisIt
119 if (visualization && Mpi::Root())
120 {
121 char title1[] = "Input mesh";
122 VisualizeBB(pmesh_ser, title1, 0, 0);
123 char title2[] = "Bounding box mesh";
124 VisualizeBB(meshbb, title2, 400, 0);
125 }
126 if (visit && Mpi::Root())
127 {
128 VisItDataCollection visit_dc("bounding-box-input", &pmesh_ser);
130 visit_dc.Save();
131
132 VisItDataCollection visit_dc_bb("bounding-box", &meshbb);
134 visit_dc_bb.Save();
135 }
136
137 // Print min and max bound of nodal gridfunction
138 int ref_factor = 4;
139 nodes->GetBounds(lower, upper, ref_factor);
140 if (Mpi::Root())
141 {
142 out << "Nodal position minimum bounds:" << endl;
143 lower.Print();
144 out << "Nodal position maximum bounds:" << endl;
145 upper.Print();
146 }
147
148 if (!jacobian) { return 0; }
149
150 // Setup gridfunction for the determinant of the Jacobian.
151 auto detgf = pmesh.GetJacobianDeterminantGF();
152
153 // Setup piecewise constant gridfunction to save bounds on the determinant
154 // of the Jacobian
155 L2_FECollection fec_det_pc(0, rdim);
156 ParFiniteElementSpace fes_det_pc(&pmesh, &fec_det_pc);
157 ParGridFunction bounds_detgf_lower(&fes_det_pc);
158 ParGridFunction bounds_detgf_upper(&fes_det_pc);
159
160 // Compute bounds
161 detgf->GetElementBounds(bounds_detgf_lower, bounds_detgf_upper, ref_factor);
162
163 // GLVis Visualization
164 if (visualization)
165 {
166 char title1[] = "Determinant of Jacobian (det J)";
167 VisualizeField(pmesh, *detgf, title1, 0, 465);
168 char title2[] = "Element-wise lower bound on det J";
169 VisualizeField(pmesh, bounds_detgf_lower, title2, 400, 465);
170 char title3[] = "Element-wise upper bound on det J";
171 VisualizeField(pmesh, bounds_detgf_upper, title3, 800, 465);
172 }
173 // Visit Visualization
174 if (visit)
175 {
176 VisItDataCollection visit_dc("jacobian-determinant-bounds", &pmesh);
178 visit_dc.RegisterField("determinant", detgf.get());
179 visit_dc.RegisterField("det-lower-bound", &bounds_detgf_lower);
180 visit_dc.RegisterField("det-upper-bound", &bounds_detgf_upper);
181 visit_dc.Save();
182 }
183
184 // Print min and max bound of determinant gridfunction
185 detgf->GetBounds(lower, upper, ref_factor);
186 if (Mpi::Root())
187 {
188 out << "Jacobian determinant minimum bound: " << lower(0) << endl;
189 out << "Jacobian determinant maximum bound: " << upper(0) << endl;
190 }
191
192 return 0;
193}
194
196{
197 int nelem = mesh.GetNE();
198 int sdim = mesh.SpaceDimension();
199 int nverts = pow(2,sdim)*nelem;
200 Mesh meshbb(sdim, nverts, nelem, 0, sdim);
201 int eidx = 0;
202 int vidx = 0;
203 for (int e = 0; e < nelem; e++)
204 {
205 Vector xyzminmax_el;
206 nodal_bb_gf.GetElementDofValues(e, xyzminmax_el);
207 if (sdim == 2)
208 {
209 Vector xyz(2);
210 xyz(0) = xyzminmax_el(0);
211 xyz(1) = xyzminmax_el(1);
212 meshbb.AddVertex(xyz);
213
214 xyz(0) = xyzminmax_el(2);
215 xyz(1) = xyzminmax_el(1);
216 meshbb.AddVertex(xyz);
217
218 xyz(0) = xyzminmax_el(2);
219 xyz(1) = xyzminmax_el(3);
220 meshbb.AddVertex(xyz);
221
222 xyz(0) = xyzminmax_el(0);
223 xyz(1) = xyzminmax_el(3);
224 meshbb.AddVertex(xyz);
225
226 const int inds[4] = {vidx++, vidx++, vidx++, vidx++};
227 int attr = eidx+1;
228 meshbb.AddQuad(inds, attr);
229 eidx++;
230 }
231 else if (sdim == 3)
232 {
233 Vector xyz(3);
234 xyz(0) = xyzminmax_el(0);
235 xyz(1) = xyzminmax_el(1);
236 xyz(2) = xyzminmax_el(2);
237 meshbb.AddVertex(xyz);
238
239 xyz(0) = xyzminmax_el(3);
240 xyz(1) = xyzminmax_el(1);
241 xyz(2) = xyzminmax_el(2);
242 meshbb.AddVertex(xyz);
243
244 xyz(0) = xyzminmax_el(3);
245 xyz(1) = xyzminmax_el(4);
246 xyz(2) = xyzminmax_el(2);
247 meshbb.AddVertex(xyz);
248
249 xyz(0) = xyzminmax_el(0);
250 xyz(1) = xyzminmax_el(4);
251 xyz(2) = xyzminmax_el(2);
252 meshbb.AddVertex(xyz);
253
254 xyz(0) = xyzminmax_el(0);
255 xyz(1) = xyzminmax_el(1);
256 xyz(2) = xyzminmax_el(5);
257 meshbb.AddVertex(xyz);
258
259 xyz(0) = xyzminmax_el(3);
260 xyz(1) = xyzminmax_el(1);
261 xyz(2) = xyzminmax_el(5);
262 meshbb.AddVertex(xyz);
263
264 xyz(0) = xyzminmax_el(3);
265 xyz(1) = xyzminmax_el(4);
266 xyz(2) = xyzminmax_el(5);
267 meshbb.AddVertex(xyz);
268
269 xyz(0) = xyzminmax_el(0);
270 xyz(1) = xyzminmax_el(4);
271 xyz(2) = xyzminmax_el(5);
272 meshbb.AddVertex(xyz);
273
274 const int inds[8] = {vidx++, vidx++, vidx++, vidx++,
275 vidx++, vidx++, vidx++, vidx++
276 };
277 meshbb.AddHex(inds, (eidx++)+1);
278 }
279 }
280 if (sdim == 2)
281 {
282 meshbb.FinalizeQuadMesh(1, 1, true);
283 }
284 else
285 {
286 meshbb.FinalizeHexMesh(1, 1, true);
287 }
288 return meshbb;
289}
290
291void VisualizeBB(Mesh &mesh, char *title, int pos_x, int pos_y)
292{
293 socketstream sock;
294 sock.open("localhost", 19916);
295 sock << "mesh\n";
296 mesh.Print(sock);
297 std::string keystrokes = mesh.SpaceDimension() == 2 ? "keys em" : "keys )";
298 sock << "window_title '"<< title << "'\n"
299 << "window_geometry "
300 << pos_x << " " << pos_y << " " << 400 << " " << 400 << "\n"
301 // << "keys jRmclA//]]]]]]]]" << endl;
302 << keystrokes << endl;
303}
304
306 char *title, int pos_x, int pos_y)
307{
308 socketstream sock;
309 if (pmesh.GetMyRank() == 0)
310 {
311 sock.open("localhost", 19916);
312 sock << "solution\n";
313 }
314 pmesh.PrintAsOne(sock);
315 input.SaveAsOne(sock);
316 if (pmesh.GetMyRank() == 0)
317 {
318 sock << "window_title '"<< title << "'\n"
319 << "window_geometry "
320 << pos_x << " " << pos_y << " " << 400 << " " << 400 << "\n"
321 << "keys jRmclApppppppppppp//]]]]]]]]" << endl;
322 }
323}
int Size() const
Return the logical size of the array.
Definition array.hpp:192
virtual void SetFormat(int fmt)
Set the desired output mesh and data format.
DofTransformation * GetElementVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom for the i'th element. The returned indices are offsets into an ...
Definition fespace.cpp:299
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:53
virtual void GetElementDofValues(int el, Vector &dof_vals) const
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.cpp:33
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:369
Mesh data type.
Definition mesh.hpp:67
int AddQuad(int v1, int v2, int v3, int v4, int attr=1)
Adds a quadrilateral to the mesh given by 4 vertices v1 through v4.
Definition mesh.cpp:2164
virtual void Print(std::ostream &os=mfem::out, const std::string &comments="") const
Print the mesh to the given stream using the default MFEM mesh format.
Definition mesh.hpp:2610
void Clear()
Clear the contents of the Mesh.
Definition mesh.hpp:835
int AddVertex(real_t x, real_t y=0.0, real_t z=0.0)
Definition mesh.cpp:2079
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1390
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1314
void FinalizeHexMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a hexahedral Mesh.
Definition mesh.cpp:3624
void FinalizeQuadMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a quadrilateral Mesh.
Definition mesh.cpp:2610
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1317
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:10112
int AddHex(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int attr=1)
Adds a hexahedron to the mesh given by 8 vertices v1 through v8.
Definition mesh.cpp:2227
static bool Root()
Return true if the rank in MPI_COMM_WORLD is zero.
static void Init(int &argc, char **&argv, int required=default_thread_required, int *provided=nullptr)
Singleton creation with Mpi::Init(argc, argv).
void ParseCheck(std::ostream &out=mfem::out)
void AddOption(bool *var, const char *enable_short_name, const char *enable_long_name, const char *disable_short_name, const char *disable_long_name, const char *description, bool required=false)
Add a boolean option and set 'var' to receive the value. Enable/disable tags are used to set the bool...
Definition optparser.hpp:82
Abstract parallel finite element space.
Definition pfespace.hpp:31
Class for parallel grid function.
Definition pgridfunc.hpp:50
void SaveAsOne(const char *fname, int precision=16) const
GridFunction GetSerialGridFunction(int save_rank, Mesh &serial_mesh) const
Returns a GridFunction on MPI rank save_rank that does not have any duplication of vertices/nodes at ...
Class for parallel meshes.
Definition pmesh.hpp:35
Mesh GetSerialMesh(int save_rank) const
Definition pmesh.cpp:5551
int GetMyRank() const
Definition pmesh.hpp:405
void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1, int pyrtype=1) override
Set the curvature of the mesh nodes using the given polynomial degree.
Definition pmesh.cpp:2034
std::unique_ptr< ParGridFunction > GetJacobianDeterminantGF() const
Create a ParGridFunction representing the Jacobian determinant. Parallel counterpart of Mesh::GetJaco...
Definition pmesh.cpp:2017
void PrintAsOne(std::ostream &out=mfem::out, const std::string &comments="") const
Write the mesh to the stream 'out' on Process 0 in a form suitable for visualization.
Definition pmesh.cpp:5131
Vector data type.
Definition vector.hpp:82
void Print(std::ostream &out=mfem::out, int width=8) const
Prints vector to stream out.
Definition vector.cpp:870
void SetSubVector(const Array< int > &dofs, const real_t value)
Set the entries listed in dofs to the given value.
Definition vector.cpp:702
Data collection with VisIt I/O routines.
void Save() override
Save the collection and a VisIt root file.
void RegisterField(const std::string &field_name, GridFunction *gf) override
Add a grid function to the collection and update the root file.
int open(const char hostname[], int port)
Open the socket stream on 'port' at 'hostname'.
int main()
void VisualizeBB(Mesh &mesh, char *title, int pos_x, int pos_y)
Mesh MakeBoundingBoxMesh(Mesh &mesh, GridFunction &nodal_bb_gf)
void VisualizeField(socketstream &sock, const char *vishost, int visport, GridFunction &gf, const char *title, int x, int y, int w, int h, const char *keys, bool vec)
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
STL namespace.
std::array< int, NCMesh::MaxFaceNodes > nodes