2

i'm currently incredibly stuck on what isn't working in my code and have been staring at it for hours. I have created some functions to approximate the solution to the laplace equation adaptively using the finite element method then estimate it's error using the dual weighted residual. The error function should give a vector of errors (one error for each element), i then choose the biggest errors, add more elements around them, solve again and then recheck the error; however i have no idea why my error estimate isn't changing!

My first 4 functions are correct but i will include them incase someone wants to try the code:

def Poisson_Stiffness(x0):
    """Finds the Poisson equation stiffness matrix with any non uniform mesh x0"""

    x0 = np.array(x0)
    N = len(x0) - 1 # The amount of elements; x0, x1, ..., xN

    h = x0[1:] - x0[:-1]

    a = np.zeros(N+1)
    a[0] = 1 #BOUNDARY CONDITIONS
    a[1:-1] = 1/h[1:] + 1/h[:-1]
    a[-1] = 1/h[-1]
    a[N] = 1 #BOUNDARY CONDITIONS

    b = -1/h
    b[0] = 0 #BOUNDARY CONDITIONS

    c = -1/h
    c[N-1] = 0 #BOUNDARY CONDITIONS: DIRICHLET

    data = [a.tolist(), b.tolist(), c.tolist()]
    Positions = [0, 1, -1]
    Stiffness_Matrix = diags(data, Positions, (N+1,N+1))

    return Stiffness_Matrix

def NodalQuadrature(x0):
    """Finds the Nodal Quadrature Approximation of sin(pi x)"""

    x0 = np.array(x0)
    h = x0[1:] - x0[:-1]
    N = len(x0) - 1

    approx = np.zeros(len(x0))
    approx[0] = 0 #BOUNDARY CONDITIONS

    for i in range(1,N):
        approx[i] = math.sin(math.pi*x0[i])
        approx[i] = (approx[i]*h[i-1] + approx[i]*h[i])/2

    approx[N] = 0 #BOUNDARY CONDITIONS

    return approx

def Solver(x0):

    Stiff_Matrix = Poisson_Stiffness(x0)

    NodalApproximation = NodalQuadrature(x0)
    NodalApproximation[0] = 0

    U = scipy.sparse.linalg.spsolve(Stiff_Matrix, NodalApproximation)

    return U

def Dualsolution(rich_mesh,qoi_rich_node): #BOUNDARY CONDITIONS?
    """Find Z from stiffness matrix Z = K^-1 Q over richer mesh"""

    K = Poisson_Stiffness(rich_mesh)
    Q = np.zeros(len(rich_mesh))
    Q[qoi_rich_node] = 1.0

    Z = scipy.sparse.linalg.spsolve(K,Q)
    return Z

My error indicator function takes in an approximation Uh, with the mesh it is solved over, and finds eta = (f - Bu)z.

def Error_Indicators(Uh,U_mesh,Z,Z_mesh,f):
    """Take in U, Interpolate to same mesh as Z then solve for eta vector"""

    u_inter = interp1d(U_mesh,Uh) #Interpolation of old mesh
    U2 = u_inter(Z_mesh) #New function u for the new mesh to use in 

    Bz = Poisson_Stiffness(Z_mesh)
    Bz = Bz.tocsr()

    eta = np.empty(len(Z_mesh))
    for i in range(len(Z_mesh)):
        for j in range(len(Z_mesh)):
            eta[i] += (f[i] - Bz[i,j]*U2[j])

    for i in range(len(Z)):
        eta[i] = eta[i]*Z[i]

    return eta

My next function seems to adapt the mesh very well to the given error indicator! Just no idea why the indicator seems to stay the same regardless?

