Reconstruction#

import dolfin as df
import math
import matplotlib.pyplot as plt
import numpy as np
import os

from elasticity_problem import elasticity_problem
from noiseless_data_case_rec import noiseless_data_rec

%load_ext autoreload
%autoreload 2

True parameter mu#

# mu_1
class Mu1Expression(df.UserExpression):
    def eval(self, value, x):
        value[0]=0
        if (x[0] - 0.5) *(x[0] - 0.5)  + (x[1] - 0.5) *(x[1] - 0.5)  <= 0.2  *0.2 :
            value[0]= 1
        elif  0.2  *0.2 <= (x[0] - 0.5) *(x[0] - 0.5)  + (x[1] - 0.5) *(x[1] - 0.5)  <= 0.3  *0.3 :
            value[0]= (1-(math.sqrt((x[0] - 0.5) *(x[0] - 0.5)  + (x[1] - 0.5) *(x[1] - 0.5)) - 0.2)/0.1 )**2*(1+2*(math.sqrt((x[0] - 0.5)*(x[0] - 0.5)  + (x[1] - 0.5) *(x[1] - 0.5))-0.2) /0.1) 
        value[0] = 1+value[0]

# mu_2
class Mu2Expression(df.UserExpression):
    def eval(self, value, x):
        value[0]=0
        if (x[0] - 0.2) *(x[0] - 0.2)  + (x[1] - 0.8) *(x[1] - 0.8)  <= 0.1  *0.1 :
            value[0]= 1
        elif  0.1  *0.1 <= (x[0] - 0.2) *(x[0] - 0.2)  + (x[1] - 0.8) *(x[1] - 0.8)  <= 0.15  *0.15 :
            value[0]= (1-(math.sqrt((x[0] - 0.2) *(x[0] - 0.2)  + (x[1] - 0.8) *(x[1] - 0.8)) - 0.1)/0.05 )**2*(1+2*(math.sqrt((x[0] - 0.2)*(x[0] - 0.2)  + (x[1] - 0.8) *(x[1] - 0.8))-0.1) /0.05) 
        elif (x[0] - 0.8) *(x[0] - 0.8)  + (x[1] - 0.2) *(x[1] - 0.2)  <= 0.1  *0.1 :
            value[0]= 2
        elif  0.1  *0.1 <= (x[0] - 0.8) *(x[0] - 0.8)  + (x[1] - 0.2) *(x[1] - 0.2)  <= 0.15  *0.15 :
            value[0]=2*(1-(math.sqrt((x[0] - 0.8) *(x[0] - 0.8)  + (x[1] - 0.2) *(x[1] - 0.2)) - 0.1)/0.05 )**2*(1+2*(math.sqrt((x[0] - 0.8)*(x[0] - 0.8)  + (x[1] - 0.2) *(x[1] - 0.2))-0.1) /0.05)     
        value[0] = 1+value[0]

