MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
pfindpts.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// Parallel Find Points Miniapp: Evaluate grid function in physical space
14// ----------------------------------------------------------------------
15//
16// This miniapp demonstrates the interpolation of a high-order grid function on
17// a set of points in physical-space. The miniapp is based on GSLIB-FindPoints,
18// which provides two key functionalities. First, for a given set of points in
19// the physical-space, it determines the computational coordinates (element
20// number, reference-space coordinates inside the element, and processor number
21// [in parallel]) for each point. Second, based on computational coordinates, it
22// interpolates a grid function in the given points. Inside GSLIB, computation
23// of the coordinates requires use of a Hash Table to identify the candidate
24// processor and element for each point, followed by the Newton's method to
25// determine the reference-space coordinates inside the candidate element.
26//
27// Compile with: make pfindpts
28//
29// Sample runs:
30// mpirun -np 2 pfindpts -m ../../data/rt-2d-p4-tri.mesh -o 8 -mo 4
31// mpirun -np 2 pfindpts -m ../../data/inline-tri.mesh -o 3
32// mpirun -np 2 pfindpts -m ../../data/inline-quad.mesh -o 3
33// mpirun -np 2 pfindpts -m ../../data/inline-quad.mesh -o 3 -po 1
34// mpirun -np 2 pfindpts -m ../../data/inline-quad.mesh -o 3 -po 1 -gfo 1 -nc 2
35// mpirun -np 2 pfindpts -m ../../data/inline-quad.mesh -o 3 -hr
36// mpirun -np 2 pfindpts -m ../../data/inline-tet.mesh -o 3
37// mpirun -np 2 pfindpts -m ../../data/inline-hex.mesh -o 3
38// mpirun -np 2 pfindpts -m ../../data/inline-wedge.mesh -o 3
39// mpirun -np 2 pfindpts -m ../../data/amr-quad.mesh -o 2
40// mpirun -np 2 pfindpts -m ../../data/rt-2d-q3.mesh -o 8 -mo 4 -ft 2
41// mpirun -np 2 pfindpts -m ../../data/inline-quad.mesh -ft 1 -sr0
42// mpirun -np 2 pfindpts -m ../../data/square-mixed.mesh -o 2 -mo 2
43// mpirun -np 2 pfindpts -m ../../data/square-mixed.mesh -o 2 -mo 2 -hr
44// mpirun -np 2 pfindpts -m ../../data/square-mixed.mesh -o 2 -mo 3 -ft 2
45// mpirun -np 2 pfindpts -m ../../data/fichera-mixed.mesh -o 3 -mo 2
46// mpirun -np 2 pfindpts -m ../../data/inline-pyramid.mesh -o 1 -mo 1
47// mpirun -np 2 pfindpts -m ../../data/tinyzoo-3d.mesh -o 1 -mo 1
48// Device runs:
49// mpirun -np 2 pfindpts -m ../../data/inline-quad.mesh -o 3 -mo 2 -random 1 -d debug
50// mpirun -np 2 pfindpts -m ../../data/amr-quad.mesh -rs 1 -o 4 -mo 2 -random 1 -npt 100 -d debug
51// mpirun -np 2 pfindpts -m ../../data/inline-hex.mesh -o 3 -mo 2 -random 1 -d debug
52
53
54#include "mfem.hpp"
56
57using namespace mfem;
58using namespace std;
59
61
62// Scalar function to project
63double field_func(const Vector &x)
64{
65 const int dim = x.Size();
66 double res = 0.0;
67 for (int d = 0; d < dim; d++) { res += std::pow(x(d), func_order); }
68 return res;
69}
70
71void F_exact(const Vector &p, Vector &F)
72{
73 F(0) = field_func(p);
74 for (int i = 1; i < F.Size(); i++) { F(i) = (i+1)*F(0); }
75}
76
77int main (int argc, char *argv[])
78{
79 // Initialize MPI and HYPRE.
80 Mpi::Init(argc, argv);
81 int num_procs = Mpi::WorldSize();
82 int myid = Mpi::WorldRank();
84
85 // Set the method's default parameters.
86 const char *mesh_file = "../../data/rt-2d-q3.mesh";
87 int order = 3;
88 int mesh_poly_deg = 3;
89 int rs_levels = 0;
90 int rp_levels = 0;
91 bool visualization = false;
92 int fieldtype = 0;
93 int ncomp = 1;
94 bool search_on_rank_0 = false;
95 bool hrefinement = false;
96 int point_ordering = 0;
97 int gf_ordering = 0;
98 const char *devopt = "cpu";
99 int randomization = 0;
100 int npt = 100; //points per proc
101 int visport = 19916;
102
103 // Parse command-line options.
104 OptionsParser args(argc, argv);
105 args.AddOption(&mesh_file, "-m", "--mesh",
106 "Mesh file to use.");
107 args.AddOption(&order, "-o", "--order",
108 "Finite element order (polynomial degree).");
109 args.AddOption(&mesh_poly_deg, "-mo", "--mesh-order",
110 "Polynomial degree of mesh finite element space.");
111 args.AddOption(&rs_levels, "-rs", "--refine-serial",
112 "Number of times to refine the mesh uniformly in serial.");
113 args.AddOption(&rp_levels, "-rp", "--refine-parallel",
114 "Number of times to refine the mesh uniformly in parallel.");
115 args.AddOption(&fieldtype, "-ft", "--field-type",
116 "Field type: 0 - H1, 1 - L2, 2 - H(div), 3 - H(curl).");
117 args.AddOption(&ncomp, "-nc", "--ncomp",
118 "Number of components for H1 or L2 GridFunctions");
119 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
120 "--no-visualization",
121 "Enable or disable GLVis visualization.");
122 args.AddOption(&search_on_rank_0, "-sr0", "--search-on-r0", "-no-sr0",
123 "--no-search-on-r0",
124 "Enable search only on rank 0 (disable to search points on all tasks). "
125 "All points added by other procs are ignored.");
126 args.AddOption(&hrefinement, "-hr", "--h-refinement", "-no-hr",
127 "--no-h-refinement",
128 "Do random h refinements to mesh (does not work for pyramids).");
129 args.AddOption(&point_ordering, "-po", "--point-ordering",
130 "Ordering of points to be found."
131 "0 (default): byNodes, 1: byVDIM");
132 args.AddOption(&gf_ordering, "-gfo", "--gridfunc-ordering",
133 "Ordering of fespace that will be used for grid function to be interpolated. "
134 "0 (default): byNodes, 1: byVDIM");
135 args.AddOption(&devopt, "-d", "--device",
136 "Device configuration string, see Device::Configure().");
137 args.AddOption(&randomization, "-random", "--random",
138 "0: generate points randomly in the bounding box of domain, "
139 "1: generate points randomly inside each element in mesh.");
140 args.AddOption(&npt, "-npt", "--npt",
141 "# points / rank initialized on entire mesh (random = 0) or every element (random = 1).");
142 args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
143
144 args.Parse();
145 if (!args.Good())
146 {
147 args.PrintUsage(cout);
148 return 1;
149 }
150 if (myid == 0) { args.PrintOptions(cout); }
151
152 bool cpu_mode = strcmp(devopt,"cpu")==0;
153 Device device(devopt);
154 if (myid == 0) { device.Print();}
155
156 func_order = std::min(order, 2);
157
158 // Initialize and refine the starting mesh.
159 Mesh *mesh = new Mesh(mesh_file, 1, 1, false);
160 for (int lev = 0; lev < rs_levels; lev++) { mesh->UniformRefinement(); }
161 const int dim = mesh->Dimension();
162
163 if (mesh->GetNumGeometries(dim) != 1 ||
166 {
167 randomization = 0;
168 }
169
170 if (myid == 0)
171 {
172 cout << "Mesh curvature of the original mesh: ";
173 if (mesh->GetNodes()) { cout << mesh->GetNodes()->OwnFEC()->Name(); }
174 else { cout << "(NONE)"; }
175 cout << endl;
176 }
177
178 // Mesh bounding box (for the full serial mesh).
179 Vector pos_min, pos_max;
180 MFEM_VERIFY(mesh_poly_deg > 0, "The order of the mesh must be positive.");
181 mesh->GetBoundingBox(pos_min, pos_max, mesh_poly_deg);
182 if (myid == 0)
183 {
184 cout << "--- Generating points for:\n"
185 << "x in [" << pos_min(0) << ", " << pos_max(0) << "]\n"
186 << "y in [" << pos_min(1) << ", " << pos_max(1) << "]" << std::endl;
187 if (dim == 3)
188 {
189 cout << "z in [" << pos_min(2) << ", " << pos_max(2) << "]" << std::endl;
190 }
191 }
192
193 // Distribute the mesh.
194 if (hrefinement) { mesh->EnsureNCMesh(); }
195 ParMesh pmesh(MPI_COMM_WORLD, *mesh);
196 if (randomization == 0) { delete mesh; }
197 else
198 {
199 // we will need mesh nodal space later
200 if (mesh->GetNodes() == NULL) { mesh->SetCurvature(1); }
201 }
202 for (int lev = 0; lev < rp_levels; lev++) { pmesh.UniformRefinement(); }
203
204 // Random h-refinements to mesh
205 if (hrefinement) { pmesh.RandomRefinement(0.5); }
206
207 // Curve the mesh based on the chosen polynomial degree.
208 H1_FECollection fecm(mesh_poly_deg, dim);
209 ParFiniteElementSpace pfespace(&pmesh, &fecm, dim);
210 pmesh.SetNodalFESpace(&pfespace);
211 ParGridFunction x(&pfespace);
212 pmesh.SetNodalGridFunction(&x);
213 if (myid == 0)
214 {
215 cout << "Mesh curvature of the curved mesh: " << fecm.Name() << endl;
216 }
217
218 int nelemglob = pmesh.GetGlobalNE();
219
220 MFEM_VERIFY(ncomp > 0, "Invalid number of components.");
221 int vec_dim = ncomp;
222 FiniteElementCollection *fec = NULL;
223 if (fieldtype == 0)
224 {
225 fec = new H1_FECollection(order, dim);
226 if (myid == 0) { cout << "H1-GridFunction" << std::endl; }
227 }
228 else if (fieldtype == 1)
229 {
230 fec = new L2_FECollection(order, dim);
231 if (myid == 0) { cout << "L2-GridFunction" << std::endl; }
232 }
233 else if (fieldtype == 2)
234 {
235 fec = new RT_FECollection(order, dim);
236 ncomp = 1;
237 vec_dim = dim;
238 if (myid == 0) { cout << "H(div)-GridFunction" << std::endl; }
239 }
240 else if (fieldtype == 3)
241 {
242 fec = new ND_FECollection(order, dim);
243 ncomp = 1;
244 vec_dim = dim;
245 if (myid == 0) { cout << "H(curl)-GridFunction" << std::endl; }
246 }
247 else
248 {
249 if (myid == 0) { MFEM_ABORT("Invalid FECollection type."); }
250 }
251 ParFiniteElementSpace sc_fes(&pmesh, fec, ncomp, gf_ordering);
252 ParGridFunction field_vals(&sc_fes);
253
254 // Project the GridFunction using VectorFunctionCoefficient.
256 field_vals.ProjectCoefficient(F);
257
258 // Display the mesh and the field through glvis.
259 if (visualization)
260 {
261 char vishost[] = "localhost";
262 socketstream sout;
263 sout.open(vishost, visport);
264 if (!sout)
265 {
266 if (myid == 0)
267 {
268 cout << "Unable to connect to GLVis server at "
269 << vishost << ':' << visport << endl;
270 }
271 }
272 else
273 {
274 sout << "parallel " << num_procs << " " << myid << "\n";
275 sout.precision(8);
276 sout << "solution\n" << pmesh << field_vals;
277 if (dim == 2) { sout << "keys RmjA*****\n"; }
278 if (dim == 3) { sout << "keys mA\n"; }
279 sout << flush;
280 }
281 }
282
283 // Generate random points in physical coordinates over the whole mesh.
284 // Note that some points might be outside if the mesh is not a box.
285 int pts_cnt = npt;
286 Vector vxyz;
287 vxyz.UseDevice(!cpu_mode);
288 int npt_face_per_elem = 4; // number of pts on el faces for randomization != 0
289 if (randomization == 0)
290 {
291 vxyz.SetSize(pts_cnt * dim);
292 vxyz.Randomize(myid+1);
293
294 // Scale based on min/max dimensions
295 for (int i = 0; i < pts_cnt; i++)
296 {
297 for (int d = 0; d < dim; d++)
298 {
299 if (point_ordering == Ordering::byNODES)
300 {
301 vxyz(i + d*pts_cnt) =
302 pos_min(d) + vxyz(i + d*pts_cnt) * (pos_max(d) - pos_min(d));
303 }
304 else
305 {
306 vxyz(i*dim + d) =
307 pos_min(d) + vxyz(i*dim + d) * (pos_max(d) - pos_min(d));
308 }
309 }
310 }
311 }
312 else // randomization == 1
313 {
314 pts_cnt = npt*nelemglob;
315 vxyz.SetSize(pts_cnt * dim);
316 for (int i=0; i<mesh->GetNE(); i++)
317 {
318 const FiniteElementSpace *s_fespace = mesh->GetNodalFESpace();
319 ElementTransformation *transf = s_fespace->GetElementTransformation(i);
320
321 Vector pos_ref1(npt*dim);
322 pos_ref1.Randomize((myid+1)*17.0);
323 for (int j=0; j<npt; j++)
324 {
326 ip.x = pos_ref1(j*dim + 0);
327 ip.y = pos_ref1(j*dim + 1);
328 if (dim == 3)
329 {
330 ip.z = pos_ref1(j*dim + 2);
331 }
332 if (j < npt_face_per_elem)
333 {
334 ip.x = 0.0; // force point to be on the face
335 }
336 Vector pos_i(dim);
337 transf->Transform(ip, pos_i);
338 for (int d=0; d<dim; d++)
339 {
340 if (point_ordering == Ordering::byNODES)
341 {
342 vxyz(j + npt*i + d*npt*nelemglob) = pos_i(d);
343 }
344 else
345 {
346 vxyz((j + npt*i)*dim + d) = pos_i(d);
347 }
348 }
349 }
350 }
351 }
352
353 if ( (myid != 0) && (search_on_rank_0) )
354 {
355 pts_cnt = 0;
356 vxyz.Destroy();
357 }
358
359 // Find and Interpolate FE function values on the desired points.
360 Vector interp_vals(pts_cnt*vec_dim);
361 FindPointsGSLIB finder(pmesh);
363 // Enable GPU to CPU fallback for GPUData only if you are using an older
364 // version of GSLIB.
365 // finder.SetGPUtoCPUFallback(true);
366 finder.FindPoints(vxyz, point_ordering);
367
368 finder.Interpolate(field_vals, interp_vals);
369 if (interp_vals.UseDevice())
370 {
371 interp_vals.HostReadWrite();
372 }
373 vxyz.HostReadWrite();
374
375 Array<unsigned int> code_out = finder.GetCode();
376 Array<unsigned int> task_id_out = finder.GetProc();
377 Vector dist_p_out = finder.GetDist();
378 Vector rst = finder.GetReferencePosition();
379
380 int face_pts = 0, not_found = 0, found_loc = 0, found_away = 0;
381 double error = 0.0, max_error = 0.0, max_dist = 0.0;
382
383 Vector pos(dim);
384 for (int j = 0; j < vec_dim; j++)
385 {
386 for (int i = 0; i < pts_cnt; i++)
387 {
388 if (j == 0)
389 {
390 (task_id_out[i] == (unsigned)myid) ? found_loc++ : found_away++;
391 }
392
393 if (code_out[i] < 2)
394 {
395 for (int d = 0; d < dim; d++)
396 {
397 pos(d) = point_ordering == Ordering::byNODES ?
398 vxyz(d*pts_cnt + i) :
399 vxyz(i*dim + d);
400 }
401 Vector exact_val(vec_dim);
402 F_exact(pos, exact_val);
403 error = gf_ordering == Ordering::byNODES ?
404 fabs(exact_val(j) - interp_vals[i + j*pts_cnt]) :
405 fabs(exact_val(j) - interp_vals[i*vec_dim + j]);
406 max_error = std::max(max_error, error);
407 max_dist = std::max(max_dist, dist_p_out(i));
408 if (code_out[i] == 1 && j == 0) { face_pts++; }
409 }
410 else { if (j == 0) { not_found++; } }
411 }
412 }
413
414 MPI_Allreduce(MPI_IN_PLACE, &found_loc, 1, MPI_INT, MPI_SUM,
415 pfespace.GetComm());
416 MPI_Allreduce(MPI_IN_PLACE, &found_away, 1, MPI_INT, MPI_SUM,
417 pfespace.GetComm());
418 MPI_Allreduce(MPI_IN_PLACE, &face_pts, 1, MPI_INT, MPI_SUM, pfespace.GetComm());
419 MPI_Allreduce(MPI_IN_PLACE, &not_found, 1, MPI_INT, MPI_SUM,
420 pfespace.GetComm());
421 MPI_Allreduce(MPI_IN_PLACE, &max_error, 1, MPI_DOUBLE, MPI_MAX,
422 pfespace.GetComm());
423 MPI_Allreduce(MPI_IN_PLACE, &max_dist, 1, MPI_DOUBLE, MPI_MAX,
424 pfespace.GetComm());
425 MPI_Allreduce(MPI_IN_PLACE, &error, 1, MPI_DOUBLE, MPI_SUM, pfespace.GetComm());
426
427
428 if (myid == 0)
429 {
430 cout << setprecision(16)
431 << "Total number of elements: " << nelemglob
432 << "\nTotal number of procs: " << num_procs
433 << "\nSearched total points: " << (search_on_rank_0 ? pts_cnt :
434 pts_cnt*num_procs)
435 << "\nFound locally on ranks: " << found_loc
436 << "\nFound on other tasks: " << found_away
437 << "\nPoints not found: " << not_found
438 << "\nPoints on faces: " << face_pts
439 << "\nMax interp error: " << max_error
440 << "\nMax dist (of found): " << max_dist
441 << endl;
442 }
443
444 delete fec;
445 if (randomization != 0) { delete mesh; }
446
447 return 0;
448}
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Definition device.hpp:124
void Print(std::ostream &os=mfem::out)
Print the configuration of the MFEM virtual device object.
Definition device.cpp:317
virtual void Transform(const IntegrationPoint &, Vector &)=0
Transform integration point from reference coordinates to physical coordinates and store them in the ...
FindPointsGSLIB can robustly evaluate a GridFunction on an arbitrary collection of points....
Definition gslib.hpp:73
virtual const Vector & GetDist() const
Definition gslib.hpp:328
void FindPoints(const Vector &point_pos, int point_pos_ordering=Ordering::byNODES)
Definition gslib.cpp:236
virtual const Vector & GetReferencePosition() const
Return reference coordinates for each point found by FindPoints.
Definition gslib.hpp:325
virtual void Interpolate(const GridFunction &field_in, Vector &field_out)
Definition gslib.cpp:1759
virtual const Array< unsigned int > & GetCode() const
Definition gslib.hpp:319
virtual const Array< unsigned int > & GetProc() const
Return MPI rank on which each point was found by FindPoints.
Definition gslib.hpp:323
virtual void SetDistanceToleranceForPointsFoundOnBoundary(double bdr_tol_)
Definition gslib.hpp:298
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:208
ElementTransformation * GetElementTransformation(int i) const
Definition fespace.hpp:905
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:279
const char * Name() const override
Definition fe_coll.hpp:301
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:350
Mesh data type.
Definition mesh.hpp:65
Element::Type GetElementType(int i) const
Returns the type of element i.
Definition mesh.cpp:7962
const FiniteElementSpace * GetNodalFESpace() const
Definition mesh.cpp:6794
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1377
void GetBoundingBox(Vector &min, Vector &max, int ref=2)
Returns the minimum and maximum corners of the mesh bounding box.
Definition mesh.cpp:141
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1306
void RandomRefinement(real_t prob, bool aniso=false, int nonconforming=-1, int nc_limit=0)
Refine each element with given probability. Uses GeneralRefinement.
Definition mesh.cpp:11317
void SetNodalGridFunction(GridFunction *nodes, bool make_owner=false)
Definition mesh.cpp:6788
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:9627
long long GetGlobalNE() const
Return the total (global) number of elements.
Definition mesh.hpp:1406
void EnsureNCMesh(bool simplices_nonconforming=false)
Definition mesh.cpp:11293
int GetNumGeometries(int dim) const
Return the number of geometries of the given dimension present in the mesh.
Definition mesh.cpp:7558
virtual void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1)
Set the curvature of the mesh nodes using the given polynomial degree.
Definition mesh.cpp:6799
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:11637
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
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).
Arbitrary order H(curl)-conforming Nedelec finite elements.
Definition fe_coll.hpp:500
void Parse()
Parse the command-line options. Note that this function expects all the options provided through the ...
void PrintUsage(std::ostream &out) const
Print the usage message.
void PrintOptions(std::ostream &out) const
Print the options.
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
bool Good() const
Return true if the command line options were parsed successfully.
Abstract parallel finite element space.
Definition pfespace.hpp:31
MPI_Comm GetComm() const
Definition pfespace.hpp:337
Class for parallel grid function.
Definition pgridfunc.hpp:50
void ProjectCoefficient(Coefficient &coeff) override
Project coeff Coefficient to this GridFunction. The projection computation depends on the choice of t...
Class for parallel meshes.
Definition pmesh.hpp:34
void SetNodalFESpace(FiniteElementSpace *nfes) override
Definition pmesh.cpp:2038
Arbitrary order H(div)-conforming Raviart-Thomas finite elements.
Definition fe_coll.hpp:407
A general vector function coefficient.
Vector data type.
Definition vector.hpp:82
void Randomize(int seed=0)
Set random values in the vector.
Definition vector.cpp:955
void Destroy()
Destroy a vector.
Definition vector.hpp:673
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:145
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:584
virtual real_t * HostReadWrite()
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:540
int open(const char hostname[], int port)
Open the socket stream on 'port' at 'hostname'.
int dim
Definition ex24.cpp:53
int main()
const char vishost[]
real_t p(const Vector &x, real_t t)
double field_func(const Vector &x)
Definition pfindpts.cpp:63
double func_order
Definition pfindpts.cpp:60
void F_exact(const Vector &p, Vector &F)
Definition pfindpts.cpp:71