#include "mfem.hpp" #include #include using namespace std; using namespace mfem; class ZCoefficient : public VectorCoefficient { protected: GridFunction *psi; public: ZCoefficient(int vdim, GridFunction &psi_) : VectorCoefficient(vdim), psi(&psi_) { } virtual void Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip); }; class DZCoefficient : public MatrixCoefficient { protected: GridFunction *psi; public: DZCoefficient(int height, GridFunction &psi_) : MatrixCoefficient(height), psi(&psi_) { } virtual void Eval(DenseMatrix &K, ElementTransformation &T, const IntegrationPoint &ip); }; bool CheckVectorComponents(const mfem::GridFunction &gf, double limit) { const double* data = gf.GetData(); const int size = gf.Size(); for (int i = 0; i < size; ++i) { if (std::abs(data[i]) > limit) { const int vdim = gf.FESpace()->GetVDim(); int dof_index = i / vdim; int component_index = i % vdim; std::cout << "--> Condition VIOLATED at DOF #" << dof_index << ", component " << component_index << ". Value: " << data[i] << ", Limit: " << limit << std::endl; return false; } } return true; } class RotationCoefficient : public MatrixCoefficient { public: RotationCoefficient() : MatrixCoefficient(2) {} virtual void Eval(DenseMatrix &M, ElementTransformation &T, const IntegrationPoint &ip) { M(0,0) = 0; M(0,1) = -1; // [0, -1] M(1,0) = 1; M(1,1) = 0; // [1, 0] } }; int main(int argc, char *argv[]) { // const char *mesh_file = "../data/star.mesh"; int order = 2; int order_l2 = 1; int max_it = 10; int ref_levels = 3; real_t alpha = 1.0; real_t growth_rate = 1.0; real_t newton_scaling = 0.9; real_t tichonov = 1e-1; real_t tol = 1e-6; int ex = 1; bool visualization = true; OptionsParser args(argc, argv); // args.AddOption(&mesh_file, "-m", "--mesh", // "Mesh file to use."); args.AddOption(&order, "-o", "--order", "Finite element order for RT space"); args.AddOption(&order_l2, "-o2", "--order", "FEM order for L2 vec space"); args.AddOption(&ex, "-ex", "--example", "example number"); args.AddOption(&ref_levels, "-r", "--refs", "Number of h-refinements."); args.AddOption(&max_it, "-mi", "--max-it", "Maximum number of iterations"); args.AddOption(&tol, "-tol", "--tol", "Stopping criteria based on the difference between" "successive solution updates"); args.AddOption(&alpha, "-step", "--step", "Initial size alpha"); args.AddOption(&growth_rate, "-gr", "--growth-rate", "Growth rate of the step size alpha"); args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", "--no-visualization", "Enable or disable GLVis visualization."); args.Parse(); if (!args.Good()) { args.PrintUsage(cout); return 1; } args.PrintOptions(cout); Mesh mesh = Mesh::MakeCartesian2D(1, 1, Element::Type::TRIANGLE, false); const int dim = mesh.Dimension(); const int sdim = mesh.SpaceDimension(); for (int l = 0; l < ref_levels; l++) { mesh.UniformRefinement(); } int curvature_order = max(order,2); mesh.SetCurvature(curvature_order); RT_FECollection RTfec(order, dim); FiniteElementSpace RTfes(&mesh, &RTfec); H1_FECollection h1fec(order_l2, sdim); FiniteElementSpace h1fes(&mesh, &h1fec); L2_FECollection L2fec(order_l2, dim); FiniteElementSpace L2fes(&mesh, &L2fec, 2); Array ess_tdof_list_rt; RTfes.GetBoundaryTrueDofs(ess_tdof_list_rt); Array ess_tdof_list_h1; h1fes.GetBoundaryTrueDofs(ess_tdof_list_h1); Array ess_bdr(mesh.bdr_attributes.Max()); ess_bdr = 1; // ess_tdof_list = 1; // if (mesh.bdr_attributes.Size()) // { // Array ess_bdr(mesh.bdr_attributes.Max()); // ess_bdr = 1; // RTfes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list); // } cout << "Number of H(div) dofs: " << RTfes.GetTrueVSize() << endl; cout << "Number of L² dofs: " << L2fes.GetTrueVSize() << endl; cout << "Number of H1 dofs: " << h1fes.GetTrueVSize() << endl; Array offsets({0, RTfes.GetVSize(), h1fes.GetVSize(), L2fes.GetVSize(), 1}); offsets.PartialSum(); BlockVector x(offsets), rhs(offsets); x = 0.0; rhs = 0.0; GridFunction p_gf(&RTfes, x.GetBlock(0)), vphi_gf(&h1fes, x.GetBlock(1)), delta_psi_gf(&L2fes, x.GetBlock(2)); GridFunction psi_old_gf(&L2fes); GridFunction psi_gf(&L2fes); GridFunction p_old_gf(&RTfes); delta_psi_gf = 0.0; psi_gf = 0.0; p_gf = 0.0; psi_old_gf = psi_gf; p_old_gf = p_gf; VectorGridFunctionCoefficient psi_old_cf(&psi_old_gf), psi_cf(&psi_gf), p_vc(&p_gf); char vishost[] = "localhost"; int visport = 19916; socketstream sol_sock, varphi_sol_sock, true_sock, varphi_true_sock; if (visualization) { sol_sock.open(vishost,visport); sol_sock.precision(8); // sol_sock << "keys jlA\n"; // turn off perspective & light // sol_sock << "keys cmA\n"; // colorbar + mesh + anti-alias true_sock.open(vishost,visport); true_sock.precision(8); varphi_sol_sock.open(vishost, visport); varphi_sol_sock.precision(8); varphi_true_sock.open(vishost, visport); varphi_true_sock.precision(8); } ConstantCoefficient one_cf(1.0); ConstantCoefficient neg_one(-1.0); VectorConstantCoefficient zero_vec_cf(Vector({0., 0.})); ConstantCoefficient zero_cf(0.0); VectorConstantCoefficient one_vec_cf(Vector({1., 1.})); VectorConstantCoefficient neg_one_vec_cf(Vector({-1., -1.})); ConstantCoefficient tichonov_cf(tichonov); ConstantCoefficient neg_tichonov_cf(-1.0*tichonov); ConstantCoefficient alpha_cf((real_t) alpha); ProductCoefficient neg_alpha_cf(neg_one, alpha_cf); ZCoefficient Z(sdim, psi_gf); DZCoefficient DZ(sdim, psi_gf); ScalarMatrixProductCoefficient neg_DZ(-1.0, DZ); VectorSumCoefficient psi_newton_res(psi_old_cf, psi_cf, 1., -1.); LinearForm b0(&RTfes, rhs.GetBlock(0).GetData()), b2(&L2fes, rhs.GetBlock(2).GetData()); b0.AddDomainIntegrator(new VectorFEDomainLFIntegrator(psi_newton_res)); VectorFunctionCoefficient f_coeff(2, [ex](const Vector &x, Vector &u) { // NOTE: constant example // u(0) = 0.5; // u(1) = 0.0; // NOTE: linear example // u(0) = x(0); // u(1) = -1.0 * x(1); if (ex == 1) { // NOTE: trig example // u(0) = cosh(M_PI*x(0)) * sin(M_PI*x(1)); // u(1) = sinh(M_PI*x(0)) * cos(M_PI*x(1)); // u /= cosh(M_PI); // u(0) = pow(x(1), 2) * (1. - 2./3 * x(1)); // u(1) = pow(x(0), 2) * (-1. + 2./3 * x(0)); // u*= -4.; u(0) = sin(2 * M_PI * x(0)) * cos(2*M_PI*x(1)); u(1) = cos(2*M_PI*x(0))*sin(2*M_PI*x(1)); u *= -.9; } else if (ex == 2) { // NOTE: trig example 2 u(0) = cos(M_PI * x(0)) * sin (M_PI * x(1)); u(1) = cos(M_PI * x(1)) * sin (M_PI * x(0)); u *= (1. + 2. * pow(M_PI, 2)); } }); // mfem::Coefficient *f_rhs = nullptr; // // f_rhs = new mfem::FunctionCoefficient([](const mfem::Vector &x) // { // return 1./4. * sin(M_PI*x(0)) * sin (M_PI*x(1)); // }); // FunctionCoefficient f_rhs([](const mfem::Vector &x) // { // return 1./4. * cos(M_PI*x(0)) * cos(M_PI*x(1)); // }); ScalarVectorProductCoefficient alpha_f_cf(alpha_cf, f_coeff); b0.AddDomainIntegrator(new VectorFEDomainLFIntegrator(alpha_f_cf)); // ProductCoefficient alpha_f_cf(alpha_cf, f_rhs); // b0.AddDomainIntegrator(new VectorFEDomainLFDivIntegrator(alpha_f_cf)); b2.AddDomainIntegrator(new VectorDomainLFIntegrator(Z)); RotationCoefficient R; ScalarMatrixProductCoefficient neg_R(neg_one, R); ScalarMatrixProductCoefficient alpha_R(alpha_cf, R); // BlockMatrix A(offsets); BilinearForm a11(&h1fes); a11.AddDomainIntegrator(new DiffusionIntegrator(neg_one)); // ConstantCoefficient eps_cf(-1e-6); a11.Assemble(false); a11.Finalize(false); SparseMatrix &A11 = a11.SpMat(); BilinearForm a22(&L2fes); a22.AddDomainIntegrator(new VectorMassIntegrator(neg_DZ)); // a22.AddDomainIntegrator(new VectorMassIntegrator(eps_cf)); // Avg 0 condition int dof_h1(h1fes.GetTrueVSize()); LinearForm avg0_data(&h1fes); avg0_data.AddDomainIntegrator(new DomainLFIntegrator(one_cf)); avg0_data.Assemble(); Array avg0_i({0, dof_h1}), avg0_j(dof_h1); std::iota(avg0_j.begin(), avg0_j.end(), 0); SparseMatrix avg0(avg0_i.GetData(), avg0_j.GetData(), avg0_data.GetData(), 1, dof_h1, false, false, true); auto avg0T = *Transpose(avg0); int k; int total_iterations = 0; real_t increment_p = 0.1; GridFunction p_tmp(&RTfes); for (k = 0; k < max_it; k++) { p_tmp = p_old_gf; mfem::out << "\nOUTER ITERATION " << k+1 << endl; int j; for ( j = 0; j < 5; j++) { total_iterations++; b0.Assemble(); b2.Assemble(); BlockMatrix A(offsets); BilinearForm a00(&RTfes); a00.AddDomainIntegrator(new DivDivIntegrator(alpha_cf)); a00.SetDiagonalPolicy(mfem::Operator::DIAG_ONE); a00.Assemble(); a00.EliminateEssentialBC(ess_tdof_list_rt, x.GetBlock(0), rhs.GetBlock(0), mfem::Operator::DIAG_ONE); a00.Finalize(); SparseMatrix &A00 = a00.SpMat(); a22.Assemble(false); a22.Finalize(false); SparseMatrix &A22 = a22.SpMat(); MixedBilinearForm a01(&h1fes, &RTfes); a01.AddDomainIntegrator(new MixedVectorGradientIntegrator(alpha_R)); a01.Assemble(false); a01.EliminateTestDofs(ess_tdof_list_rt); a01.Finalize(false); SparseMatrix &A01 = a01.SpMat(); SparseMatrix *A10 = Transpose(A01); *A10 *= 1.0/alpha; // A.SetBlock(0, 1, &A01); // A.SetBlock(1, 0, A10); MixedBilinearForm a20(&RTfes, &L2fes); a20.AddDomainIntegrator(new VectorFEMassIntegrator()); a20.Assemble(); a20.EliminateTrialDofs(ess_tdof_list_rt, x.GetBlock(0), rhs.GetBlock(0)); a20.Finalize(); SparseMatrix &A20 = a20.SpMat(); SparseMatrix *A02 = Transpose(A20); // NOTE: this does not work because VectorFEMassIntegrator expects the test & trial spaces to be in a specific order :/ // MixedBilinearForm a02(&L2fes, &RTfes); // a02.AddDomainIntegrator(new VectorFEMassIntegrator()); // a02.Assemble(false); // a02.EliminateTestDofs(ess_bdr); // a02.Finalize(); // SparseMatrix &A02 = a02.SpMat(); // SparseMatrix *A20 = Transpose(A02); A.SetBlock(0,0,&A00); A.SetBlock(1,0,A10); A.SetBlock(0,1,&A01); A.SetBlock(1,1,&A11); A.SetBlock(2,0,&A20); A.SetBlock(0,2,A02); A.SetBlock(2,2,&A22); A.SetBlock(3, 1, &avg0); A.SetBlock(1, 3, &avg0T); // TODO: correct the schur preconditioner // Vector A22_diag(a22.Height()); // A22.GetDiag(A22_diag); // A22_diag.Reciprocal(); // SparseMatrix *S = Mult_AtDA(A20, A22_diag); // prec.SetDiagonalBlock(2, new DSmoother(A22)); // #ifndef MFEM_USE_SUITESPARSE BlockDiagonalPreconditioner prec(offsets); prec.SetDiagonalBlock(0,new GSSmoother(A00)); prec.SetDiagonalBlock(1,new GSSmoother(A11)); prec.SetDiagonalBlock(2,new DSmoother(A22)); prec.owns_blocks = 3; GMRES(A,prec,rhs,x,0,10000,500,1e-12,0.0); // prec.SetDiagonalBlock(0, new GSSmoother(*S)); // #else // prec.SetDiagonalBlock(0, new UMFPackSolver(*S)); // SparseMatrix *A_mono = A.CreateMonolithic(); // UMFPackSolver umf(*A_mono); // umf.Mult(rhs, x); // #endif // delete S; p_tmp -= p_gf; real_t Newton_update_size = p_tmp.ComputeL2Error(zero_vec_cf); p_tmp = p_gf; // Damped Newton update psi_gf.Add(newton_scaling, delta_psi_gf); // a11.Update(); // a22.Update(); b0.Update(); b2.Update(); if (visualization) { GridFunction p_vec(&L2fes); p_vec.ProjectCoefficient(p_vc); sol_sock << "solution\n" << mesh << p_vec << "window_title 'Discrete solution '" << flush; varphi_sol_sock << "solution\n" << mesh << vphi_gf << "window_title 'Discrete varphi '" << flush; } mfem::out << "Newton_update_size = " << Newton_update_size << endl; if (Newton_update_size < increment_p) { break; } } p_tmp = p_gf; p_tmp -= p_old_gf; increment_p = p_tmp.ComputeL2Error(zero_vec_cf); mfem::out << "Number of Newton iterations = " << j+1 << endl; mfem::out << "Increment (|| uₕ - uₕ_prvs||) = " << increment_p << endl; p_old_gf = p_gf; psi_old_gf = psi_gf; if (increment_p < tol || k == max_it-1) { break; } alpha *= max(growth_rate, 1_r); alpha_cf.constant = alpha; } // delete A01; // delete A10; mfem::out << "\n Outer iterations: " << k+1 << "\n Total iterations: " << total_iterations << "\n Total dofs: " << RTfes.GetTrueVSize() + L2fes.GetTrueVSize() << endl; VectorFunctionCoefficient exact_coeff(2, [ex](const Vector &x, Vector &u) { // NOTE: constant example // u(0) = 0.5; // u(1) = 0.0; // NOTE: linear example // u(0) = x(0); // u(1) = -1.0 * x(1); if (ex == 1) { // NOTE: trig example // u(0) = cosh(M_PI*x(0)) * sin(M_PI*x(1)); // u(1) = sinh(M_PI*x(0)) * cos(M_PI*x(1)); // // u /= cosh(M_PI); // u(0) = - sin(M_PI*x(0))*cos(M_PI*x(1)); // u(1) = cos(M_PI*x(0)) * sin(M_PI*x(1)); // u *= M_PI; // u(0) = x(0) * (1. - x(0))*(1 - 2.*x(1)); // u(1) = x(1) * (1. - x(1))*(2.*x(0) - 1.); // u*= 4.; u(0) = sin(2 * M_PI * x(0)) * cos(2*M_PI*x(1)); u(1) = -cos(2*M_PI*x(0))*sin(2*M_PI*x(1)); u *= 0.9; } else if (ex == 2) { // NOTE trig example 2 u(0) = cos(M_PI * x(0)) * sin (M_PI * x(1)); u(1) = cos(M_PI * x(1)) * sin (M_PI * x(0)); } }); FunctionCoefficient varphi_exact_coeff([](const Vector &x) { return 1 / (2. * M_PI) * sin(2*M_PI*x(0)) * sin(2*M_PI*x(1)); }); // GridFunctionCoefficient vphi_exact_gf(&varphi_exact_coeff); if (visualization) { GridFunction vphi_exact_gf(&h1fes); vphi_exact_gf.ProjectCoefficient(varphi_exact_coeff); GridFunction exact_vec(&L2fes); exact_vec.ProjectCoefficient(exact_coeff); true_sock << "solution\n" << mesh << exact_vec << "window_title 'True solution '" << flush; varphi_true_sock << "solution\n" << mesh << vphi_exact_gf << "window_title 'varphi True Solution '" << flush; } if (CheckVectorComponents(p_gf, 1.0)) { std::cout << "Result: SUCCESS. All components are within the limit." << std::endl; } else { std::cout << "Result: FAILURE. At least one component is outside the limit." << std::endl; } real_t l2_error = p_gf.ComputeL2Error(exact_coeff); cout << "L2 error: " << l2_error << endl; mfem::Coefficient *div_u_exact = nullptr; // NOTE: for constant, linear, trig examples, div p = 0 if (ex == 1) { // For ex=1, the divergence is zero. div_u_exact = new mfem::ConstantCoefficient(0.0); } else if (ex == 2) { // NOTE: for trig example2, div p != 0 div_u_exact = new mfem::FunctionCoefficient([](const mfem::Vector &x) { return -2. * M_PI * sin(M_PI * x(0)) * sin(M_PI * x(1)); }); } real_t hdiv_error = p_gf.ComputeDivError(div_u_exact); cout << "div error: " << hdiv_error << endl; return 0; } // NOTE: 2D ONLY void ZCoefficient::Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip) { MFEM_ASSERT(psi != NULL, "grid function is not set"); Vector psi_vals(2); psi->GetVectorValue(T, ip, psi_vals); V.SetSize(2); for (int i = 0; i < psi_vals.Size(); ++i) { V(i) = tanh(psi_vals(i) / 2.); } } // NOTE: 2D ONLY void DZCoefficient::Eval(DenseMatrix &K, ElementTransformation &T, const IntegrationPoint &ip) { MFEM_ASSERT(psi != NULL, "grid function is not set"); Vector psi_vals(2); psi->GetVectorValue(T, ip, psi_vals); K.SetSize(2); K = 0.0; for (int i = 0; i < 2; ++i) { K(i, i) = (1. - pow(tanh(psi_vals(i) / 2.), 2)) / 2.; } }