# mu_3
class Mu3Expression(df.UserExpression):
    def eval(self, value, x):
        value[0]=1
        if (x[0] - 0.5) *(x[0] - 0.5)  + (x[1] - 0.5) *(x[1] - 0.5)  <= 0.4   *0.4 :
            value[0]= 2
        elif  0.4  *0.4 <= (x[0] - 0.5) *(x[0] - 0.5)  + (x[1] - 0.5) *(x[1] - 0.5)  <= 0.45  *0.45:
            value[0]= 1+(1-(math.sqrt((x[0] - 0.5) *(x[0] - 0.5)  + (x[1] - 0.5) *(x[1] - 0.5)) - 0.4)/0.05 )**2*(1+2*(math.sqrt((x[0] - 0.5)*(x[0] - 0.5)  + (x[1] - 0.5) *(x[1] - 0.5))-0.4) /0.05) 

        if (x[0] - 0.35) *(x[0] - 0.35)  + (x[1] - 0.65) *(x[1] - 0.65)  <= 0.1   *0.1 :
            value[0]= 3
        elif  0.1  *0.1<= (x[0] - 0.35) *(x[0] - 0.35)  + (x[1] - 0.65) *(x[1] - 0.65)  <= 0.15  *0.15:
            value[0]= 2+(1-(math.sqrt((x[0] - 0.35) *(x[0] - 0.35)  + (x[1] - 0.65) *(x[1] - 0.65)) - 0.1)/0.05 )**2*(1+2*(math.sqrt((x[0] - 0.35)*(x[0] - 0.35)  + (x[1] - 0.65) *(x[1] - 0.65))-0.1) /0.05) 

        if (x[0] - 0.5) *(x[0] - 0.5)  + (x[1] - 0.3) *(x[1] - 0.3)  <= 0.1   *0.1 :
            value[0]= 4  
        elif  0.1  *0.1<= (x[0] - 0.5) *(x[0] - 0.5)  + (x[1] - 0.3) *(x[1] - 0.3) <= 0.15  *0.15:
           value[0]= 2+2*(1-(math.sqrt((x[0] - 0.5) *(x[0] - 0.5)  + (x[1] - 0.3) *(x[1] - 0.3)) - 0.1)/0.05 )**2*(1+2*(math.sqrt((x[0] - 0.5) *(x[0] - 0.5)  + (x[1] - 0.3) *(x[1] - 0.3))-0.1) /0.05)            

        if (x[0] - 0.65) *(x[0] - 0.65)  + (x[1] - 0.65) *(x[1] - 0.65)  <= 0.1   *0.1 :
            value[0]= 3.5       
        elif  0.1  *0.1<= (x[0] - 0.65) *(x[0] - 0.65)  + (x[1] - 0.65) *(x[1] - 0.65)   <= 0.15  *0.15:
            value[0]= 2+1.5*(1-(math.sqrt((x[0] - 0.65) *(x[0] - 0.65)  + (x[1] - 0.65) *(x[1] - 0.65) ) - 0.1)/0.05 )**2*(1+2*(math.sqrt((x[0] - 0.65) *(x[0] - 0.65)  + (x[1] - 0.65) *(x[1] - 0.65) )-0.1) /0.05)     
        value[0] = value[0]

# mu = Mu1Expression()
mu = Mu2Expression()
# mu = Mu3Expression()

Problem parameters#

omega = 1
rho = 1
degree = 3
dim = 2
Nx = 200
Ny = 200
u_boundary = df.Expression(('x[1]', 0), degree=1)
f = df.Constant((0,0))
tol = 1e-15

mesh_params = {"Nx":Nx, "Ny":Ny, "degree":degree}
load_params = {"u_boundary":u_boundary, "f":f} 
mat_params  = {"mu":mu, "rho":rho, "omega":omega}

Direct problem#

u, eps_12, mu_function = elasticity_problem(
    mesh_params=mesh_params,
    mat_params=mat_params,
    load_params=load_params)

Figures 1 and 2#

#Plot u 
u1, u2 = u.split()
plt.subplot(2,2,1)
p = df.plot(u1, title="u_x", cmap="inferno")
plt.colorbar(p) 

plt.subplot(2,2,2)
p = df.plot(u2, title="u_y", cmap="inferno")
plt.colorbar(p) 

plt.subplot(2,2,3)
p = df.plot(mu_function, title="mu", cmap="inferno")
plt.colorbar(p) 


plt.subplot(2,2,4)
p = df.plot(eps_12, title="$eps_12$", cmap="inferno")
plt.colorbar(p) 

Inverse Problem - Noiseless data - Figure 3b#

mu_rec, err, prim = noiseless_data_rec(
    u,
    mesh_params=mesh_params,
    mat_params=mat_params,
    load_params=load_params)

plt.subplot(2,2,3)
p = df.plot(mu_rec, title="mu_rec", cmap="inferno", vmin=1.0, vmax=3.0)
plt.colorbar(p) 

Inverse Problem - noiseless data - Figure 4#

Nx_coarse_list = [200, 100, 50, 40, 25, 20]
l = len(Nx_coarse_list)

Error_list = np.zeros(l)
h_list = np.zeros(l)

