MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
gridfunction-bounds.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// Compute bounds of the given grid-function
14// ---------------------------------------------------------------------
15//
16// This miniapp computes piecewise linear bounds on a given gridfunction, and
17// visualizes the lower and upper bound for each element. The bounding approach
18// is based on the method described in:
19//
20// (1) Section 3 of Mittal et al., "General Field Evaluation in High-Order
21// Meshes on GPUs"
22// and
23// (2) Dzanic et al., "A method for bounding high-order finite element
24// functions: Applications to mesh validity and bounds-preserving limiters".
25//
26// We also use a recursive subdivision strategy to compute tighter estimate of
27// the function extremum.
28//
29// Compile with: make gridfunction-bounds
30//
31// Sample runs:
32// mpirun -np 4 gridfunction-bounds
33// mpirun -np 4 gridfunction-bounds -nb 100 -ref 5 -bt 2 -l2
34
35#include "mfem.hpp"
36
37using namespace mfem;
38using namespace std;
39
40void VisualizeField(ParMesh &pmesh, ParGridFunction &input,
41 char *title, int pos_x, int pos_y);
42
43int main (int argc, char *argv[])
44{
45 // 0. Initialize MPI and HYPRE.
46 Mpi::Init(argc, argv);
48
49 // Set the method's default parameters.
50 const char *mesh_file = "../gslib/triple-pt-1.mesh";
51 const char *sltn_file = "../gslib/triple-pt-1.gf";
52 int ref = 2;
53 bool visualization = true;
54 bool visit = false;
55 int b_type = -1;
56 bool continuous = true;
57 int nbrute = 0;
58 int rec_depth = 4;
59 real_t rel_tol = 1e-4;
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(&sltn_file, "-s", "--sltn",
66 "Solution file to use.");
67 args.AddOption(&ref, "-ref", "--piecewise-linear-ref-factor",
68 "Scaling factor for resolution of piecewise linear bounds."
69 " If less than 2, the resolution is picked automatically");
70 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
71 "--no-visualization",
72 "Enable or disable GLVis visualization.");
73 args.AddOption(&visit, "-visit", "--visit", "-no-visit",
74 "--no-visit",
75 "Enable or disable VisIt output.");
76 args.AddOption(&b_type, "-bt", "--basis-type",
77 "Project input function to a different bases. "
78 "-1 = don't project (default)."
79 "0 = Gauss-Legendre nodes. "
80 "1 = Gauss-Lobatto nodes. "
81 "2 = uniformly spaced nodes. ");
82 args.AddOption(&continuous, "-h1", "--h1", "-l2", "--l2",
83 "Use continuous or discontinuous space.");
84 args.AddOption(&nbrute, "-nb", "--nbrute",
85 "Brute force search for minimum in an array of nxnxn points "
86 "in each element.");
87 args.AddOption(&rec_depth, "-rd", "--rec-depth",
88 "Maximum depth for recursive subdivision to compute function "
89 "extremum.");
90 args.AddOption(&rel_tol, "-rt", "--rel-tol",
91 "Relative tolerance for termination of recursive "
92 "subdivision.");
93 args.ParseCheck();
94
95 Mesh mesh(mesh_file, 1, 1, false);
96 const int dim = mesh.Dimension();
97 if (continuous && b_type != -1)
98 {
99 MFEM_VERIFY(b_type > 0, "Continuous space do not support GL nodes. "
100 "Please use basis type: 1 for Lagrange interpolants on GLL "
101 " nodes 2 for positive bases on uniformly spaced nodes.");
102 }
103
104 std::unique_ptr<int[]> partition(
106 );
107
108 ifstream mat_stream_1(sltn_file);
109 std::unique_ptr<GridFunction> func(new GridFunction(&mesh, mat_stream_1));
110
111 ParMesh pmesh(MPI_COMM_WORLD, mesh, partition.get());
112 ParGridFunction pfunc(&pmesh, func.get(), partition.get());
113 int func_order = func->FESpace()->GetMaxElementOrder();
114 int vdim = pfunc.FESpace()->GetVDim();
115 int nel = pmesh.GetNE();
116
117 func.reset();
118 mesh.Clear();
119 partition.reset();
120
121 // Project input function based on user input
122 ParGridFunction *pfunc_proj = NULL;
123 if (b_type >= 0)
124 {
125 FiniteElementCollection *fec = NULL;
126 if (continuous)
127 {
128 fec = new H1_FECollection(func_order, dim, b_type);
129 }
130 else
131 {
132 fec = new L2_FECollection(func_order, dim, b_type);
133 }
134 int ordering = pfunc.FESpace()->GetOrdering();
135 ParFiniteElementSpace *fes = new ParFiniteElementSpace(&pmesh, fec,
136 vdim, ordering);
137 pfunc_proj = new ParGridFunction(fes);
138 pfunc_proj->MakeOwner(fec);
139 pfunc_proj->ProjectGridFunction(pfunc);
140 if (Mpi::Root())
141 {
142 cout << "fec name orig: " << pfunc.FESpace()->FEColl()->Name() <<
143 endl;
144 cout << "fec name: " << fec->Name() << endl;
145 }
146 }
147 else
148 {
149 pfunc_proj = &pfunc;
150 if (Mpi::Root())
151 {
152 cout << "fec name: " << pfunc.FESpace()->FEColl()->Name() << endl;
153 }
154 }
155
156 L2_FECollection fec_pc(0, dim);
157 ParFiniteElementSpace fes_pc(&pmesh, &fec_pc, vdim, Ordering::byNODES);
158 ParGridFunction lowerb(&fes_pc), upperb(&fes_pc);
159
160 // Compute bounds
161 PLBound plb = pfunc_proj->GetElementBounds(lowerb, upperb, ref);
162
163 // Compute minimum and maximum bounds via recursion
164 Vector bound_rec_min(vdim), bound_rec_max(vdim);
165 for (int d = 0; d < vdim; d++)
166 {
167 auto min_interval = pfunc_proj->EstimateFunctionMinimum(d, plb, rec_depth,
168 rel_tol);
169 auto max_interval = pfunc_proj->EstimateFunctionMaximum(d, plb, rec_depth,
170 rel_tol);
171 bound_rec_min(d) = min_interval.first;
172 bound_rec_max(d) = max_interval.second;
173 }
174
175 Vector bound_min(vdim), bound_max(vdim);
176 for (int d = 0; d < vdim; d++)
177 {
178 Vector lowerT(lowerb.GetData() + d*nel, nel);
179 Vector upperT(upperb.GetData() + d*nel, nel);
180 bound_min(d) = lowerT.Min();
181 bound_max(d) = upperT.Max();
182 }
183
184 MPI_Allreduce(MPI_IN_PLACE, bound_min.GetData(), vdim,
185 MPITypeMap<real_t>::mpi_type, MPI_MIN, pmesh.GetComm());
186 MPI_Allreduce(MPI_IN_PLACE, bound_max.GetData(), vdim,
187 MPITypeMap<real_t>::mpi_type, MPI_MAX, pmesh.GetComm());
188
189 // GLVis Visualization
190 if (visualization)
191 {
192 char title1[] = "Input gridfunction";
193 VisualizeField(pmesh, pfunc, title1, 0, 0);
194 if (b_type >= 0)
195 {
196 char title1p[] = "Projected gridfunction";
197 VisualizeField(pmesh, *pfunc_proj, title1p, 0, 400);
198 }
199 char title2[] = "Element-wise lower bound";
200 VisualizeField(pmesh, lowerb, title2, 400, 0);
201 char title3[] = "Element-wise upper bound";
202 VisualizeField(pmesh, upperb, title3, 800, 0);
203 }
204
205 // Visit Visualization
206 if (visit)
207 {
208 VisItDataCollection visit_dc("jacobian-determinant-bounds", &pmesh);
210 visit_dc.RegisterField("input-function", &pfunc);
211 if (b_type >= 0)
212 {
213 visit_dc.RegisterField("projected-function", pfunc_proj);
214 }
215 visit_dc.RegisterField("lower-bound", &lowerb);
216 visit_dc.RegisterField("upper-bound", &upperb);
217 visit_dc.Save();
218 }
219
220 if (nbrute > 0)
221 {
222 Vector global_min(vdim), global_max(vdim);
223 global_min = numeric_limits<real_t>::max();
224 global_max = numeric_limits<real_t>::min();
225 // search for the minimum value of pfunc_proj in each element at
226 // an array of integration points
227 for (int e = 0; e < pmesh.GetNE(); e++)
228 {
230 for (int k = 0; k < (dim > 2 ? nbrute : 1); k++)
231 {
232 ip.z = k/(nbrute-1.0);
233 for (int j = 0; j < (dim > 1 ? nbrute : 1); j++)
234 {
235 ip.y = j/(nbrute-1.0);
236 for (int i = 0; i < nbrute; i++)
237 {
238 ip.x = i/(nbrute-1.0);
239 for (int d = 0; d < vdim; d++)
240 {
241 real_t val = pfunc_proj->GetValue(e, ip, d+1);
242 global_min(d) = min(global_min(d), val);
243 global_max(d) = max(global_max(d), val);
244 }
245 }
246 }
247 }
248 }
249
250 MPI_Allreduce(MPI_IN_PLACE, global_min.GetData(), vdim,
251 MPITypeMap<real_t>::mpi_type, MPI_MIN, pmesh.GetComm());
252 MPI_Allreduce(MPI_IN_PLACE, global_max.GetData(), vdim,
253 MPITypeMap<real_t>::mpi_type, MPI_MAX, pmesh.GetComm());
254 if (Mpi::Root())
255 {
256 for (int d = 0; d < vdim; d++)
257 {
258 cout << "Compare function extremum for component " <<
259 d << endl;
260 constexpr int w = 20;
261 cout << left << setw(w) << " "
262 << setw(w) << "Brute force"
263 << setw(w) << "PL Bound"
264 << setw(w) << "PL Bound + recursion" << endl
265 << left << setw(w) << "Minimum: "
266 << setw(w) << global_min(d)
267 << setw(w) << bound_min(d)
268 << setw(w) << bound_rec_min(d) << endl
269 << left << setw(w) << "Difference: "
270 << setw(w) << "-"
271 << setw(w) << global_min(d)-bound_min(d)
272 << setw(w) << global_min(d)-bound_rec_min(d) << endl;
273 cout << endl
274 << left << setw(w) << "Maximum: "
275 << setw(w) << global_max(d)
276 << setw(w) << bound_max(d)
277 << setw(w) << bound_rec_max(d) << endl
278 << left << setw(w) << "Difference: "
279 << setw(w) << "-"
280 << setw(w) << bound_max(d)-global_max(d)
281 << setw(w) << bound_rec_max(d)-global_max(d) << endl;
282 cout << endl;
283 }
284 }
285 }
286
287 if (nbrute == 0 && Mpi::Root())
288 {
289 for (int d = 0; d < vdim; d++)
290 {
291 cout << "Compare function extremum for component " <<
292 d << endl;
293 constexpr int w = 20;
294 cout << left << setw(w) << " "
295 << setw(w) << "PL Bound"
296 << setw(w) << "PL Bound + recursion" << endl
297 << left << setw(w) << "Minimum: "
298 << setw(w) << bound_min(d)
299 << setw(w) << bound_rec_min(d) << endl;
300 cout << endl
301 << left << setw(w) << "Maximum: "
302 << setw(w) << bound_max(d)
303 << setw(w) << bound_rec_max(d) << endl;
304 }
305 }
306
307 if (b_type >= 0)
308 {
309 delete pfunc_proj;
310 }
311 return 0;
312}
313
315 char *title, int pos_x, int pos_y)
316{
317 socketstream sock;
318 if (pmesh.GetMyRank() == 0)
319 {
320 sock.open("localhost", 19916);
321 sock << "solution\n";
322 }
323 pmesh.PrintAsOne(sock);
324 input.SaveAsOne(sock);
325 if (pmesh.GetMyRank() == 0)
326 {
327 sock << "window_title '"<< title << "'\n"
328 << "window_geometry "
329 << pos_x << " " << pos_y << " " << 400 << " " << 400 << "\n"
330 << "keys jRmclApppppppppppp//]]]]]]]]" << endl;
331 }
332}
virtual void SetFormat(int fmt)
Set the desired output mesh and data format.
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
virtual const char * Name() const
Definition fe_coll.hpp:79
Ordering::Type GetOrdering() const
Return the ordering method.
Definition fespace.hpp:852
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:854
int GetVDim() const
Returns the vector dimension of the finite element space.
Definition fespace.hpp:817
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:53
PLBound GetElementBounds(Vector &lower, Vector &upper, const int ref_factor=1, const int vdim=-1) const
void MakeOwner(FiniteElementCollection *fec_)
Make the GridFunction the owner of fec_owned and fes.
Definition gridfunc.hpp:160
FiniteElementSpace * FESpace()
void ProjectGridFunction(const GridFunction &src)
Project the src GridFunction to this GridFunction, both of which must be on the same mesh.
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:291
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
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:369
Mesh data type.
Definition mesh.hpp:67
void Clear()
Clear the contents of the Mesh.
Definition mesh.hpp:835
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
int * GeneratePartitioning(int nparts, int part_method=1)
Definition mesh.cpp:9232
static bool Root()
Return true if the rank in MPI_COMM_WORLD is zero.
static int WorldSize()
Return the size of MPI_COMM_WORLD.
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
real_t GetValue(int i, const IntegrationPoint &ip, int vdim=1) const override
std::pair< real_t, real_t > EstimateFunctionMaximum(const int vdim, const PLBound &plb, const int max_depth, const real_t tol) const override
Estimate the GridFunction maximum across all elements.
std::pair< real_t, real_t > EstimateFunctionMinimum(const int vdim, const PLBound &plb, const int max_depth, const real_t tol) const override
Estimate the GridFunction minimum across all elements.
void SaveAsOne(const char *fname, int precision=16) const
Class for parallel meshes.
Definition pmesh.hpp:35
MPI_Comm GetComm() const
Definition pmesh.hpp:403
int GetMyRank() const
Definition pmesh.hpp:405
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
real_t Max() const
Returns the maximal element of the vector.
Definition vector.cpp:1200
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:243
real_t Min() const
Returns the minimal element of the vector.
Definition vector.cpp:1154
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
double func_order
Definition findpts.cpp:80
int main()
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)
float real_t
Definition config.hpp:46
STL namespace.
Helper struct to convert a C++ type to an MPI type.