// 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" using namespace mfem; namespace lin_interp { double f1(const Vector & x) { return 2.345 * x[0]; } double grad_f1(const Vector & x) { return 2.345; } void Grad_f1(const Vector & x, Vector & df) { df.SetSize(1); df[0] = grad_f1(x); } double f2(const Vector & x) { return 2.345 * x[0] + 3.579 * x[1]; } void F2(const Vector & x, Vector & v) { v.SetSize(2); v[0] = 1.234 * x[0] - 2.357 * x[1]; v[1] = 3.572 * x[0] + 4.321 * x[1]; } void Grad_f2(const Vector & x, Vector & df) { df.SetSize(2); df[0] = 2.345; df[1] = 3.579; } double curlF2(const Vector & x) { return 3.572 + 2.357; } void CurlF2(const Vector & x, Vector & v) { v.SetSize(1); v[0] = curlF2(x); } double DivF2(const Vector & x) { return 1.234 + 4.321; } double f3(const Vector & x) { return 2.345 * x[0] + 3.579 * x[1] + 4.680 * x[2]; } void F3(const Vector & x, Vector & v) { v.SetSize(3); v[0] = 1.234 * x[0] - 2.357 * x[1] + 3.572 * x[2]; v[1] = 2.537 * x[0] + 4.321 * x[1] - 1.234 * x[2]; v[2] = -2.572 * x[0] + 1.321 * x[1] + 3.234 * x[2]; } void Grad_f3(const Vector & x, Vector & df) { df.SetSize(3); df[0] = 2.345; df[1] = 3.579; df[2] = 4.680; } void CurlF3(const Vector & x, Vector & df) { df.SetSize(3); df[0] = 1.321 + 1.234; df[1] = 3.572 + 2.572; df[2] = 2.537 + 2.357; } double DivF3(const Vector & x) { return 1.234 + 4.321 + 3.234; } double g1(const Vector & x) { return 4.234 * x[0]; } double g2(const Vector & x) { return 4.234 * x[0] + 3.357 * x[1]; } double g3(const Vector & x) { return 4.234 * x[0] + 3.357 * x[1] + 1.572 * x[2]; } void G2(const Vector & x, Vector & v) { v.SetSize(2); v[0] = 4.234 * x[0] + 3.357 * x[1]; v[1] = 4.537 * x[0] + 1.321 * x[1]; } void G3(const Vector & x, Vector & v) { v.SetSize(3); v[0] = 4.234 * x[0] + 3.357 * x[1] + 1.572 * x[2]; v[1] = 4.537 * x[0] + 1.321 * x[1] + 2.234 * x[2]; v[2] = 1.572 * x[0] + 2.321 * x[1] + 3.234 * x[2]; } double fg1(const Vector & x) { return f1(x) * g1(x); } double fg2(const Vector & x) { return f2(x) * g2(x); } void fG2(const Vector & x, Vector & v) { G2(x, v); v *= f2(x); } void Fg2(const Vector & x, Vector & v) { F2(x, v); v *= g2(x); } void FcrossG2(const Vector & x, Vector & FxG) { Vector F; F2(x, F); Vector G; G2(x, G); FxG.SetSize(1); FxG(0) = F(0) * G(1) - F(1) * G(0); } double FdotG2(const Vector & x) { Vector F; F2(x, F); Vector G; G2(x, G); return F * G; } double fg3(const Vector & x) { return f3(x) * g3(x); } void fG3(const Vector & x, Vector & v) { G3(x, v); v *= f3(x); } void Fg3(const Vector & x, Vector & v) { F3(x, v); v *= g3(x); } void FcrossG3(const Vector & x, Vector & FxG) { Vector F; F3(x, F); Vector G; G3(x, G); FxG.SetSize(3); FxG(0) = F(1) * G(2) - F(2) * G(1); FxG(1) = F(2) * G(0) - F(0) * G(2); FxG(2) = F(0) * G(1) - F(1) * G(0); } double FdotG3(const Vector & x) { Vector F; F3(x, F); Vector G; G3(x, G); return F * G; } TEST_CASE("Identity Linear Interpolators", "[IdentityInterpolator]") { int order_h1 = 1, order_nd = 2, order_rt = 1, order_l2 = 1, n = 3, dim = -1; double tol = 1e-9; for (int type = (int)Element::SEGMENT; type <= (int)Element::WEDGE; type++) { Mesh mesh; if (type < (int)Element::TRIANGLE) { dim = 1; mesh = Mesh::MakeCartesian1D(n, 2.0); } else if (type < (int)Element::TETRAHEDRON) { dim = 2; mesh = Mesh::MakeCartesian2D(n, n, (Element::Type)type, 1, 2.0, 3.0); } else { dim = 3; mesh = Mesh::MakeCartesian3D(n, n, n, (Element::Type)type, 2.0, 3.0, 5.0); } FunctionCoefficient fCoef((dim==1) ? f1 : ((dim==2) ? f2 : f3)); VectorFunctionCoefficient dfCoef(dim, (dim==1) ? Grad_f1 : ((dim==2)? Grad_f2 : Grad_f3)); SECTION("Operators on H1 for element type " + std::to_string(type)) { H1_FECollection fec_h1(order_h1, dim); FiniteElementSpace fespace_h1(&mesh, &fec_h1); GridFunction f0(&fespace_h1); f0.ProjectCoefficient(fCoef); SECTION("Mapping to H1") { H1_FECollection fec_h1p(order_h1+1, dim); FiniteElementSpace fespace_h1p(&mesh, &fec_h1p); GridFunction f0p(&fespace_h1p); DiscreteLinearOperator Op(&fespace_h1,&fespace_h1p); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f0,f0p); REQUIRE( f0p.ComputeH1Error(&fCoef, &dfCoef) < tol ); } SECTION("Mapping to L2") { L2_FECollection fec_l2(order_l2, dim); FiniteElementSpace fespace_l2(&mesh, &fec_l2); GridFunction f1(&fespace_l2); DiscreteLinearOperator Op(&fespace_h1,&fespace_l2); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f0,f1); REQUIRE( f1.ComputeL2Error(fCoef) < tol ); } SECTION("Mapping to L2 (INTEGRAL)") { L2_FECollection fec_l2(order_l2, dim, BasisType::GaussLegendre, FiniteElement::INTEGRAL); FiniteElementSpace fespace_l2(&mesh, &fec_l2); GridFunction f1(&fespace_l2); DiscreteLinearOperator Op(&fespace_h1,&fespace_l2); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f0,f1); REQUIRE( f1.ComputeL2Error(fCoef) < tol ); } } SECTION("Operators on L2 for element type " + std::to_string(type)) { L2_FECollection fec_l2(order_l2, dim); FiniteElementSpace fespace_l2(&mesh, &fec_l2); GridFunction f0(&fespace_l2); f0.ProjectCoefficient(fCoef); SECTION("Mapping to L2") { L2_FECollection fec_l2p(order_l2+1, dim); FiniteElementSpace fespace_l2p(&mesh, &fec_l2p); GridFunction f1(&fespace_l2p); DiscreteLinearOperator Op(&fespace_l2,&fespace_l2p); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f0,f1); REQUIRE( f1.ComputeL2Error(fCoef) < tol ); } SECTION("Mapping to L2 (INTEGRAL)") { L2_FECollection fec_l2p(order_l2+1, dim, BasisType::GaussLegendre, FiniteElement::INTEGRAL); FiniteElementSpace fespace_l2p(&mesh, &fec_l2p); GridFunction f1(&fespace_l2p); DiscreteLinearOperator Op(&fespace_l2,&fespace_l2p); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f0,f1); REQUIRE( f1.ComputeL2Error(fCoef) < tol ); } } SECTION("Operators on L2 (INTEGRAL) for element type " + std::to_string(type)) { L2_FECollection fec_l2(order_l2, dim, BasisType::GaussLegendre, FiniteElement::INTEGRAL); FiniteElementSpace fespace_l2(&mesh, &fec_l2); GridFunction f0(&fespace_l2); f0.ProjectCoefficient(fCoef); SECTION("Mapping to L2") { L2_FECollection fec_l2p(order_l2+1, dim); FiniteElementSpace fespace_l2p(&mesh, &fec_l2p); GridFunction f1(&fespace_l2p); DiscreteLinearOperator Op(&fespace_l2,&fespace_l2p); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f0,f1); REQUIRE( f1.ComputeL2Error(fCoef) < tol ); } SECTION("Mapping to L2 (INTEGRAL)") { L2_FECollection fec_l2p(order_l2+1, dim, BasisType::GaussLegendre, FiniteElement::INTEGRAL); FiniteElementSpace fespace_l2p(&mesh, &fec_l2p); GridFunction f1(&fespace_l2p); DiscreteLinearOperator Op(&fespace_l2,&fespace_l2p); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f0,f1); REQUIRE( f1.ComputeL2Error(fCoef) < tol ); } } if (dim > 1) { VectorFunctionCoefficient FCoef(dim, (dim==2) ? F2 : F3); VectorFunctionCoefficient curlFCoef(dim, (dim==2) ? CurlF2 : CurlF3); FunctionCoefficient divFCoef((dim==2) ? DivF2 : DivF3); SECTION("Operators on HCurl for element type " + std::to_string(type)) { ND_FECollection fec_nd(order_nd, dim); FiniteElementSpace fespace_nd(&mesh, &fec_nd); GridFunction f1(&fespace_nd); f1.ProjectCoefficient(FCoef); SECTION("Mapping to HCurl") { ND_FECollection fec_ndp(order_nd+1, dim); FiniteElementSpace fespace_ndp(&mesh, &fec_ndp); GridFunction f1p(&fespace_ndp); DiscreteLinearOperator Op(&fespace_nd,&fespace_ndp); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f1,f1p); REQUIRE( f1p.ComputeHCurlError(&FCoef, &curlFCoef) < tol ); } SECTION("Mapping to L2^d") { L2_FECollection fec_l2(order_l2, dim); FiniteElementSpace fespace_l2(&mesh, &fec_l2, dim); GridFunction f2d(&fespace_l2); DiscreteLinearOperator Op(&fespace_nd,&fespace_l2); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f1,f2d); REQUIRE( f2d.ComputeL2Error(FCoef) < tol ); } SECTION("Mapping to L2^d (INTEGRAL)") { L2_FECollection fec_l2(order_l2, dim, BasisType::GaussLegendre, FiniteElement::INTEGRAL); FiniteElementSpace fespace_l2(&mesh, &fec_l2, dim); GridFunction f2d(&fespace_l2); DiscreteLinearOperator Op(&fespace_nd,&fespace_l2); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f1,f2d); REQUIRE( f2d.ComputeL2Error(FCoef) < tol ); } } SECTION("Operators on HDiv for element type " + std::to_string(type)) { RT_FECollection fec_rt(order_rt, dim); FiniteElementSpace fespace_rt(&mesh, &fec_rt); GridFunction f2(&fespace_rt); f2.ProjectCoefficient(FCoef); SECTION("Mapping to HDiv") { RT_FECollection fec_rtp(order_rt+1, dim); FiniteElementSpace fespace_rtp(&mesh, &fec_rtp); GridFunction f2p(&fespace_rtp); DiscreteLinearOperator Op(&fespace_rt,&fespace_rtp); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f2,f2p); REQUIRE( f2p.ComputeHDivError(&FCoef, &divFCoef) < tol ); } SECTION("Mapping to L2^d") { L2_FECollection fec_l2(order_l2, dim); FiniteElementSpace fespace_l2(&mesh, &fec_l2, dim); GridFunction f2d(&fespace_l2); DiscreteLinearOperator Op(&fespace_rt,&fespace_l2); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f2,f2d); REQUIRE( f2d.ComputeL2Error(FCoef) < tol ); } SECTION("Mapping to L2^d (INTEGRAL)") { L2_FECollection fec_l2(order_l2, dim, BasisType::GaussLegendre, FiniteElement::INTEGRAL); FiniteElementSpace fespace_l2(&mesh, &fec_l2, dim); GridFunction f2d(&fespace_l2); DiscreteLinearOperator Op(&fespace_rt,&fespace_l2); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f2,f2d); REQUIRE( f2d.ComputeL2Error(FCoef) < tol ); } } SECTION("Operators on H1^d for element type " + std::to_string(type)) { H1_FECollection fec_h1(order_h1, dim); FiniteElementSpace fespace_h1(&mesh, &fec_h1, dim); GridFunction f0(&fespace_h1); f0.ProjectCoefficient(FCoef); SECTION("Mapping to HCurl") { ND_FECollection fec_ndp(order_nd, dim); FiniteElementSpace fespace_ndp(&mesh, &fec_ndp); GridFunction f1(&fespace_ndp); DiscreteLinearOperator Op(&fespace_h1,&fespace_ndp); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f0,f1); REQUIRE( f1.ComputeHCurlError(&FCoef, &curlFCoef) < tol ); } SECTION("Mapping to HDiv") { RT_FECollection fec_rtp(order_rt, dim); FiniteElementSpace fespace_rtp(&mesh, &fec_rtp); GridFunction f2(&fespace_rtp); DiscreteLinearOperator Op(&fespace_h1,&fespace_rtp); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f0,f2); REQUIRE( f2.ComputeHDivError(&FCoef, &divFCoef) < tol ); } } /// The following tests would fail. The reason for the failure would /// not be obvious from the user's point of view. I recommend keeping /// these tests here as a reminder that we should consider supporting /// this, or a very similar, usage. /* SECTION("Mapping to L2^d") { L2_FECollection fec_l2(order_l2, dim); FiniteElementSpace fespace_l2(&mesh, &fec_l2, dim); GridFunction f2d(&fespace_l2); DiscreteLinearOperator Op(&fespace_h1,&fespace_l2); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f0,f2d); REQUIRE( f2d.ComputeL2Error(FCoef) < tol ); } SECTION("Mapping to L2^d (INTEGRAL)") { L2_FECollection fec_l2(order_l2, dim, BasisType::GaussLegendre, FiniteElement::INTEGRAL); FiniteElementSpace fespace_l2(&mesh, &fec_l2, dim); GridFunction f2d(&fespace_l2); DiscreteLinearOperator Op(&fespace_h1,&fespace_l2); Op.AddDomainInterpolator(new IdentityInterpolator()); Op.Assemble(); Op.Mult(f0,f2d); REQUIRE( f2d.ComputeL2Error(FCoef) < tol ); } */ } } } TEST_CASE("Derivative Linear Interpolators", "[GradientInterpolator]" "[CurlInterpolator]" "[DivergenceInterpolator]") { int order_h1 = 1, order_nd = 1, order_rt = 0, order_l2 = 0, n = 3, dim = -1; double tol = 1e-9; for (int type = (int)Element::SEGMENT; type <= (int)Element::WEDGE; type++) { Mesh mesh; if (type < (int)Element::TRIANGLE) { dim = 1; mesh = Mesh::MakeCartesian1D(n, 2.0); } else if (type < (int)Element::TETRAHEDRON) { dim = 2; mesh = Mesh::MakeCartesian2D(n, n, (Element::Type)type, 1, 2.0, 3.0); } else { dim = 3; mesh = Mesh::MakeCartesian3D(n, n, n, (Element::Type)type, 2.0, 3.0, 5.0); } FunctionCoefficient fCoef((dim==1) ? f1 : ((dim==2) ? f2 : f3)); FunctionCoefficient gradfCoef(grad_f1); VectorFunctionCoefficient GradfCoef(dim, (dim==1) ? Grad_f1 : ((dim==2)? Grad_f2 : Grad_f3)); SECTION("Operators on H1 for element type " + std::to_string(type)) { H1_FECollection fec_h1(order_h1, dim); FiniteElementSpace fespace_h1(&mesh, &fec_h1); GridFunction f0(&fespace_h1); f0.ProjectCoefficient(fCoef); if (dim ==1) { SECTION("Mapping to L2") { L2_FECollection fec_l2(order_l2, dim); FiniteElementSpace fespace_l2(&mesh, &fec_l2); GridFunction df0(&fespace_l2); DiscreteLinearOperator Op(&fespace_h1,&fespace_l2); Op.AddDomainInterpolator(new GradientInterpolator()); Op.Assemble(); Op.Mult(f0,df0); REQUIRE( df0.ComputeL2Error(gradfCoef) < tol ); } SECTION("Mapping to L2 (INTEGRAL)") { L2_FECollection fec_l2(order_l2, dim, BasisType::GaussLegendre, FiniteElement::INTEGRAL); FiniteElementSpace fespace_l2(&mesh, &fec_l2); GridFunction df0(&fespace_l2); DiscreteLinearOperator Op(&fespace_h1,&fespace_l2); Op.AddDomainInterpolator(new GradientInterpolator()); Op.Assemble(); Op.Mult(f0,df0); REQUIRE( df0.ComputeL2Error(gradfCoef) < tol ); } } else { SECTION("Mapping to HCurl") { ND_FECollection fec_nd(order_nd, dim); FiniteElementSpace fespace_nd(&mesh, &fec_nd); GridFunction df0(&fespace_nd); DiscreteLinearOperator Op(&fespace_h1,&fespace_nd); Op.AddDomainInterpolator(new GradientInterpolator()); Op.Assemble(); Op.Mult(f0,df0); REQUIRE( df0.ComputeL2Error(GradfCoef) < tol ); } } } if (dim > 1) { VectorFunctionCoefficient FCoef(dim, (dim==2) ? F2 : F3); FunctionCoefficient curlFCoef(curlF2); VectorFunctionCoefficient CurlFCoef(dim, (dim==2) ? CurlF2 : CurlF3); FunctionCoefficient DivFCoef((dim==2) ? DivF2 : DivF3); SECTION("Operators on HCurl for element type " + std::to_string(type)) { ND_FECollection fec_nd(order_nd, dim); FiniteElementSpace fespace_nd(&mesh, &fec_nd); GridFunction F1(&fespace_nd); F1.ProjectCoefficient(FCoef); if (dim == 2) { SECTION("Mapping to L2") { L2_FECollection fec_l2(order_l2, dim); FiniteElementSpace fespace_l2(&mesh, &fec_l2); GridFunction dF1(&fespace_l2); DiscreteLinearOperator Op(&fespace_nd,&fespace_l2); Op.AddDomainInterpolator(new CurlInterpolator()); Op.Assemble(); Op.Mult(F1,dF1); REQUIRE( dF1.ComputeL2Error(curlFCoef) < tol ); } SECTION("Mapping to L2 (INTEGRAL)") { L2_FECollection fec_l2(order_l2, dim, BasisType::GaussLegendre, FiniteElement::INTEGRAL); FiniteElementSpace fespace_l2(&mesh, &fec_l2); GridFunction dF1(&fespace_l2); DiscreteLinearOperator Op(&fespace_nd,&fespace_l2); Op.AddDomainInterpolator(new CurlInterpolator()); Op.Assemble(); Op.Mult(F1,dF1); REQUIRE( dF1.ComputeL2Error(curlFCoef) < tol ); } } else { SECTION("Mapping to HDiv") { RT_FECollection fec_rt(order_rt, dim); FiniteElementSpace fespace_rt(&mesh, &fec_rt); GridFunction dF1(&fespace_rt); DiscreteLinearOperator Op(&fespace_nd,&fespace_rt); Op.AddDomainInterpolator(new CurlInterpolator()); Op.Assemble(); Op.Mult(F1,dF1); REQUIRE( dF1.ComputeL2Error(CurlFCoef) < tol ); } } } SECTION("Operators on HDiv for element type " + std::to_string(type)) { RT_FECollection fec_rt(order_rt, dim); FiniteElementSpace fespace_rt(&mesh, &fec_rt); GridFunction F2(&fespace_rt); F2.ProjectCoefficient(FCoef); SECTION("Mapping to L2") { L2_FECollection fec_l2(order_l2, dim); FiniteElementSpace fespace_l2(&mesh, &fec_l2); GridFunction dF2(&fespace_l2); DiscreteLinearOperator Op(&fespace_rt,&fespace_l2); Op.AddDomainInterpolator(new DivergenceInterpolator()); Op.Assemble(); Op.Mult(F2,dF2); REQUIRE( dF2.ComputeL2Error(DivFCoef) < tol ); } SECTION("Mapping to L2 (INTEGRAL)") { L2_FECollection fec_l2(order_l2, dim, BasisType::GaussLegendre, FiniteElement::INTEGRAL); FiniteElementSpace fespace_l2(&mesh, &fec_l2); GridFunction dF2(&fespace_l2); DiscreteLinearOperator Op(&fespace_rt,&fespace_l2); Op.AddDomainInterpolator(new DivergenceInterpolator()); Op.Assemble(); Op.Mult(F2,dF2); REQUIRE( dF2.ComputeL2Error(DivFCoef) < tol ); } } } } } TEST_CASE("Product Linear Interpolators", "[ScalarProductInterpolator]" "[VectorScalarProductInterpolator]" "[ScalarVectorProductInterpolator]" "[ScalarCrossProductInterpolator]" "[VectorCrossProductInterpolator]" "[VectorInnerProductInterpolator]") { int order_h1 = 1, order_nd = 2, order_rt = 2, n = 3, dim = -1; double tol = 1e-9; for (int type = (int)Element::SEGMENT; type <= (int)Element::WEDGE; type++) { Mesh mesh; if (type < (int)Element::TRIANGLE) { dim = 1; mesh = Mesh::MakeCartesian1D(n, 2.0); } else if (type < (int)Element::TETRAHEDRON) { dim = 2; mesh = Mesh::MakeCartesian2D(n, n, (Element::Type)type, 1, 2.0, 3.0); } else { dim = 3; mesh = Mesh::MakeCartesian3D(n, n, n, (Element::Type)type, 2.0, 3.0, 5.0); } FunctionCoefficient fCoef((dim==1) ? f1 : ((dim==2) ? f2 : f3)); FunctionCoefficient gCoef((dim==1) ? g1 : ((dim==2) ? g2 : g3)); FunctionCoefficient fgCoef((dim==1) ? fg1 : ((dim==2) ? fg2 : fg3)); VectorFunctionCoefficient FCoef(dim, (dim==2) ? F2 : F3); VectorFunctionCoefficient GCoef(dim, (dim==2) ? G2 : G3); FunctionCoefficient FGCoef((dim==2) ? FdotG2 : FdotG3); VectorFunctionCoefficient fGCoef(dim, (dim==2) ? fG2 : fG3); VectorFunctionCoefficient FgCoef(dim, (dim==2) ? Fg2 : Fg3); VectorFunctionCoefficient FxGCoef(dim, (dim==2) ? FcrossG2 : FcrossG3); SECTION("Operators on H1 for element type " + std::to_string(type)) { H1_FECollection fec_h1(order_h1, dim); FiniteElementSpace fespace_h1(&mesh, &fec_h1); GridFunction g0(&fespace_h1); g0.ProjectCoefficient(gCoef); SECTION("Mapping H1 to H1") { H1_FECollection fec_h1p(2*order_h1, dim); FiniteElementSpace fespace_h1p(&mesh, &fec_h1p); DiscreteLinearOperator Opf0(&fespace_h1,&fespace_h1p); Opf0.AddDomainInterpolator( new ScalarProductInterpolator(fCoef)); Opf0.Assemble(); GridFunction fg0(&fespace_h1p); Opf0.Mult(g0,fg0); REQUIRE( fg0.ComputeL2Error(fgCoef) < tol ); } if (dim > 1) { SECTION("Mapping to HCurl") { ND_FECollection fec_nd(order_nd, dim); FiniteElementSpace fespace_nd(&mesh, &fec_nd); ND_FECollection fec_ndp(order_h1+order_nd, dim); FiniteElementSpace fespace_ndp(&mesh, &fec_ndp); DiscreteLinearOperator OpF1(&fespace_h1,&fespace_ndp); OpF1.AddDomainInterpolator( new VectorScalarProductInterpolator(FCoef)); OpF1.Assemble(); GridFunction Fg1(&fespace_ndp); OpF1.Mult(g0,Fg1); REQUIRE( Fg1.ComputeL2Error(FgCoef) < tol ); } } } if (dim > 1) { SECTION("Operators on HCurl for element type " + std::to_string(type)) { ND_FECollection fec_nd(order_nd, dim); FiniteElementSpace fespace_nd(&mesh, &fec_nd); GridFunction G1(&fespace_nd); G1.ProjectCoefficient(GCoef); SECTION("Mapping HCurl to HCurl") { H1_FECollection fec_h1(order_h1, dim); FiniteElementSpace fespace_h1(&mesh, &fec_h1); ND_FECollection fec_ndp(order_nd+order_h1, dim); FiniteElementSpace fespace_ndp(&mesh, &fec_ndp); DiscreteLinearOperator Opf0(&fespace_nd,&fespace_ndp); Opf0.AddDomainInterpolator( new ScalarVectorProductInterpolator(fCoef)); Opf0.Assemble(); GridFunction fG1(&fespace_ndp); Opf0.Mult(G1,fG1); REQUIRE( fG1.ComputeL2Error(fGCoef) < tol ); } if (dim == 2) { SECTION("Mapping to L2") { L2_FECollection fec_l2p(2*order_nd-1, dim); FiniteElementSpace fespace_l2p(&mesh, &fec_l2p); DiscreteLinearOperator OpF1(&fespace_nd,&fespace_l2p); OpF1.AddDomainInterpolator( new ScalarCrossProductInterpolator(FCoef)); OpF1.Assemble(); GridFunction FxG2(&fespace_l2p); OpF1.Mult(G1,FxG2); REQUIRE( FxG2.ComputeL2Error(FxGCoef) < tol ); } } else { SECTION("Mapping to HDiv") { RT_FECollection fec_rtp(2*order_nd-1, dim); FiniteElementSpace fespace_rtp(&mesh, &fec_rtp); DiscreteLinearOperator OpF1(&fespace_nd,&fespace_rtp); OpF1.AddDomainInterpolator( new VectorCrossProductInterpolator(FCoef)); OpF1.Assemble(); GridFunction FxG2(&fespace_rtp); OpF1.Mult(G1,FxG2); REQUIRE( FxG2.ComputeL2Error(FxGCoef) < tol ); } } SECTION("Mapping to L2") { RT_FECollection fec_rt(order_rt, dim); FiniteElementSpace fespace_rt(&mesh, &fec_rt); L2_FECollection fec_l2p(order_nd+order_rt, dim); FiniteElementSpace fespace_l2p(&mesh, &fec_l2p); DiscreteLinearOperator OpF2(&fespace_nd,&fespace_l2p); OpF2.AddDomainInterpolator( new VectorInnerProductInterpolator(FCoef)); OpF2.Assemble(); GridFunction FG3(&fespace_l2p); OpF2.Mult(G1,FG3); REQUIRE( FG3.ComputeL2Error(FGCoef) < tol ); } } SECTION("Operators on HDiv for element type " + std::to_string(type)) { RT_FECollection fec_rt(order_rt, dim); FiniteElementSpace fespace_rt(&mesh, &fec_rt); GridFunction G2(&fespace_rt); G2.ProjectCoefficient(GCoef); SECTION("Mapping to L2") { ND_FECollection fec_nd(order_nd, dim); FiniteElementSpace fespace_nd(&mesh, &fec_nd); L2_FECollection fec_l2p(order_nd+order_rt, dim); FiniteElementSpace fespace_l2p(&mesh, &fec_l2p); DiscreteLinearOperator OpF1(&fespace_rt,&fespace_l2p); OpF1.AddDomainInterpolator( new VectorInnerProductInterpolator(FCoef)); OpF1.Assemble(); GridFunction FG3(&fespace_l2p); OpF1.Mult(G2,FG3); REQUIRE( FG3.ComputeL2Error(FGCoef) < tol ); } } } } } } // namespace lin_interp