for s in range(l):
    Nx_coarse = Nx_coarse_list[s]
    k_0 = Nx/Nx_coarse
    mesh_coarse = df.UnitSquareMesh(Nx_coarse, Nx_coarse)
    dx_coarse = df.Measure("dx", domain=mesh_coarse)
    degree_coarse = degree
    V1_coarse = df.VectorFunctionSpace(mesh_coarse, 'CG', degree_coarse)
    V_S_coarse = df.FunctionSpace(mesh_coarse, 'CG', degree_coarse-1)  
    V2_coarse = df.FunctionSpace(mesh_coarse, 'CG', degree_coarse-1)  

    u_coarse = df.project(u, V1_coarse)
    S_coarse = (df.nabla_grad(u_coarse) + df.nabla_grad(u_coarse).T)/2
    S_11_coarse = S_coarse[0,0]
    S_22_coarse = S_coarse[1,1]
    S_12_coarse = S_coarse[0,1]

    V_rot_coarse = df.VectorFunctionSpace(mesh_coarse, 'CG', degree_coarse-1)
    V_S1_coarse = df.FunctionSpace(mesh_coarse, 'CG', degree_coarse-1) 
    mu1 = df.TrialFunctions(V_rot_coarse)
    nu1 = df.TestFunctions(V_rot_coarse)

    # Normal equation
    a1 = df.dot(df.grad(mu1[0]*(S_11_coarse-S_22_coarse))[0] + df.grad(mu1[0]*S_12_coarse)[1] - df.grad(mu1[1]*S_12_coarse)[0], df.grad(nu1[0]*(S_11_coarse-S_22_coarse))[0] + df.grad(nu1[0]*S_12_coarse)[1] - df.grad(nu1[1]*S_12_coarse)[0]) * dx_coarse + df.dot(df.grad(mu1[1]*S_12_coarse)[1] - df.grad(mu1[0]*S_12_coarse)[0], df.grad(nu1[1]*S_12_coarse)[1] - df.grad(nu1[0]*S_12_coarse)[0]) * dx_coarse
    l1 = df.dot(prim/2, df.grad(nu1[0]*(S_11_coarse-S_22_coarse))[0] + df.grad(nu1[0]*S_12_coarse)[1] - df.grad(nu1[1]*S_12_coarse)[0]) * dx_coarse

    def custom_boundary1(x, on_boundary):
        return on_boundary and (x[0]<tol or x[0]>1-tol or x[1]<tol)
    def custom_boundary2(x, on_boundary):
        return on_boundary  and x[1]<tol 
    bc_mu1 = df.DirichletBC(V_rot_coarse.sub(0), df.Constant(0), custom_boundary1) 
    bc_mu2 = df.DirichletBC(V_rot_coarse.sub(1), df.Constant(0), custom_boundary2)
    bc = [bc_mu1, bc_mu2]

    mu1 = df.Function(V_rot_coarse)
    df.solve(a1 == l1, mu1, bc, solver_parameters={'linear_solver':'mumps'})
    mu_function_rec_coarse = df.project(mu1[0]+1, V_S1_coarse)  
    mu_function_coarse = df.project(mu_function, V_S1_coarse)
    norm1_l2 = df.sqrt(df.assemble((mu_function_coarse - mu_function_rec_coarse)**2 * dx_coarse)) / df.sqrt(df.assemble(mu_function_coarse**2 * dx_coarse))
    h_list[s] = 1/Nx_coarse
    Error_list[s] = norm1_l2
    print('For noiseless data, the L2-norm for the reconstruction:', norm1_l2)
    
print("h_list =", h_list)    
print("Error_list =", Error_list) 
# Calcul convergence entre points consécutifs
slope = np.log(Error_list[:-1] / Error_list[1:]) / np.log(h_list[:-1] / h_list[1:])
print("Convergence orders between points:", slope)

# Régression linéaire en log-log
log_h = np.log(h_list)
log_err = np.log(Error_list)

# Ajustement linéaire : log(Error) ≈ a*log(h) + b
coeffs = np.polyfit(log_h, log_err, 1)
a, b = coeffs
print(f"Régression linéaire: slope = {a:.2f}, intercept = {b:.2f}")

# Erreur prédite par la régression
fit_err = np.exp(a*log_h + b)

# Plot
fig, ax = plt.subplots()

# Courbe des erreurs
ax.plot(h_list, Error_list, "-sr", label="Relative error")

# Régression linéaire (log-log)
ax.plot(h_list, fit_err, "--g", label=f"Fit slope {a:.2f}")

# Références pente 1 et 2
ref_error_2 = 1e3 * h_list**2
ax.plot(h_list, ref_error_2, "--k", label="Slope 2 ($h^2$)")

ref_error_1 = 1 * h_list
ax.plot(h_list, ref_error_1, "--b", label="Slope 1 ($h$)")

# Échelles log
ax.set_xscale("log")
ax.set_yscale("log")