def Mesh_Refinement(base_mesh,tolerance,refinement,z_mesh,QOI_z_mesh):
    """Solve for U on a normal mesh, Take in Z, Find error indicators, adapt. OUTPUT NEW MESH"""
    New_mesh = base_mesh
    Z = Dualsolution(z_mesh,QOI_z_mesh) #Solve dual solution only once

    f = np.empty(len(z_mesh))
    for i in range(len(z_mesh)):
        f[i] = math.sin(math.pi*z_mesh[i])

    U = Solver(New_mesh)
    eta = Error_Indicators(U,base_mesh,Z,z_mesh,f) 

    while max(abs(k) for k in eta) > tolerance:

        orderedeta = np.sort(eta) #Sort error indicators LENGTH 40
        biggest = np.flipud(orderedeta[int((1-refinement)*len(eta)):len(eta)]) 
        position = np.empty(len(biggest))
        ratio = float(len(New_mesh))/float(len(z_mesh))

        for i in range(len(biggest)):
            position[i] = eta.tolist().index(biggest[i])*ratio #GIVES WHAT NUMBER NODE TO REFINE

        refine = np.zeros(len(position))
        for i in range(len(position)):
            refine[i] = math.floor(position[i])+0.5 #AT WHAT NODE TO PUT NEW ELEMENT 5.5 ETC
        refine = np.flipud(sorted(set(refine)))

        for i in range(len(refine)):
            New_mesh = np.insert(New_mesh,refine[i]+0.5,(New_mesh[refine[i]+0.5]+New_mesh[refine[i]-0.5])/2)

        U = Solver(New_mesh)
        eta = Error_Indicators(U,New_mesh,Z,z_mesh,f)
        print eta

An example input for this would be: Mesh_Refinement(np.linspace(0,1,3),0.1,0.2,np.linspace(0,1,60),20)

I understand there is alot of code here but i am at a loss, i have no idea where to turn!

James
  • 395
  • 2
  • 8
  • 16
  • What's the `Traceback`? – x otikoruk x Sep 04 '15 at 03:21
  • The mesh will just continue to get bigger and bigger and more accurate each time until there is a division by zero due to the nodes being SO close to each other. Yet the eta error estimate seems to remain the same throughout – James Sep 04 '15 at 03:26
  • 1
    Something looks funny about the nested loops in Error_indicators. Every time you step through the inner loop you assign to eta[i], but that over-writes the previous value. – Paul Cornelius Sep 04 '15 at 04:30
  • Thank you i have now fixed this but the same problem still occurs. Only about 2 or 3 of the 50 error indicators seem to ever change! – James Sep 04 '15 at 05:27

1 Answers1

2

Please consider this piece of code from def Error_Indicators:

eta = np.empty(len(Z_mesh))
for i in range(len(Z_mesh)):
    for j in range(len(Z_mesh)):
        eta[i] = (f[i] - Bz[i,j]*U2[j])

Here you override eta[i] each j iteration, so the inner cycle proves useless and you can go directly to the last possible j. Did you mean to find a sum of the (f[i] - Bz[i,j]*U2[j]) series?

eta = np.empty(len(Z_mesh))
for i in range(len(Z_mesh)):
    for j in range(len(Z_mesh)):
        eta[i] += (f[i] - Bz[i,j]*U2[j])
u354356007
  • 3,205
  • 15
  • 25
  • Hi, indeed this is definitely wrong! I meant to take away Bz[i,j]U2[j] for each j. I tried f[i] - np.dot(Bz[i,:],U2[:]) but it didnt work? Will your suggestion fix this? – James Sep 04 '15 at 04:58
  • I have entered your correction now which fixes this part of the code yet the same problem still occurs – James Sep 04 '15 at 05:19
  • @malonej Just in case, my corrections is just a blind suggestion. I'm not familiar with mesh algorithms. However, the original version is obviously wrong. – u354356007 Sep 04 '15 at 06:08
  • @malonej just in case `eta[i] += (f[i] - Bz[i,j]*U2[j])` plus inner loop is not the same as `f[i] - dot_product(Bz, U2)` – u354356007 Sep 04 '15 at 06:12
  • Thanks for the help, I did it as a separate sum to be sure :) No idea what is causing this i was so happy when i thought you found it haha – James Sep 04 '15 at 06:28