// This code projects an expression on a finite element basis, i.e. it computes 
// the best approximation of the expression for the requested interpolation order.
// The relative L2 norm of the error is output so that one can investigate 
// the impact of the interpolation order and the mesh size on the error.
//
// The theory of finite elements states that for a fine enough mesh 
// (i.e. a large enough 'n' in the quad.geo file) the error should decrease 
// at a rate n^(p+1) where p is the interpolation order. 
// All standard shape functions in sparselizard follow this asymptotic decrease rate.


#include "sparselizardbase.h"


using namespace mathop;

void sparselizard(void)
{	
    
    // CHANGE THE INTERPOLATION ORDER HERE TO SEE THE IMPACT ON THE ERROR
    // Recommended order range is 1 to 6 here. Note that when 
    // you reach machine precision the error stops decreasing.
    int interpolationorder = 5;
	

    // The domain region as defined in 'quad.geo':
    int face = 1;
    
    mesh mymesh("quad.msh");
    
    // Nodal shape functions 'h1' for field v.
    // Fields x and y are the x and y coordinate fields:
    field v("h1"), x("x"), y("y");

    // Use the requested interpolation order on the whole domain:
    v.setorder(face, interpolationorder);
    

    // A finite element approximation of this expression is computed:
    expression toproject = sin(10*x)*sin(13*y);
    
    // This formulation computes integral( v*v' ) = integral( toproject*v' ) : 
    formulation projection;
    
    // A last int argument is added. The 20 means the term below will be
    // assembled using an integration order +20 compared to the default
    // order (which is order of the dof + order of the tf + 2). 
    projection += integral(face, dof(v)*tf(v) - toproject*tf(v), +20 );
    
    projection.generate();

    vec solv = solve(projection.A(), projection.b());

    // Transfer the data from the solution vector to the v field:
    v.setdata(face, solv);
    
    // Compute the relative L2 norm of the error, i.e. the square root of 
    // the integral of the error squared divided by the expression squared.
    // The integration is exact for up to order 20 polynomials.
    double relativel2normoferror = sqrt( pow( v - toproject , 2).integrate(face, 20) / pow( toproject , 2).integrate(face, 20) );
    
    std::cout << std::endl << "Relative L2 norm of the error is " << relativel2normoferror << std::endl << std::endl;
    
    // Write with an order 4 interpolation:
    v.write(face, "v.pos", 4);
    
    // Code validation line. Can be removed.
    std::cout << (relativel2normoferror < 1e-5);
}

int main(void)
{	
    SlepcInitialize(0,{},0,0);

    sparselizard();

    SlepcFinalize();

    return 0;
}