# Mise en forme
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.set_xlabel('Mesh size (h)')
ax.set_ylabel('Error')
ax.legend()
plt.show()

Inverse Problem - noisy data#

ureal = u

Noise_list = np.array([1e-7, 1e-6])
Nx_coarse_list = [50] #[200, 100, 50, 40, 25, 20, 10]
l = len(Nx_coarse_list)
tol = 1e-15

k_list = []
size_noise = np.size(Noise_list)
Error_list_with_projection = np.zeros((l, size_noise))
Error_list_without_projection = np.zeros(size_noise)
h = ureal.vector().get_local()
a = h.shape 
V = u.function_space()
mesh = u.function_space().mesh()
dx = df.Measure("dx", domain=mesh)

Inverse Problem - noisy data - Figure 3#

for k in range(size_noise) :
    print("Noise = ", Noise_list[k])
    noise_Expression_2 = Function(V)

    for i in range(a[0]) :
        if k==1 :
           noise_Expression_2.vector()[i] = np.random.normal(0,Noise_list[k])
        else :
            noise_Expression_2.vector()[i] = np.random.normal(Noise_list[k-1],Noise_list[k])
    end 
    print( noise_Expression_2.vector())
 
    u_noised = Function(V)
    u_noised.vector()[:] = ureal.vector()[:] + noise_Expression_2.vector()[:] 


    #Without Projection
    mu_rec, err, prim= noiseless_data_rec( u_noised, 
                        mesh_params= mesh_params,
                        mat_params=mat_params,
                        load_params=load_params)
    Error_list_without_projection[k]= err
 
    plt.figure(figsize=(5,4))
    p = plot(mu_rec, title=f"mu_rec (no reg) — noise={Noise_list[k]}", cmap="inferno", vmin=1.0, vmax=3.0)
    plt.colorbar(p)
    plt.tight_layout()
    plt.show(block=False)
    plt.pause(0.5) 


    #With regularization
    for s in range(l) :
        Nx_coarse = Nx_coarse_list[s]
        k_0=Nx/Nx_coarse
        k_list.append(k_0)
        print("k_0= h_0/h_1=", k_0)
        mesh_coarse = UnitSquareMesh(Nx_coarse, Nx_coarse)
        dx_coarse = Measure("dx", domain=mesh_coarse)
        degree_coarse = degree
        V1_coarse = VectorFunctionSpace(mesh_coarse, 'DG', degree_coarse)
        V_tensor_coarse = TensorFunctionSpace(mesh_coarse, 'DG', degree=degree_coarse-1, shape=(dim, dim))
        
        u=TrialFunction(V1_coarse)
        v=TestFunction(V1_coarse)
        a_proj = dot(u,v)*dx_coarse 
        l_proj = dot(u_noised,v)*dx_coarse 
        u_proj= Function(V1_coarse)
        solve(a_proj== l_proj, u_proj,  solver_parameters={'linear_solver': 'mumps'})
        
        V1_DG= VectorFunctionSpace(mesh,  'DG',degree_coarse)
        epsilon_udelta =  0.5*( nabla_grad(interpolate(u_proj,V1_DG)) + nabla_grad(interpolate(u_proj,V1_DG) ).T)
        
        S_11delta = epsilon_udelta [0, 0]
        S_22delta = epsilon_udelta [1, 1]
        S_12delta = epsilon_udelta [0, 1]

        V_mu= mu_function.function_space()
        V_S = FunctionSpace(mesh, 'CG',degree)  

        #By taking into account that the components of eps are in Wˆ{1,\infty}
        S_11delta_cont = project(S_11delta,V_mu )
        S_22delta_cont = project(S_22delta, V_mu )
        S_12delta_cont = project(S_12delta, V_mu ) 

        f1=f[0]
        f2=f[1]

        u1= interpolate(u_proj,V1_DG)[0]
        u2= interpolate(u_proj,V1_DG)[1]

        #Compute the antiderivative
        F=- rho * omega**2* (grad(u1 )[1] - grad( u2 )[0] )  - 2*grad(grad(S_11delta - S_22delta)[1])[0] - 2*grad(grad(S_12delta)[1])[1] + 2*grad(grad(S_12delta)[0])[0]

        def boundary(x, on_boundary):
           return on_boundary and x[0]<tol 
        bc = DirichletBC(V_S, Constant(0), boundary)
        w1 = TrialFunction(V_S)
        w1_test = TestFunction(V_S)
        a2 = grad(w1)[0]*w1_test*dx   
        l2 = F* w1_test*dx
        prim = Function(V_S)
        solve(a2 == l2, prim, bc)

        V_rot = VectorFunctionSpace(mesh, 'CG', degree-1)
        V_S1 = FunctionSpace(mesh, 'CG',degree-1) 
        mu1 = TrialFunctions(V_rot)
        nu1 = TestFunctions(V_rot)

        a1=dot(grad(S_12delta_cont*mu1[0])[1] + 0.5*grad((S_11delta-S_22delta)/S_12delta)[0] * mu1[0]*S_12delta_cont + 0.5*(S_11delta_cont-S_22delta_cont)/S_12delta_cont*grad(mu1[0]*S_12delta_cont)[0] - grad(mu1[1]*S_12delta_cont)[0] ,nu1[0] )*dx  +  dot(grad(S_12delta_cont*mu1[1])[1] ,nu1[1] )*dx - 0.5*dot((S_11delta_cont-S_22delta_cont)*mu1[0],grad(nu1[0])[0])*dx  + dot(S_12delta_cont*mu1[0], grad(nu1[1])[0])*dx    
    
        l1=dot (prim /2,nu1[1])* dx

        def custom_boundary1(x, on_boundary):
            return on_boundary and (x[0]<tol or x[0] > 1-tol or x[1]<tol)
        def custom_boundary2(x, on_boundary):
            return on_boundary  and x[1]<tol
        bc_mu1 = DirichletBC(V_rot.sub(0), Constant(0), custom_boundary1)    
        bc_mu2 = DirichletBC(V_rot.sub(1), Constant(0), custom_boundary2)
        bc = [bc_mu1, bc_mu2]


        #Normal equation
        A = assemble(a1)
        b = assemble(l1)
        [bc.apply(A, b) for bc in bc]
    
        mu1 = Function(V_rot )
        [bc.apply(A, b) for bc in bc]

        # Calculer A^T A et A^T b
        from petsc4py import PETSc
        from dolfin import as_backend_type

        A_petsc = as_backend_type(A).mat()
        #AT_petsc = A_petsc.transpose()
        b_petsc = as_backend_type(b).vec()


        AT_petsc = PETSc.Mat().createTranspose(A_petsc)

        # Multiplier AT par b pour obtenir AT_b
        AT_b_petsc = AT_petsc * b_petsc
    
        # Créer la matrice normale ATA
        ATA_petsc = AT_petsc * A_petsc

        # Définir le vecteur solution
        mu1_petsc = b_petsc.duplicate()

        # Configurer le solveur PETSc
        ksp = PETSc.KSP().create()
        ksp.setOperators(ATA_petsc)
        ksp.setFromOptions()
        ksp.setType('cg')
        ksp.getPC().setType('none')

        # Résoudre le système linéaire
        ksp.solve(AT_b_petsc, mu1_petsc)

        converged_reason = ksp.getConvergedReason()
        #print(converged_reason)

        if converged_reason > 0:
            print("The solver has converged successfully.")
        else:
            print("Solver diverged due to high residual, but let's check the result.")
            print(f"Final residual norm: {ksp.getResidualNorm()}")
       
        mu1 = Function(V_rot)
        mu1.vector().set_local(mu1_petsc.getArray())

        mu_function_rec_bruit  = project(mu1[0]+ 1 , V_S1)  

        plt.figure(figsize=(5,4))
        p = plot(mu_function_rec_bruit, title=f"mu_rec (reg) with k={k_0} — noise={Noise_list[k]}", cmap="inferno", vmin=1.0, vmax=3.0)
        plt.colorbar(p)
        plt.tight_layout()
        plt.show(block=False)
        plt.pause(0.5) 

        norm1_l2_normal = sqrt(assemble((mu_function - mu_function_rec_bruit)**2 * dx)) / sqrt(assemble(mu_function**2 * dx))
        print('erreur en norme L2 pour l approche avec bruit avec projection, equation normale:', norm1_l2_normal)
        #####################
        Error_list_with_projection[s,k] = norm1_l2_normal 
 
print("k_list=", k_list)
print("Noise=", Noise_list)

print("Error_with_projection_normal_eqt = ", Error_list_with_projection)
print("Error_without_projection = ", Error_list_without_projection)