// 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. #include "mfem.hpp" #include "unit_tests.hpp" #include #include using namespace mfem; namespace testhelper { double SmoothSolutionX(const mfem::Vector& x) { return x(0); } double SmoothSolutionY(const mfem::Vector& x) { return x(1); } double SmoothSolutionZ(const mfem::Vector& x) { return x(2); } double NonsmoothSolutionX(const mfem::Vector& x) { return std::abs(x(0)-0.5); } double NonsmoothSolutionY(const mfem::Vector& x) { return std::abs(x(1)-0.5); } double NonsmoothSolutionZ(const mfem::Vector& x) { return std::abs(x(2)-0.5); } double SinXSinY(const mfem::Vector& x) { return std::sin(M_PI*x(0)) * std::sin(M_PI*x(1)); } } TEST_CASE("Least-squares ZZ estimator on 2D NCMesh", "[NCMesh]") { // Setup const auto order = GENERATE(1, 3, 5); Mesh mesh = Mesh::MakeCartesian2D(2, 2, Element::QUADRILATERAL); // Make the mesh NC mesh.EnsureNCMesh(); mesh.RandomRefinement(0.2); H1_FECollection fe_coll(order, mesh.Dimension()); FiniteElementSpace fespace(&mesh, &fe_coll); SECTION("Perfect Approximation X") { FunctionCoefficient u_analytic(testhelper::SmoothSolutionX); GridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); DiffusionIntegrator di; LSZienkiewiczZhuEstimator estimator(di, u_gf); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= 0.0); } REQUIRE(estimator.GetTotalError() > 0.0); } SECTION("Nonsmooth Approximation Y") { FunctionCoefficient u_analytic(testhelper::NonsmoothSolutionY); GridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); DiffusionIntegrator di; LSZienkiewiczZhuEstimator estimator(di, u_gf); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= 0.0); } REQUIRE(estimator.GetTotalError() > 0.0); } } TEST_CASE("Convergence rate test on 2D NCMesh", "[NCMesh]") { // Setup ConstantCoefficient one(1.0); const auto order = GENERATE(1, 2, 3, 4); Mesh mesh = Mesh::MakeCartesian2D(2, 2, Element::QUADRILATERAL); // Make the mesh NC mesh.EnsureNCMesh(); mesh.UniformRefinement(); H1_FECollection fe_coll(order, mesh.Dimension()); FiniteElementSpace fespace(&mesh, &fe_coll); FunctionCoefficient exsol(testhelper::SinXSinY); ProductCoefficient rhs(-2.0*M_PI*M_PI,exsol); LinearForm b(&fespace); BilinearForm a(&fespace); b.AddDomainIntegrator(new DomainLFIntegrator(rhs)); a.AddDomainIntegrator(new DiffusionIntegrator(one)); DiffusionIntegrator di; // Define the solution vector x as a finite element grid function GridFunction x(&fespace); double old_error = 0.0; double old_num_dofs = 0.0; double rate = 0.0; for (int it = 0; it < 4; it++) { int num_dofs = fespace.GetTrueVSize(); // Set Dirichlet boundary values in the GridFunction x. // Determine the list of Dirichlet true DOFs in the linear system. Array ess_bdr(mesh.bdr_attributes.Max()); ess_bdr = 1; x = 0.0; Array ess_tdof_list; fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list); // Solve for the current mesh: b.Assemble(); a.Assemble(); OperatorPtr A; Vector B, X; const int copy_interior = 1; a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior); GSSmoother M((SparseMatrix&)(*A)); PCG(*A, M, B, X, 0, 2000, 1e-30, 0.0); a.RecoverFEMSolution(X, b, x); LSZienkiewiczZhuEstimator estimator(di, x); estimator.GetLocalErrors(); double error = estimator.GetTotalError(); if (old_error > 0.0) { rate = log(error/old_error) / log(old_num_dofs/num_dofs); } old_num_dofs = double(num_dofs); old_error = error; mesh.UniformRefinement(); // Update the space, interpolate the solution. fespace.Update(); a.Update(); b.Update(); x.Update(); } REQUIRE(rate < order/2.0 + 1e-1); REQUIRE(rate > order/2.0 - 1e-1); } TEST_CASE("Least-squares ZZ estimator on 3D NCMesh", "[NCMesh]") { // Setup const auto order = GENERATE(2, 3); Mesh mesh = Mesh::MakeCartesian3D(2, 2, 2, Element::HEXAHEDRON); // Make the mesh NC mesh.EnsureNCMesh(); mesh.RandomRefinement(0.05); H1_FECollection fe_coll(order, mesh.Dimension()); FiniteElementSpace fespace(&mesh, &fe_coll); SECTION("Perfect Approximation X") { FunctionCoefficient u_analytic(testhelper::SmoothSolutionX); GridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); DiffusionIntegrator di; LSZienkiewiczZhuEstimator estimator(di, u_gf); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= 0.0); } REQUIRE(estimator.GetTotalError() > 0.0); } SECTION("Nonsmooth Approximation Y") { FunctionCoefficient u_analytic(testhelper::NonsmoothSolutionY); GridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); DiffusionIntegrator di; LSZienkiewiczZhuEstimator estimator(di, u_gf); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= 0.0); } REQUIRE(estimator.GetTotalError() > 0.0); } SECTION("Nonsmooth Approximation Z") { FunctionCoefficient u_analytic(testhelper::NonsmoothSolutionZ); GridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); DiffusionIntegrator di; LSZienkiewiczZhuEstimator estimator(di, u_gf); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= 0.0); } REQUIRE(estimator.GetTotalError() > 0.0); } } #ifdef MFEM_USE_MPI TEST_CASE("Kelly Error Estimator on 2D NCMesh", "[NCMesh], [Parallel]") { // Setup const auto order = GENERATE(1, 3, 5); Mesh mesh = Mesh::MakeCartesian2D(2, 2, Element::QUADRILATERAL); // Make the mesh NC mesh.EnsureNCMesh(); { Array elements_to_refine(1); elements_to_refine[0] = 1; mesh.GeneralRefinement(elements_to_refine, 1, 0); } auto pmesh = new ParMesh(MPI_COMM_WORLD, mesh); mesh.Clear(); H1_FECollection fe_coll(order, pmesh->Dimension()); ParFiniteElementSpace fespace(pmesh, &fe_coll); SECTION("Perfect Approximation X") { FunctionCoefficient u_analytic(testhelper::SmoothSolutionX); ParGridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); L2_FECollection flux_fec(order, pmesh->Dimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; iDimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; iDimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= 0.0); } REQUIRE(estimator.GetTotalError() > 0.0); } SECTION("Nonsmooth Approximation Y") { FunctionCoefficient u_analytic(testhelper::NonsmoothSolutionY); ParGridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); L2_FECollection flux_fec(order, pmesh->Dimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= MFEM_Approx(0.0)); } REQUIRE(estimator.GetTotalError() > 0.0); } delete pmesh; } TEST_CASE("Kelly Error Estimator on 2D NCMesh embedded in 3D", "[NCMesh], [Parallel]") { // Setup const auto order = GENERATE(1, 3, 5); // Manually construct embedded mesh std::array vertices = { 0.0,0.0,0.0, 0.0,1.0,0.0, 1.0,1.0,0.0, 1.0,0.0,0.0 }; std::array element_indices = { 0,1,2,3 }; std::array element_attributes = { 1 }; std::array boundary_indices = { 0,1, 1,2, 2,3, 3,0 }; std::array boundary_attributes = { 1, 1, 1, 1 }; auto mesh = new Mesh( vertices.data(), 4, element_indices.data(), Geometry::SQUARE, element_attributes.data(), 1, boundary_indices.data(), Geometry::SEGMENT, boundary_attributes.data(), 4, 2, 3 ); mesh->UniformRefinement(); mesh->Finalize(); // Make the mesh NC mesh->EnsureNCMesh(); { Array elements_to_refine(1); elements_to_refine[0] = 1; mesh->GeneralRefinement(elements_to_refine, 1, 0); } auto pmesh = new ParMesh(MPI_COMM_WORLD, *mesh); delete mesh; H1_FECollection fe_coll(order, pmesh->Dimension()); ParFiniteElementSpace fespace(pmesh, &fe_coll); SECTION("Perfect Approximation X") { FunctionCoefficient u_analytic(testhelper::SmoothSolutionX); ParGridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); L2_FECollection flux_fec(order, pmesh->Dimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; iDimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; iDimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= 0.0); } REQUIRE(estimator.GetTotalError() > 0.0); } SECTION("Nonsmooth Approximation Y") { FunctionCoefficient u_analytic(testhelper::NonsmoothSolutionY); ParGridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); L2_FECollection flux_fec(order, pmesh->Dimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= MFEM_Approx(0.0)); } REQUIRE(estimator.GetTotalError() > 0.0); } delete pmesh; } TEST_CASE("Kelly Error Estimator on 3D NCMesh", "[NCMesh], [Parallel]") { // Setup const auto order = GENERATE(1, 3, 5); Mesh mesh = Mesh::MakeCartesian3D(2, 2, 2, Element::HEXAHEDRON); // Make the mesh NC mesh.EnsureNCMesh(); { Array elements_to_refine(1); elements_to_refine[0] = 1; mesh.GeneralRefinement(elements_to_refine, 1, 0); } auto pmesh = new ParMesh(MPI_COMM_WORLD, mesh); mesh.Clear(); H1_FECollection fe_coll(order, pmesh->Dimension()); ParFiniteElementSpace fespace(pmesh, &fe_coll); SECTION("Perfect Approximation X") { FunctionCoefficient u_analytic(testhelper::SmoothSolutionX); ParGridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); L2_FECollection flux_fec(order, pmesh->Dimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; iDimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; iDimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; iDimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= 0.0); } REQUIRE(estimator.GetTotalError() > 0.0); } SECTION("Nonsmooth Approximation Y") { FunctionCoefficient u_analytic(testhelper::NonsmoothSolutionY); ParGridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); L2_FECollection flux_fec(order, pmesh->Dimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= MFEM_Approx(0.0)); } REQUIRE(estimator.GetTotalError() > 0.0); } SECTION("Nonsmooth Approximation Z") { FunctionCoefficient u_analytic(testhelper::NonsmoothSolutionZ); ParGridFunction u_gf(&fespace); u_gf.ProjectCoefficient(u_analytic); L2_FECollection flux_fec(order, pmesh->Dimension()); ParFiniteElementSpace flux_fes(pmesh, &flux_fec, pmesh->SpaceDimension()); DiffusionIntegrator di; KellyErrorEstimator estimator(di, u_gf, flux_fes); auto &local_errors = estimator.GetLocalErrors(); for (int i=0; i= 0.0); } REQUIRE(estimator.GetTotalError() > 0.0); } delete pmesh; } #endif