MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
nurbs_patch_ex1.cpp
Go to the documentation of this file.
1// MFEM Example 1 - NURBS with patch-wise assembly
2//
3// Compile with: make nurbs_patch_ex1
4//
5// Sample runs: nurbs_patch_ex1 -incdeg 3 -ref 2 -iro 8 -patcha
6// nurbs_patch_ex1 -incdeg 3 -ref 2 -iro 8 -patcha -pa
7// nurbs_patch_ex1 -incdeg 3 -ref 2 -iro 8 -patcha -fint
8//
9// Description: This example code demonstrates the use of MFEM to define a
10// simple finite element discretization of the Poisson problem
11// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
12// Specifically, we discretize using a FE space of the specified
13// order, or if order < 1 using an isoparametric/isogeometric
14// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
15// NURBS mesh, etc.)
16//
17// This example is a specialization of ex1 which demonstrates
18// patch-wise matrix assembly and partial assembly on NURBS
19// meshes. There is the option to compare run times of patch
20// and element assembly, as well as relative error computation.
21
22#include "mfem.hpp"
23#include <fstream>
24#include <iostream>
25
26using namespace std;
27using namespace mfem;
28
30 Array<int> const& ess_tdof_list, const bool pa,
31 const bool algebraic_ceed, GridFunction & x);
32
33int main(int argc, char *argv[])
34{
35 // 1. Parse command-line options.
36 const char *mesh_file = "../../data/beam-hex-nurbs.mesh";
37 int order = -1;
38 bool pa = false;
39 const char *device_config = "cpu";
40 bool visualization = true;
41 bool algebraic_ceed = false;
42 bool patchAssembly = false;
43 bool reducedIntegration = true;
44 bool compareToElementWise = true;
45 int nurbs_degree_increase = 0; // Elevate the NURBS mesh degree by this
46 int ref_levels = 0;
47 int visport = 19916;
48 int ir_order = -1;
49
50 OptionsParser args(argc, argv);
51 args.AddOption(&mesh_file, "-m", "--mesh",
52 "Mesh file to use.");
53 args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
54 "--no-partial-assembly", "Enable Partial Assembly.");
55 args.AddOption(&device_config, "-d", "--device",
56 "Device configuration string, see Device::Configure().");
57#ifdef MFEM_USE_CEED
58 args.AddOption(&algebraic_ceed, "-a", "--algebraic", "-no-a", "--no-algebraic",
59 "Use algebraic Ceed solver");
60#endif
61 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
62 "--no-visualization",
63 "Enable or disable GLVis visualization.");
64 args.AddOption(&patchAssembly, "-patcha", "--patch-assembly", "-no-patcha",
65 "--no-patch-assembly", "Enable patch-wise assembly.");
66 args.AddOption(&reducedIntegration, "-rint", "--reduced-integration", "-fint",
67 "--full-integration", "Enable reduced integration rules.");
68 args.AddOption(&ref_levels, "-ref", "--refine",
69 "Number of uniform mesh refinements.");
70 args.AddOption(&ir_order, "-iro", "--integration-order",
71 "Order of integration rule.");
72 args.AddOption(&nurbs_degree_increase, "-incdeg", "--nurbs-degree-increase",
73 "Elevate NURBS mesh degree by this amount.");
74 args.AddOption(&compareToElementWise, "-cew", "--compare-element",
75 "-no-compare", "-no-compare-element",
76 "Compute element-wise solution for comparison");
77 args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
78 args.Parse();
79 if (!args.Good())
80 {
81 args.PrintUsage(cout);
82 return 1;
83 }
84 args.PrintOptions(cout);
85
86 MFEM_VERIFY(!(pa && !patchAssembly), "Patch assembly must be used with -pa");
87
88 // 2. Enable hardware devices such as GPUs, and programming models such as
89 // CUDA, OCCA, RAJA and OpenMP based on command line options.
90 Device device(device_config);
91 device.Print();
92
93 // 3. Read the mesh from the given mesh file. For this NURBS patch example,
94 // only 3D hexahedral meshes are currently supported. The NURBS degree is
95 // optionally increased.
96 Mesh mesh(mesh_file, 1, 1);
97 int dim = mesh.Dimension();
98
99 if (nurbs_degree_increase > 0) { mesh.DegreeElevate(nurbs_degree_increase); }
100
101 // 4. Refine the mesh to increase the resolution.
102 for (int l = 0; l < ref_levels; l++)
103 {
104 mesh.UniformRefinement();
105 }
106
107 // 5. Define an isoparametric/isogeometric finite element space on the mesh.
108 FiniteElementCollection *fec = nullptr;
109 bool delete_fec;
110 if (mesh.GetNodes())
111 {
112 fec = mesh.GetNodes()->OwnFEC();
113 delete_fec = false;
114 cout << "Using isoparametric FEs: " << fec->Name() << endl;
115 }
116 else
117 {
118 MFEM_ABORT("Mesh must have nodes");
119 }
120 FiniteElementSpace fespace(&mesh, fec);
121 cout << "Number of finite element unknowns: "
122 << fespace.GetTrueVSize() << endl;
123
124 // 6. Determine the list of true (i.e. conforming) essential boundary dofs.
125 // In this example, the boundary conditions are defined by marking all
126 // the boundary attributes from the mesh as essential (Dirichlet) and
127 // converting them to a list of true dofs.
128 Array<int> ess_tdof_list;
129 if (mesh.bdr_attributes.Size())
130 {
131 Array<int> ess_bdr(mesh.bdr_attributes.Max());
132 ess_bdr = 1;
133 fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
134 }
135
136 // 7. Set up the linear form b(.) which corresponds to the right-hand side of
137 // the FEM linear system, which in this case is (1,phi_i) where phi_i are
138 // the basis functions in the finite element fespace.
139 LinearForm b(&fespace);
140 ConstantCoefficient one(1.0);
141 b.AddDomainIntegrator(new DomainLFIntegrator(one));
142 b.Assemble();
143
144 // 8. Define the solution vector x as a finite element grid function
145 // corresponding to fespace. Initialize x with initial guess of zero,
146 // which satisfies the boundary conditions.
147 GridFunction x(&fespace);
148 x = 0.0;
149
150 // 9. Set up the bilinear form a(.,.) on the finite element space
151 // corresponding to the Laplacian operator -Delta, by adding the Diffusion
152 // domain integrator.
154
155 if (patchAssembly && reducedIntegration && !pa)
156 {
157#ifdef MFEM_USE_SINGLE
158 cout << "Reduced integration is not supported in single precision.\n";
159 return MFEM_SKIP_RETURN_VALUE;
160#endif
161
162 di->SetIntegrationMode(NonlinearFormIntegrator::Mode::PATCHWISE_REDUCED);
163 }
164 else if (patchAssembly)
165 {
166 di->SetIntegrationMode(NonlinearFormIntegrator::Mode::PATCHWISE);
167 }
168
169 NURBSMeshRules *patchRule = nullptr;
170 if (order < 0)
171 {
172 if (ir_order == -1) { ir_order = 2*fec->GetOrder(); }
173 cout << "Using ir_order " << ir_order << endl;
174
175 patchRule = new NURBSMeshRules(mesh.NURBSext->GetNP(), dim);
176 // Loop over patches and set a different rule for each patch.
177 for (int p=0; p<mesh.NURBSext->GetNP(); ++p)
178 {
180 mesh.NURBSext->GetPatchKnotVectors(p, kv);
181
182 std::vector<const IntegrationRule*> ir1D(dim);
183 const IntegrationRule *ir = &IntRules.Get(Geometry::SEGMENT, ir_order);
184
185 // Construct 1D integration rules by applying the rule ir to each
186 // knot span.
187 for (int i=0; i<dim; ++i)
188 {
189 ir1D[i] = ir->ApplyToKnotIntervals(*kv[i]);
190 }
191
192 patchRule->SetPatchRules1D(p, ir1D);
193 } // loop (p) over patches
194
195 patchRule->Finalize(mesh);
196 di->SetNURBSPatchIntRule(patchRule);
197 }
198
199 // 10. Assemble and solve the linear system
200 cout << "Assembling system patch-wise and solving" << endl;
201 AssembleAndSolve(b, di, ess_tdof_list, pa, algebraic_ceed, x);
202
203 delete patchRule;
204
205 // 11. Save the refined mesh and the solution. This output can be viewed
206 // later using GLVis: "glvis -m refined.mesh -g sol.gf".
207 ofstream mesh_ofs("refined.mesh");
208 mesh_ofs.precision(8);
209 mesh.Print(mesh_ofs);
210 ofstream sol_ofs("sol.gf");
211 sol_ofs.precision(8);
212 x.Save(sol_ofs);
213
214 // 12. Send the solution by socket to a GLVis server.
215 if (visualization)
216 {
217 char vishost[] = "localhost";
218 socketstream sol_sock(vishost, visport);
219 sol_sock.precision(8);
220 sol_sock << "solution\n" << mesh << x << flush;
221 }
222
223 // 13. Optionally assemble element-wise and solve the linear system, to
224 // compare timings and compute relative error.
225 if (compareToElementWise)
226 {
227 Vector x_pw, x_ew;
228 x.GetTrueDofs(x_pw);
229
230 cout << "Assembling system element-wise and solving" << endl;
232 // Element-wise partial assembly is not supported on NURBS meshes, so we
233 // pass pa = false here.
234 AssembleAndSolve(b, d, ess_tdof_list, false, algebraic_ceed, x);
235
236 x.GetTrueDofs(x_ew);
237
238 const real_t solNorm = x_ew.Norml2();
239 x_ew -= x_pw;
240
241 cout << "Element-wise solution norm " << solNorm << endl;
242 cout << "Relative error of patch-wise solution "
243 << x_ew.Norml2() / solNorm << endl;
244 }
245
246 // 14. Free the used memory.
247 if (delete_fec)
248 {
249 delete fec;
250 }
251
252 return 0;
253}
254
255// This function deletes bfi when the BilinearForm goes out of scope.
257 Array<int> const& ess_tdof_list, const bool pa,
258 const bool algebraic_ceed, GridFunction & x)
259{
260 FiniteElementSpace *fespace = b.FESpace();
261 BilinearForm a(fespace);
262 if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
263
264 a.AddDomainIntegrator(bfi); // Takes ownership of bfi
265
266 StopWatch sw;
267 sw.Start();
268
269 // Assemble the bilinear form and the corresponding linear system, applying
270 // any necessary transformations such as: eliminating boundary conditions,
271 // applying conforming constraints for non-conforming AMR, etc.
272 a.Assemble();
273
274 sw.Stop();
275
276 const real_t timeAssemble = sw.RealTime();
277
278 sw.Clear();
279 sw.Start();
280
281 OperatorPtr A;
282 Vector B, X;
283 a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
284
285 sw.Stop();
286
287 const real_t timeFormLinearSystem = sw.RealTime();
288
289 cout << "Timing for Assemble: " << timeAssemble << " seconds" << endl;
290 cout << "Timing for FormLinearSystem: " << timeFormLinearSystem << " seconds"
291 << endl;
292 cout << "Timing for entire setup: " << timeAssemble + timeFormLinearSystem
293 << " seconds" << endl;
294
295 sw.Clear();
296 sw.Start();
297
298 // Solve the linear system A X = B.
299 if (!pa)
300 {
301#ifndef MFEM_USE_SUITESPARSE
302 // Use a simple symmetric Gauss-Seidel preconditioner with PCG.
303 GSSmoother M((SparseMatrix&)(*A));
304 PCG(*A, M, B, X, 1, 200, 1e-20, 0.0);
305#else
306 // If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
307 UMFPackSolver umf_solver;
308 umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
309 umf_solver.SetOperator(*A);
310 umf_solver.Mult(B, X);
311#endif
312 }
313 else
314 {
315 if (UsesTensorBasis(*fespace))
316 {
317 if (algebraic_ceed)
318 {
319 ceed::AlgebraicSolver M(a, ess_tdof_list);
320 PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
321 }
322 else
323 {
324 OperatorJacobiSmoother M(a, ess_tdof_list);
325 PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
326 }
327 }
328 else
329 {
330 CG(*A, B, X, 1, 400, 1e-20, 0.0);
331 }
332 }
333
334 sw.Stop();
335 cout << "Timing for solve " << sw.RealTime() << endl;
336
337 // Recover the solution as a finite element grid function.
338 a.RecoverFEMSolution(X, b, x);
339}
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:68
int Size() const
Return the logical size of the array.
Definition array.hpp:147
Abstract base class BilinearFormIntegrator.
A "square matrix" operator for the associated FE space and BLFIntegrators The sum of all the BLFInteg...
A coefficient that is constant across space and time.
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Definition device.hpp:123
void Print(std::ostream &out=mfem::out)
Print the configuration of the MFEM virtual device object.
Definition device.cpp:297
Class for domain integration .
Definition lininteg.hpp:106
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
int GetOrder() const
Return the order (polynomial degree) of the FE collection, corresponding to the order/degree returned...
Definition fe_coll.hpp:240
virtual const char * Name() const
Definition fe_coll.hpp:79
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:244
virtual int GetTrueVSize() const
Return the number of vector true (conforming) dofs.
Definition fespace.hpp:851
virtual void GetEssentialTrueDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_tdof_list, int component=-1) const
Get a list of essential true dofs, ess_tdof_list, corresponding to the boundary attributes marked in ...
Definition fespace.cpp:658
Data type for Gauss-Seidel smoother of sparse matrix.
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:31
virtual void Save(std::ostream &out) const
Save the GridFunction to an output stream.
void GetTrueDofs(Vector &tv) const
Extract the true-dofs from the GridFunction.
Definition gridfunc.cpp:363
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:100
IntegrationRule * ApplyToKnotIntervals(KnotVector const &kv) const
Return an integration rule for KnotVector kv, defined by applying this rule on each knot interval.
Definition intrules.cpp:181
const IntegrationRule & Get(int GeomType, int Order)
Returns an integration rule for given GeomType and Order.
void SetNURBSPatchIntRule(NURBSMeshRules *pr)
Sets an integration rule for use on NURBS patches.
Vector with associated FE space and LinearFormIntegrators.
Mesh data type.
Definition mesh.hpp:64
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition mesh.hpp:290
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:298
virtual void Print(std::ostream &os=mfem::out, const std::string &comments="") const
Definition mesh.hpp:2433
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1216
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:9294
void DegreeElevate(int rel_degree, int degree=16)
Definition mesh.cpp:6052
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:11295
int GetNP() const
Return the number of patches.
Definition nurbs.hpp:745
void GetPatchKnotVectors(int p, Array< KnotVector * > &kv)
Return KnotVectors in kv in each dimension for patch p.
Definition nurbs.cpp:3253
Class for defining different integration rules on each NURBS patch.
Definition intrules.hpp:279
void Finalize(Mesh const &mesh)
Finalize() must be called before this class can be used for assembly. In particular,...
void SetPatchRules1D(const int patch, std::vector< const IntegrationRule * > &ir1D)
Set 1D integration rules to be used as a tensor product rule on the patch with index patch....
Pointer to an Operator of a specified type.
Definition handle.hpp:34
Jacobi smoothing for a given bilinear form (no matrix necessary).
Definition solvers.hpp:333
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.
Data type sparse matrix.
Definition sparsemat.hpp:51
Timing object.
Definition tic_toc.hpp:36
double RealTime()
Return the number of real seconds elapsed since the stopwatch was started.
Definition tic_toc.cpp:432
void Start()
Start the stopwatch. The elapsed time is not cleared.
Definition tic_toc.cpp:411
void Stop()
Stop the stopwatch.
Definition tic_toc.cpp:422
void Clear()
Clear the elapsed time on the stopwatch and restart it if it's running.
Definition tic_toc.cpp:406
Direct sparse solver using UMFPACK.
Definition solvers.hpp:1121
real_t Control[UMFPACK_CONTROL]
Definition solvers.hpp:1131
void SetOperator(const Operator &op) override
Factorize the given Operator op which must be a SparseMatrix.
Definition solvers.cpp:3218
void Mult(const Vector &b, Vector &x) const override
Direct solution of the linear system using UMFPACK.
Definition solvers.cpp:3313
Vector data type.
Definition vector.hpp:82
real_t Norml2() const
Returns the l2 norm of the vector.
Definition vector.cpp:931
Wrapper for AlgebraicMultigrid object.
int dim
Definition ex24.cpp:53
int main()
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
void PCG(const Operator &A, Solver &B, const Vector &b, Vector &x, int print_iter, int max_num_iter, real_t RTOLERANCE, real_t ATOLERANCE)
Preconditioned conjugate gradient method. (tolerances are squared)
Definition solvers.cpp:949
void CG(const Operator &A, const Vector &b, Vector &x, int print_iter, int max_num_iter, real_t RTOLERANCE, real_t ATOLERANCE)
Conjugate gradient method. (tolerances are squared)
Definition solvers.cpp:934
bool UsesTensorBasis(const FiniteElementSpace &fes)
Return true if the mesh contains only one topology and the elements are tensor elements.
Definition fespace.hpp:1555
float real_t
Definition config.hpp:43
IntegrationRules IntRules(0, Quadrature1D::GaussLegendre)
A global object with all integration rules (defined in intrules.cpp)
Definition intrules.hpp:492
const char vishost[]
real_t p(const Vector &x, real_t t)
void AssembleAndSolve(LinearForm &b, BilinearFormIntegrator *bfi, Array< int > const &ess_tdof_list, const bool pa, const bool algebraic_ceed, GridFunction &x)