// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. // // --------------------------------- // Convergence Rates Test (Parallel) // --------------------------------- // // Compile with: make prates // // Sample runs: mpirun -np 4 prates -m ../../data/inline-segment.mesh -sr 1 -pr 4 -prob 0 -o 1 // mpirun -np 4 prates -m ../../data/inline-quad.mesh -sr 1 -pr 3 -prob 0 -o 2 // mpirun -np 4 prates -m ../../data/inline-quad.mesh -sr 1 -pr 3 -prob 1 -o 2 // mpirun -np 4 prates -m ../../data/inline-quad.mesh -sr 1 -pr 3 -prob 2 -o 2 // mpirun -np 4 prates -m ../../data/inline-tri.mesh -sr 1 -pr 3 -prob 2 -o 3 // mpirun -np 4 prates -m ../../data/star.mesh -sr 1 -pr 2 -prob 1 -o 4 // mpirun -np 4 prates -m ../../data/fichera.mesh -sr 1 -pr 2 -prob 2 -o 2 // mpirun -np 4 prates -m ../../data/inline-wedge.mesh -sr 0 -pr 2 -prob 0 -o 2 // mpirun -np 4 prates -m ../../data/inline-hex.mesh -sr 0 -pr 1 -prob 1 -o 3 // mpirun -np 4 prates -m ../../data/square-disc.mesh -sr 1 -pr 2 -prob 1 -o 2 // mpirun -np 4 prates -m ../../data/star.mesh -sr 1 -pr 2 -prob 3 -o 2 // mpirun -np 4 prates -m ../../data/star.mesh -sr 1 -pr 2 -prob 3 -o 2 -j 0 // mpirun -np 4 prates -m ../../data/inline-hex.mesh -sr 1 -pr 1 -prob 3 -o 2 // // Description: This example code demonstrates the use of MFEM to define and // solve finite element problem for various discretizations and // provide convergence rates in parallel. // // prob 0: H1 projection: // (grad u, grad v) + (u,v) = (grad u_exact, grad v) + (u_exact, v) // prob 1: H(curl) projection // (curl u, curl v) + (u,v) = (curl u_exact, curl v) + (u_exact, v) // prob 2: H(div) projection // (div u, div v) + (u,v) = (div u_exact, div v) + (u_exact, v) // prob 3: DG discretization for the Poisson problem // -Delta u = f #include "mfem.hpp" #include #include using namespace std; using namespace mfem; // Exact solution parameters: double sol_s[3] = { -0.32, 0.15, 0.24 }; double sol_k[3] = { 1.21, 1.45, 1.37 }; // H1 double scalar_u_exact(const Vector &x); double rhs_func(const Vector &x); void gradu_exact(const Vector &x, Vector &gradu); // Vector FE void vector_u_exact(const Vector &x, Vector & vector_u); // H(curl) void curlu_exact(const Vector &x, Vector &curlu); // H(div) double divu_exact(const Vector &x); int dim; int prob=0; int main(int argc, char *argv[]) { // 1. Initialize MPI and HYPRE. Mpi::Init(argc, argv); int num_procs = Mpi::WorldSize(); int myid = Mpi::WorldRank(); Hypre::Init(); // 2. Parse command-line options. const char *mesh_file = "../../data/inline-quad.mesh"; int order = 1; bool visualization = 1; int sr = 1; int pr = 1; int jump_scaling_type = 1; double sigma = -1.0; double kappa = -1.0; OptionsParser args(argc, argv); args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use."); args.AddOption(&order, "-o", "--order", "Finite element order (polynomial degree)"); args.AddOption(&prob, "-prob", "--problem", "Problem kind: 0: H1, 1: H(curl), 2: H(div), 3: DG "); args.AddOption(&sigma, "-s", "--sigma", "One of the two DG penalty parameters, typically +1/-1." " See the documentation of class DGDiffusionIntegrator."); args.AddOption(&kappa, "-k", "--kappa", "One of the two DG penalty parameters, should be positive." " Negative values are replaced with (order+1)^2."); args.AddOption(&jump_scaling_type, "-j", "--jump-scaling", "Scaling of the jump error for DG methods: " "0: no scaling, 1: 1/h, 2: p^2/h"); args.AddOption(&sr, "-sr", "--serial_ref", "Number of serial refinements."); args.AddOption(&pr, "-pr", "--parallel_ref", "Number of parallel refinements."); args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", "--no-visualization", "Enable or disable GLVis visualization."); args.Parse(); if (!args.Good()) { if (myid == 0) { args.PrintUsage(cout); } return 1; } if (prob >3 || prob <0) { prob = 0; } // default problem = H1 if (prob == 3) { if (kappa < 0) { kappa = (order+1)*(order+1); } } if (myid == 0) { args.PrintOptions(cout); } // 3. Read the (serial) mesh from the given mesh file on all processors. We // can handle triangular, quadrilateral, tetrahedral, hexahedral, surface // and volume meshes with the same code. Mesh *mesh = new Mesh(mesh_file, 1, 1); dim = mesh->Dimension(); // 4. Refine the serial mesh on all processors to increase the resolution. for (int i = 0; i < sr; i++ ) { mesh->UniformRefinement(); } // 5. Define a parallel mesh by a partitioning of the serial mesh. Once the // parallel mesh is defined, the serial mesh can be deleted. ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh); delete mesh; // 6. Define a parallel finite element space on the parallel mesh. FiniteElementCollection *fec=nullptr; switch (prob) { case 0: fec = new H1_FECollection(order,dim); break; case 1: fec = new ND_FECollection(order,dim); break; case 2: fec = new RT_FECollection(order-1,dim); break; case 3: fec = new DG_FECollection(order,dim); break; default: break; } ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec); // 7. Define the solution vector x as a parallel finite element grid function // corresponding to fespace. ParGridFunction x(fespace); x = 0.0; // 8. Set up the parallel linear form b(.) and the parallel bilinear form // a(.,.). FunctionCoefficient *f=nullptr; FunctionCoefficient *scalar_u=nullptr; FunctionCoefficient *divu=nullptr; VectorFunctionCoefficient *vector_u=nullptr; VectorFunctionCoefficient *gradu=nullptr; VectorFunctionCoefficient *curlu=nullptr; ConstantCoefficient one(1.0); ParLinearForm b(fespace); ParBilinearForm a(fespace); switch (prob) { case 0: //(grad u_ex, grad v) + (u_ex,v) scalar_u = new FunctionCoefficient(scalar_u_exact); gradu = new VectorFunctionCoefficient(dim,gradu_exact); b.AddDomainIntegrator(new DomainLFGradIntegrator(*gradu)); b.AddDomainIntegrator(new DomainLFIntegrator(*scalar_u)); // (grad u, grad v) + (u,v) a.AddDomainIntegrator(new DiffusionIntegrator(one)); a.AddDomainIntegrator(new MassIntegrator(one)); break; case 1: //(curl u_ex, curl v) + (u_ex,v) vector_u = new VectorFunctionCoefficient(dim,vector_u_exact); curlu = new VectorFunctionCoefficient((dim==3)?dim:1,curlu_exact); b.AddDomainIntegrator(new VectorFEDomainLFCurlIntegrator(*curlu)); b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(*vector_u)); // (curl u, curl v) + (u,v) a.AddDomainIntegrator(new CurlCurlIntegrator(one)); a.AddDomainIntegrator(new VectorFEMassIntegrator(one)); break; case 2: //(div u_ex, div v) + (u_ex,v) vector_u = new VectorFunctionCoefficient(dim,vector_u_exact); divu = new FunctionCoefficient(divu_exact); b.AddDomainIntegrator(new VectorFEDomainLFDivIntegrator(*divu)); b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(*vector_u)); // (div u, div v) + (u,v) a.AddDomainIntegrator(new DivDivIntegrator(one)); a.AddDomainIntegrator(new VectorFEMassIntegrator(one)); break; case 3: scalar_u = new FunctionCoefficient(scalar_u_exact); f = new FunctionCoefficient(rhs_func); gradu = new VectorFunctionCoefficient(dim,gradu_exact); b.AddDomainIntegrator(new DomainLFIntegrator(*f)); b.AddBdrFaceIntegrator( new DGDirichletLFIntegrator(*scalar_u, one, sigma, kappa)); a.AddDomainIntegrator(new DiffusionIntegrator(one)); a.AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa)); a.AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa)); break; default: break; } // 9. Perform successive parallel refinements, compute the L2 error and the // corresponding rate of convergence. ConvergenceStudy rates; for (int l = 0; l <= pr; l++) { b.Assemble(); a.Assemble(); a.Finalize(); HypreParMatrix *A = a.ParallelAssemble(); HypreParVector *B = b.ParallelAssemble(); HypreParVector *X = x.ParallelProject(); Solver *prec = nullptr; IterativeSolver *solver = nullptr; switch (prob) { case 0: case 3: prec = new HypreBoomerAMG(*A); dynamic_cast(prec)->SetPrintLevel(0); break; case 1: prec = new HypreAMS(*A, fespace); dynamic_cast(prec)->SetPrintLevel(0); break; case 2: if (dim == 2) { prec = new HypreAMS(*A, fespace); dynamic_cast(prec)->SetPrintLevel(0); } else { prec = new HypreADS(*A, fespace); dynamic_cast(prec)->SetPrintLevel(0); } break; default: break; } if (prob==3 && sigma !=-1.0) { solver = new GMRESSolver(MPI_COMM_WORLD); } else { solver = new CGSolver(MPI_COMM_WORLD); } solver->SetRelTol(1e-12); solver->SetMaxIter(2000); solver->SetPrintLevel(0); solver->SetPreconditioner(*prec); solver->SetOperator(*A); solver->Mult(*B, *X); delete prec; delete solver; x = *X; JumpScaling js(1.0, jump_scaling_type == 2 ? JumpScaling::P_SQUARED_OVER_H : jump_scaling_type == 1 ? JumpScaling::ONE_OVER_H : JumpScaling::CONSTANT); switch (prob) { case 0: rates.AddH1GridFunction(&x,scalar_u,gradu); break; case 1: rates.AddHcurlGridFunction(&x,vector_u,curlu); break; case 2: rates.AddHdivGridFunction(&x,vector_u,divu); break; case 3: rates.AddL2GridFunction(&x,scalar_u,gradu,&one,js); break; } delete X; delete B; delete A; if (l==pr) { break; } pmesh->UniformRefinement(); fespace->Update(); a.Update(); b.Update(); x.Update(); } rates.Print(); // 10. Send the solution by socket to a GLVis server. if (visualization) { char vishost[] = "localhost"; int visport = 19916; socketstream sol_sock(vishost, visport); sol_sock << "parallel " << num_procs << " " << myid << "\n"; sol_sock.precision(8); sol_sock << "solution\n" << *pmesh << x << "window_title 'Numerical Solution' " << flush; } // 11. Free the used memory. delete scalar_u; delete divu; delete vector_u; delete gradu; delete curlu; delete fespace; delete fec; delete pmesh; return 0; } double rhs_func(const Vector &x) { double val = 1.0, lap = 0.0; for (int d = 0; d < x.Size(); d++) { const double f = sin(M_PI*(sol_s[d]+sol_k[d]*x(d))); val *= f; lap = lap*f + val*M_PI*M_PI*sol_k[d]*sol_k[d]; } return lap; } double scalar_u_exact(const Vector &x) { double val = 1.0; for (int d = 0; d < x.Size(); d++) { val *= sin(M_PI*(sol_s[d]+sol_k[d]*x(d))); } return val; } void gradu_exact(const Vector &x, Vector &grad) { grad.SetSize(x.Size()); double *g = grad.GetData(); double val = 1.0; for (int d = 0; d < x.Size(); d++) { const double y = M_PI*(sol_s[d]+sol_k[d]*x(d)); const double f = sin(y); for (int j = 0; j < d; j++) { g[j] *= f; } g[d] = val*M_PI*sol_k[d]*cos(y); val *= f; } } void vector_u_exact(const Vector &x, Vector & vector_u) { vector_u.SetSize(x.Size()); vector_u=0.0; vector_u[0] = scalar_u_exact(x); } // H(curl) void curlu_exact(const Vector &x, Vector &curlu) { Vector grad; gradu_exact(x,grad); int n = (x.Size()==3)?3:1; curlu.SetSize(n); if (x.Size()==3) { curlu[0] = 0.0; curlu[1] = grad[2]; curlu[2] = -grad[1]; } else if (x.Size()==2) { curlu[0] = -grad[1]; } } // H(div) double divu_exact(const Vector &x) { Vector grad; gradu_exact(x,grad); return grad[0]; }