# This file is part of the mlhp project. License: See LICENSE

import mlhp
import unittest

def compute( D, refinementDepth ):

    analytical = mlhp.makeSingularSolution( D ).solution

    # Domain and discretization
    nelements = [4] * D
    lengths = [1.0] * D

    sphere = mlhp.implicitSphere( [0.0] * D, 0.0 )
    strategy = mlhp.refineTowardsBoundary( sphere, refinementDepth )

    grid = mlhp.makeRefinedGrid( nelements, lengths )
    grid.refine( strategy )

    basis = mlhp.makeHpTrunkSpace( grid, mlhp.LinearGrading( 1 ) )

    # Compute indices and values for dirichlet boundary condition
    dofs = mlhp.projectOnto( basis, analytical )
    
    # Vtk Postprocessing
    processors = [mlhp.solutionProcessor( D, dofs ),
                  mlhp.functionProcessor( analytical, "Analytical" )]

    postmesh = mlhp.gridOnCells( [refinementDepth + 1] * D )
    writer = mlhp.VtuOutput( f'outputs/singular_L2_{D}D_{refinementDepth}' )
            
    mlhp.writeBasisOutput( basis, postmesh, writer, processors )
        
    # Error computation
    l2ErrorIntegrand = mlhp.l2ErrorIntegrand( dofs, analytical );
    l2Integrals = mlhp.makeScalars( 3 )
    
    mlhp.integrateOnDomain( basis, l2ErrorIntegrand, l2Integrals )
                               
    # Return number of dofs and || u_fe - u_ex ||^2 
    return [ len( dofs ), l2Integrals[2].get( ) ]
    
class SingularL2Test ( unittest.TestCase ):
    
    def computeStudy( self, D, expected, tol ):
        expectedNDofs, expectedL2 = expected
        
        for iStudy, expectedNDof in enumerate( expectedNDofs ):
            computedNDof, computedL2 = compute( D, iStudy )
            
            self.assertEqual(computedNDof, expectedNDof)
            self.assertAlmostEqual(computedL2, expectedL2[iStudy], delta=tol)
        
    def test_1D(self):
    
        expectedNDof = [5, 9, 14, 20, 27]
        l2 = [3.08705e-06, 6.21486e-07, 1.25989e-07, 2.55838e-08, 5.19513e-09]

        self.computeStudy( 1, [expectedNDof, l2], 1e-10 )
             
    def test_2D(self):
    
        expectedNDof = [25, 66, 113, 181, 273]
        l2 = [1.42219e-06, 1.2541e-06, 1.67571e-07, 2.11436e-08, 2.68362e-09]

        self.computeStudy( 2, [expectedNDof, l2], 1e-10 )
             
    def test_3D(self):
    
        expectedNDof = [125, 429, 754, 1337]
        l2 = [7.35596e-07, 2.28911e-07, 1.66542e-08, 1.27925e-09]

        self.computeStudy( 3, [expectedNDof, l2], 1e-10 )
     
