MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
mesh-bounding-boxes.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, 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);
45void VisualizeBB(Mesh &mesh, char *title, int pos_x, int pos_y);
46void VisualizeField(ParMesh &pmesh, ParGridFunction &input,
47 char *title, int pos_x, int pos_y);
48
49int main (int argc, char *argv[])
50{
51 // 0. Initialize MPI and HYPRE.
52 Mpi::Init(argc, argv);
54
55 // Set the method's default parameters.
56 const char *mesh_file = "../../data/klein-bottle.mesh";
57 int mesh_poly_deg = 2;
58 bool visualization = true;
59 bool visit = false;
60 bool jacobian = true;
61
62 // Parse command-line options.
63 OptionsParser args(argc, argv);
64 args.AddOption(&mesh_file, "-m", "--mesh",
65 "Mesh file to use.");
66 args.AddOption(&mesh_poly_deg, "-o", "--order",
67 "Polynomial degree of mesh finite element space.");
68 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
69 "--no-visualization",
70 "Enable or disable GLVis visualization.");
71 args.AddOption(&visit, "-visit", "--visit", "-no-visit",
72 "--no-visit",
73 "Enable or disable VisIt output.");
74 args.AddOption(&jacobian, "-jac", "--jacobian", "-no-jac",
75 "--no-jacobian",
76 "Compute bounds on determinant of mesh Jacobian");
77 args.ParseCheck();
78
79 // Initialize and refine the starting mesh.
80 Mesh mesh(mesh_file, 1, 1, false);
81 const int rdim = mesh.Dimension();
82 const int sdim = mesh.SpaceDimension();
83
84 ParMesh pmesh(MPI_COMM_WORLD, mesh);
85 if (pmesh.GetNodes() == NULL) { pmesh.SetCurvature(mesh_poly_deg); }
86 else { mesh_poly_deg = pmesh.GetNodes()->FESpace()->GetMaxElementOrder(); }
87 mesh.Clear();
88
89 // Setup finite element space and gridfunction to store bounding box
90 // x/y/z min & max for each element.
91 L2_FECollection fec_pc(0, rdim);
92 ParFiniteElementSpace fes_l2_bb(&pmesh, &fec_pc, sdim*2, Ordering::byVDIM);
93 ParGridFunction nodal_bb(&fes_l2_bb);
94 Array<int> vdofs;
95
96 GridFunction *nodes = pmesh.GetNodes();
97 int nelem = pmesh.GetNE();
98
99 // Compute bounds on nodal positions and save in nodal_bb gridfunction.
100 Vector lower, upper;
101 nodes->GetElementBounds(lower, upper, 2, -1);
102 for (int e = 0; e < nelem; e++)
103 {
104 fes_l2_bb.GetElementVDofs(e, vdofs);
105 Vector lower_upper(vdofs.Size());
106 for (int d = 0; d < sdim; d++)
107 {
108 lower_upper(d) = lower(e + d*nelem);
109 lower_upper(d+sdim) = upper(e + d*nelem);
110 }
111 nodal_bb.SetSubVector(vdofs, lower_upper);
112 }
113
114 // Make a mesh of bounding boxes to output.
115 Mesh pmesh_ser = pmesh.GetSerialMesh(0);
116 GridFunction nodal_bb_ser = nodal_bb.GetSerialGridFunction(0, pmesh_ser);
117 Mesh meshbb = MakeBoundingBoxMesh(pmesh_ser, nodal_bb_ser);
118
119 // Output in GLVis and VisIt
120 if (visualization && Mpi::Root())
121 {
122 char title1[] = "Input mesh";
123 VisualizeBB(pmesh_ser, title1, 0, 0);
124 char title2[] = "Bounding box mesh";
125 VisualizeBB(meshbb, title2, 400, 0);
126 }
127 if (visit && Mpi::Root())
128 {
129 VisItDataCollection visit_dc("bounding-box-input", &pmesh_ser);
131 visit_dc.Save();
132
133 VisItDataCollection visit_dc_bb("bounding-box", &meshbb);
135 visit_dc_bb.Save();
136 }
137
138 // Print min and max bound of nodal gridfunction
139 int ref_factor = 4;
140 nodes->GetBounds(lower, upper, ref_factor);
141 if (Mpi::Root())
142 {
143 out << "Nodal position minimum bounds:" << endl;
144 lower.Print();
145 out << "Nodal position maximum bounds:" << endl;
146 upper.Print();
147 }
148
149 if (!jacobian) { return 0; }
150
151 // Setup gridfunction for the determinant of the Jacobian.
152 // Note: determinant order = rdim*mesh_order - 1 for quads/hexes
153 int det_order = rdim*mesh_poly_deg-1;
154 L2_FECollection fec_det(det_order, rdim, BasisType::GaussLobatto);
155 ParFiniteElementSpace fespace_det(&pmesh, &fec_det);
156 ParGridFunction detgf(&fespace_det);
157 GetDeterminantJacobianGF(&pmesh, &detgf);
158
159 // Setup piecewise constant gridfunction to save bounds on the determinant
160 // of the Jacobian
161 L2_FECollection fec_det_pc(0, rdim);
162 ParFiniteElementSpace fes_det_pc(&pmesh, &fec_det_pc);
163 ParGridFunction bounds_detgf_lower(&fes_det_pc);
164 ParGridFunction bounds_detgf_upper(&fes_det_pc);
165
166 // Compute bounds
167 detgf.GetElementBounds(bounds_detgf_lower, bounds_detgf_upper, ref_factor);
168
169 // GLVis Visualization
170 if (visualization)
171 {
172 char title1[] = "Determinant of Jacobian (det J)";
173 VisualizeField(pmesh, detgf, title1, 0, 465);
174 char title2[] = "Element-wise lower bound on det J";
175 VisualizeField(pmesh, bounds_detgf_lower, title2, 400, 465);
176 char title3[] = "Element-wise upper bound on det J";
177 VisualizeField(pmesh, bounds_detgf_upper, title3, 800, 465);
178 }
179 // Visit Visualization
180 if (visit)
181 {
182 VisItDataCollection visit_dc("jacobian-determinant-bounds", &pmesh);
184 visit_dc.RegisterField("determinant", &detgf);
185 visit_dc.RegisterField("det-lower-bound", &bounds_detgf_lower);
186 visit_dc.RegisterField("det-upper-bound", &bounds_detgf_upper);
187 visit_dc.Save();
188 }
189
190 // Print min and max bound of determinant gridfunction
191 detgf.GetBounds(lower, upper, ref_factor);
192 if (Mpi::Root())
193 {
194 out << "Jacobian determinant minimum bound: " << lower(0) << endl;
195 out << "Jacobian determinant maximum bound: " << upper(0) << endl;
196 }
197
198 return 0;
199}
200
202{
203 int nelem = mesh.GetNE();
204 int sdim = mesh.SpaceDimension();
205 int nverts = pow(2,sdim)*nelem;
206 Mesh meshbb(sdim, nverts, nelem, 0, sdim);
207 int eidx = 0;
208 int vidx = 0;
209 for (int e = 0; e < nelem; e++)
210 {
211 Vector xyzminmax_el;
212 nodal_bb_gf.GetElementDofValues(e, xyzminmax_el);
213 if (sdim == 2)
214 {
215 Vector xyz(2);
216 xyz(0) = xyzminmax_el(0);
217 xyz(1) = xyzminmax_el(1);
218 meshbb.AddVertex(xyz);
219
220 xyz(0) = xyzminmax_el(2);
221 xyz(1) = xyzminmax_el(1);
222 meshbb.AddVertex(xyz);
223
224 xyz(0) = xyzminmax_el(2);
225 xyz(1) = xyzminmax_el(3);
226 meshbb.AddVertex(xyz);
227
228 xyz(0) = xyzminmax_el(0);
229 xyz(1) = xyzminmax_el(3);
230 meshbb.AddVertex(xyz);
231
232 const int inds[4] = {vidx++, vidx++, vidx++, vidx++};
233 int attr = eidx+1;
234 meshbb.AddQuad(inds, attr);
235 eidx++;
236 }
237 else if (sdim == 3)
238 {
239 Vector xyz(3);
240 xyz(0) = xyzminmax_el(0);
241 xyz(1) = xyzminmax_el(1);
242 xyz(2) = xyzminmax_el(2);
243 meshbb.AddVertex(xyz);
244
245 xyz(0) = xyzminmax_el(3);
246 xyz(1) = xyzminmax_el(1);
247 xyz(2) = xyzminmax_el(2);
248 meshbb.AddVertex(xyz);
249
250 xyz(0) = xyzminmax_el(3);
251 xyz(1) = xyzminmax_el(4);
252 xyz(2) = xyzminmax_el(2);
253 meshbb.AddVertex(xyz);
254
255 xyz(0) = xyzminmax_el(0);
256 xyz(1) = xyzminmax_el(4);
257 xyz(2) = xyzminmax_el(2);
258 meshbb.AddVertex(xyz);
259
260 xyz(0) = xyzminmax_el(0);
261 xyz(1) = xyzminmax_el(1);
262 xyz(2) = xyzminmax_el(5);
263 meshbb.AddVertex(xyz);
264
265 xyz(0) = xyzminmax_el(3);
266 xyz(1) = xyzminmax_el(1);
267 xyz(2) = xyzminmax_el(5);
268 meshbb.AddVertex(xyz);
269
270 xyz(0) = xyzminmax_el(3);
271 xyz(1) = xyzminmax_el(4);
272 xyz(2) = xyzminmax_el(5);
273 meshbb.AddVertex(xyz);
274
275 xyz(0) = xyzminmax_el(0);
276 xyz(1) = xyzminmax_el(4);
277 xyz(2) = xyzminmax_el(5);
278 meshbb.AddVertex(xyz);
279
280 const int inds[8] = {vidx++, vidx++, vidx++, vidx++,
281 vidx++, vidx++, vidx++, vidx++
282 };
283 meshbb.AddHex(inds, (eidx++)+1);
284 }
285 }
286 if (sdim == 2)
287 {
288 meshbb.FinalizeQuadMesh(1, 1, true);
289 }
290 else
291 {
292 meshbb.FinalizeHexMesh(1, 1, true);
293 }
294 return meshbb;
295}
296
298 const Array<int> ordering)
299{
300 const int np = irule.GetNPoints();
301 MFEM_VERIFY(np == ordering.Size(), "Invalid permutation size");
302 IntegrationRule ir(np);
303 ir.SetOrder(irule.GetOrder());
304
305 for (int i = 0; i < np; i++)
306 {
307 IntegrationPoint &ip_new = ir.IntPoint(i);
308 const IntegrationPoint &ip_old = irule.IntPoint(ordering[i]);
309 ip_new.Set(ip_old.x, ip_old.y, ip_old.z, ip_old.weight);
310 }
311
312 return ir;
313}
314
316{
317 int dim = mesh->Dimension();
318 FiniteElementSpace *fespace = detgf->FESpace();
319 Array<int> dofs;
320
321 for (int e = 0; e < mesh->GetNE(); e++)
322 {
323 const FiniteElement *fe = fespace->GetFE(e);
324 const IntegrationRule ir = fe->GetNodes();
326 DenseMatrix Jac(fe->GetDim());
327 const NodalFiniteElement *nfe = dynamic_cast<const NodalFiniteElement*>
328 (fe);
329 const Array<int> &irordering = nfe->GetLexicographicOrdering();
330 IntegrationRule ir2 = irordering.Size() ?
331 PermuteIR(ir, irordering) :
332 ir;
333
334 Vector detvals(ir2.GetNPoints());
335 Vector loc(dim);
336 for (int q = 0; q < ir2.GetNPoints(); q++)
337 {
338 IntegrationPoint ip = ir2.IntPoint(q);
339 transf->SetIntPoint(&ip);
340 transf->Transform(ip, loc);
341 Jac = transf->Jacobian();
342 detvals(q) = Jac.Weight();
343 }
344
345 fespace->GetElementDofs(e, dofs);
346 if (irordering.Size())
347 {
348 for (int i = 0; i < dofs.Size(); i++)
349 {
350 (*detgf)(dofs[i]) = detvals(irordering[i]);
351 }
352 }
353 else
354 {
355 detgf->SetSubVector(dofs, detvals);
356 }
357 }
358}
359
360void VisualizeBB(Mesh &mesh, char *title, int pos_x, int pos_y)
361{
362 socketstream sock;
363 sock.open("localhost", 19916);
364 sock << "mesh\n";
365 mesh.Print(sock);
366 std::string keystrokes = mesh.SpaceDimension() == 2 ? "keys em" : "keys )";
367 sock << "window_title '"<< title << "'\n"
368 << "window_geometry "
369 << pos_x << " " << pos_y << " " << 400 << " " << 400 << "\n"
370 // << "keys jRmclA//]]]]]]]]" << endl;
371 << keystrokes << endl;
372}
373
375 char *title, int pos_x, int pos_y)
376{
377 socketstream sock;
378 if (pmesh.GetMyRank() == 0)
379 {
380 sock.open("localhost", 19916);
381 sock << "solution\n";
382 }
383 pmesh.PrintAsOne(sock);
384 input.SaveAsOne(sock);
385 if (pmesh.GetMyRank() == 0)
386 {
387 sock << "window_title '"<< title << "'\n"
388 << "window_geometry "
389 << pos_x << " " << pos_y << " " << 400 << " " << 400 << "\n"
390 << "keys jRmclApppppppppppp//]]]]]]]]" << endl;
391 }
392}
int Size() const
Return the logical size of the array.
Definition array.hpp:166
@ GaussLobatto
Closed type.
Definition fe_base.hpp:36
virtual void SetFormat(int fmt)
Set the desired output mesh and data format.
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
real_t Weight() const
Definition densemat.cpp:553
const DenseMatrix & Jacobian()
Return the Jacobian matrix of the transformation at the currently set IntegrationPoint,...
Definition eltrans.hpp:132
void SetIntPoint(const IntegrationPoint *ip)
Set the integration point ip that weights and Jacobians will be evaluated at.
Definition eltrans.hpp:106
virtual void Transform(const IntegrationPoint &, Vector &)=0
Transform integration point from reference coordinates to physical coordinates and store them in the ...
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:208
DofTransformation * GetElementDofs(int elem, Array< int > &dofs) const
Returns indices of degrees of freedom of element 'elem'. The returned indices are offsets into an ldo...
Definition fespace.cpp:3502
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:304
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
Definition fespace.cpp:3824
Abstract class for all finite elements.
Definition fe_base.hpp:247
int GetDim() const
Returns the reference space dimension for the finite element.
Definition fe_base.hpp:324
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition fe_base.hpp:403
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:32
PLBound GetElementBounds(Vector &lower, Vector &upper, const int ref_factor=1, const int vdim=-1) const
virtual void GetElementDofValues(int el, Vector &dof_vals) const
FiniteElementSpace * FESpace()
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.cpp:33
Class for integration point with weight.
Definition intrules.hpp:35
void Set(const real_t *p, const int dim)
Definition intrules.hpp:46
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:100
int GetOrder() const
Returns the order of the integration rule.
Definition intrules.hpp:249
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition intrules.hpp:256
void SetOrder(const int order)
Sets the order of the integration rule. This is only for keeping order information,...
Definition intrules.hpp:253
IntegrationPoint & IntPoint(int i)
Returns a reference to the i-th integration point.
Definition intrules.hpp:259
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:350
Mesh data type.
Definition mesh.hpp:65
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:2085
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:2567
void Clear()
Clear the contents of the Mesh.
Definition mesh.hpp:827
int AddVertex(real_t x, real_t y=0.0, real_t z=0.0)
Definition mesh.cpp:2000
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1377
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1306
void FinalizeHexMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a hexahedral Mesh.
Definition mesh.cpp:3466
void GetElementTransformation(int i, IsoparametricTransformation *ElTr) const
Builds the transformation defining the i-th element in ElTr. ElTr must be allocated in advance and wi...
Definition mesh.cpp:360
void FinalizeQuadMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a quadrilateral Mesh.
Definition mesh.cpp:2531
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1309
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:9627
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:2148
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).
Class for standard nodal finite elements.
Definition fe_base.hpp:724
const Array< int > & GetLexicographicOrdering() const
Get an Array<int> that maps lexicographically ordered indices to the indices of the respective nodes/...
Definition fe_base.hpp:804
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 ...
PLBound GetBounds(Vector &lower, Vector &upper, const int ref_factor=1, const int vdim=-1) const override
Class for parallel meshes.
Definition pmesh.hpp:34
Mesh GetSerialMesh(int save_rank) const
Definition pmesh.cpp:5319
int GetMyRank() const
Definition pmesh.hpp:407
void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1) override
Set the curvature of the mesh nodes using the given polynomial degree.
Definition pmesh.cpp:2017
void PrintAsOne(std::ostream &out=mfem::out, const std::string &comments="") const
Definition pmesh.cpp:4994
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 dim
Definition ex24.cpp:53
int main()
void VisualizeBB(Mesh &mesh, char *title, int pos_x, int pos_y)
IntegrationRule PermuteIR(const IntegrationRule &irule, const Array< int > ordering)
void GetDeterminantJacobianGF(ParMesh *mesh, ParGridFunction *detgf)
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
std::array< int, NCMesh::MaxFaceNodes > nodes