In [1]:
import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay
from scipy.sparse import lil_matrix
from scipy.sparse.linalg import spsolve
In [5]:
def generate_mesh(n, orientation="null"): 
    x = np.linspace(0, 1, n)
    y = np.linspace(0, 1, n)
    X, Y = np.meshgrid(x, y)
    points = np.c_[X.ravel(), Y.ravel()]
    simplices = []
    for i in range(n - 1):
        for j in range(n - 1):
            # Indices des coins d'un rectangle
            p0 = i * n + j  # Bas gauche
            p1 = i * n + (j + 1)  # Bas droite
            p2 = (i + 1) * n + j  # Haut gauche
            p3 = (i + 1) * n + (j + 1)  # Haut droite
            
            if orientation == "left":
                # Division selon la diagonale gauche
                simplices.append([p0, p2, p3])
                simplices.append([p0, p3, p1])
            elif orientation == "right":
                # Division selon la diagonale droite
                simplices.append([p0, p2, p1])
                simplices.append([p2, p3, p1])
            else:  # Orientation "null" : Delaunay (par défaut)
                simplices.extend(Delaunay(points).simplices)
                return points, simplices
    
    return points, np.array(simplices)


def assemble_stiffness_matrix(points, simplices):
    """
    Assemble la matrice de raideur pour -Δu + u avec FEM.
    """
    num_points = len(points)
    A = lil_matrix((num_points, num_points)) 
    
    for tri in simplices:
        p0, p1, p2 = points[tri]
        J = np.array([[p1[0] - p0[0], p2[0] - p0[0]], 
                    [p1[1] - p0[1], p2[1] - p0[1]]])
        detJ = np.abs(np.linalg.det(J))
        B = np.linalg.inv(J).T @ np.array([[-1, 1, 0], [-1, 0, 1]])
        stiffness_local = (B.T @ B) * detJ / 2 + np.eye(3) * detJ / 12
        for i in range(3):
            for j in range(3):
                A[tri[i], tri[j]] += stiffness_local[i, j]
    
    return A

def assemble_load_vector(points, simplices, f_func):
    """
    Assemble le vecteur de charges pour f(x, y).
    """
    num_points = len(points)
    b = np.zeros(num_points)
    
    for tri in simplices:
        p0, p1, p2 = points[tri]
        detJ = np.abs(np.linalg.det(np.array([[p1[0] - p0[0], p2[0] - p0[0]], 
                                            [p1[1] - p0[1], p2[1] - p0[1]]])))
        f_avg = f_func((p0[0] + p1[0] + p2[0]) / 3, 
                    (p0[1] + p1[1] + p2[1]) / 3)
        b_local = f_avg * detJ / 6
        for i in range(3):
            b[tri[i]] += b_local
    
    return b

def apply_dirichlet_bc(A, b, points, boundary_value=0):
    """
    Applique les conditions de Dirichlet homogènes u = 0 sur le bord.
    """
    for i, (x, y) in enumerate(points):
        if x == 0 or x == 1 or y == 0 or y == 1:
            A[i, :] = 0
            A[i, i] = 1
            b[i] = boundary_value

def solve_fem(n, f_expr):
    """
    Résout -Δu + u = f sur le domaine d'étude.
    """
    x, y = sp.symbols('x y')
    f_func = sp.lambdify((x, y), f_expr, 'numpy')
    points, simplices = generate_mesh(n, "right")
    A = assemble_stiffness_matrix(points, simplices)
    b = assemble_load_vector(points, simplices, f_func)
    apply_dirichlet_bc(A, b, points)
    u = spsolve(A.tocsr(), b)
    return points, simplices, u


# f(x, y) = sin(πx)sin(πy)
x, y = sp.symbols('x y')
f = sp.sin(sp.pi * x) * sp.sin(sp.pi * y)

# Résolution
n = 50
points, simplices, u = solve_fem(n, f)

# plt.triplot(points[:, 0], points[:, 1], simplices)
# plt.title("Maillage")
# plt.xlabel('x')
# plt.ylabel('y')
# plt.show()

fig = plt.figure(figsize=(10, 12))
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(points[:, 0], points[:, 1], simplices, u, cmap='viridis', edgecolor='k', linewidth=0.2)
ax.set_title("-Δu + u = f")
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('u(x, y)')
plt.show()
No description has been provided for this image
In [ ]: