Python Forum
Finite element method
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Finite element method
#1
Hi everybody, we are french students in mechanics and we have a project in Python, we have to code finite element method. For now we are restricted to a simple dimension and studying the displacement of a rod divided in 3 parts, the first one has young module of k1, second of k2 and third of k1 again. We wrote this code and our proffessor told us to I quote :

"You can leave the functions you already have where they are. So you need one more function which takes x, connectivity, k_func and F as parameters and returns the displacement. Inside this function you can call other functions you already have. The idea is that from now on the calculation method will remain unchanged, but we will have to evaluate solutions for different meshes and different heterogeneous properties. So to make this easier we wrap the resolution in a function."

And we have absolutely no idea of what to do. Does someone can help our little lost souls please ?

import numpy as np
import matplotlib.pyplot as plt
from scipy.sparse.linalg import spsolve
from scipy.sparse import coo_array

# P1 shape functions
N_1 = lambda x: (1 - x) / 2
N_2 = lambda x: (x + 1) / 2

# 2-points quadrature positions
x1 = 1/np.sqrt(3)
x2 = -1/np.sqrt(3)

# Derivatives of P1 shape functions
dN_1 = lambda x: -1. / 2
dN_2 = lambda x: 1. / 2

def extend_dx(dx):
    """Extend dimension of element sizes array to work with local matrices"""
    if dx.ndim == 1:
        dx = dx[:, np.newaxis, np.newaxis]
    return dx

def local_stiffness_matrix(dx,k):
    """
    Make array of local stiffness matrices ∫ k(x) Ni'Nj' dx with 2-points Gauss rule
    """
    #on doit mettre la dedasn le E dans chaque coef voir page 4 recto
    dN = np.array([
        [dN_1(x1)*dN_1(x1)*k[0] + dN_1(x2)*dN_1(x2)*k[1], dN_1(x1)*dN_2(x1)*k[0]+ dN_1(x2)*dN_2(x2)*k[1]],
        [dN_1(x1)*dN_2(x1)*k[0] + dN_1(x2)*dN_2(x2)*k[1], dN_2(x1)*dN_2(x1)*k[0] + dN_2(x2)*dN_2(x2)*k[1]],
    ])
    return 2 / dx * dN.T

def assemble(x, connectivity, local_matrix_func,k):
    """
    Compute local matrices and assemble into a sparse global matrix

    Parameters
    ----------
    - x: position of nodes
    - connectivity: element definition (node i, node j) pair
    - local_matrix_func: function to compute local stiffness matrices
    """
    N = x.size
    dx = extend_dx(x[1:] - x[:-1])

    # Local matrices
    element_matrices = local_matrix_func(dx,k)
    i = np.zeros_like(element_matrices, int)
    j = np.zeros_like(element_matrices, int)

    i[:] = connectivity[:, :, np.newaxis]
    j[:] = connectivity[:, np.newaxis, :]

    # Boundary condition u(0) = 0
    element_matrices[(i == 0) | (j == 0)] = 0
    element_matrices[(i == 0) & (j == 0)] = 1

    # Make sparse matrix
    M = coo_array((element_matrices.flatten(),
                   (i.flatten(), j.flatten()))).tocsc()
    return M
"""""
def quad_positions(x, connectivity, N):
    x = np.linspace(0, L, N)
    connectivity = np.zeros((N - 1, 2), int)
    connectivity[:, 0] = np.arange(x - 1)
    connectivity[:, 1] = np.arange(x - 1) + 1
    xq_tab = np.zeros(2, N-1)
    for i in range(N-1):
        xq_tab[0,i] = x[i] * N_1(x1) + x[i + 1] * N_2(x1)#calcul du point de quadrature 
        xq_tab[1,i] = x[i] * N_1(x2) + x[i + 1] * N_2(x2)
          
    return xq_tab
    
x_moy = (x[:-1] + x[1:]) / 2 #on regarde à la moitié de l'elt
    k = k_func(x_q1,x_q2)
    k = np.zeros(N - 1)#initialisation de k plein de zéros
    pareil mais pour le tableau
    
        tableau avec les points de quadrature qui se remplie au dir et a mesure de la boucle
        k[i] = np.mean(k_func(k_tab))#calcul de k en faisant la moyenne des k1 et k2 Ã  chaque point de quadrature
        

    
    k = k_func(x_q1,x_q2)  # on regarde la valeur de k à cet endroit 
"""
def make_matrices(N):
    """
    Make mass and stiffness matrices for a domain of (-pi, pi) and unit
    coefficients.
    """
    # Domain
    x = np.linspace(0, L, N)

    # Simple 1D connectivity [(0, 1), (1, 2), ...]
    connectivity = np.zeros((N - 1, 2), int)
    connectivity[:, 0] = np.arange(N - 1)
    connectivity[:, 1] = np.arange(N - 1) + 1

    # Make matrices
    xq_tab = quad_positions(x, connectivity, N_elements) 
    k_f = k_func(xq_tab)
    K = assemble(x, connectivity, local_stiffness_matrix,k_f)

    return x, K, connectivity  # On retourne maintenant connectivity


def quad_positions(x, connectivity, N):
    """
    on range nos points de quadrature dans un tableau avec N colonne et 2 lignes pour xq1 et xq2
    """
    xq_tab = np.zeros((2, N - 1))
    for i in range(N - 1):
        xq_tab[0, i] = x[connectivity[i, 0]] * N_1(x1) + x[connectivity[i, 1]] * N_2(x1)
        xq_tab[1, i] = x[connectivity[i, 0]] * N_1(x2) + x[connectivity[i, 1]] * N_2(x2)
    return xq_tab

# on def les k pour les 3 zones de la barre avec k1=k3
def k_func(x):
 
    k = np.zeros_like(x)
    k[x < 1/3] = k1
    k[(x >= 1/3) & (x <= 2/3)] = k2
    k[x > 2/3] = k1
    return k
"""
def E_func(x):
    return 1+np.cos(2*np.pi*x)**2
"""
N_elements = 9
L = 1
k1 = 10
k2 = 1
F = np.zeros(N_elements)
F0=1
F[-1] = F0  # assemblage de F (zeros sauf dernière composante)
x, K, connectivity = make_matrices(N_elements)  # je sors connectivity pour pouvoir l'utiliser après

"""""
#plot la variation de k le long de la barre 
plt.figure(figsize=(8, 4))
x_points = (x[:-1] + x[1:]) / 2  
plt.plot(x_points, k_func(x_points), label="variation de l'hétéro de k")
plt.xlabel('x')
plt.ylabel('k(x)')
plt.title("avec hétéro")
plt.legend()
plt.grid(True)
"""

# Solution Ku = F
u = spsolve(K, F)
x_ana=np.linspace(0,L,N_elements)
def u_ana(x):#on définit la solution analytique
    u1=F0/k1*x
    u2=F0/k2*x+F0*L/(3*k1*k2)*(k2-k1)
    u3=u1+2*F0*L/(3*k2)+F0*L/(3*k1*k2)*(k2-k1)-2*F0*L/(3*k1)
    uana=np.zeros_like(x)
    uana[x < 1/3] = u1[x < 1/3]
    uana[(x >= 1/3) & (x <= 2/3)] = u2[(x >= 1/3) & (x <= 2/3)]
    uana[x > 2/3] = u3[x > 2/3]
    return uana

plt.figure(figsize=(8, 5))
plt.plot(x_ana, u_ana(x_ana), label='Solution analytique u(x)', marker='')
plt.plot(x, u, label='Solution u(x)', marker='')
plt.xlabel('x')
plt.ylabel('u(x)')
plt.title('Solution Ku = F hetero')
plt.legend()
plt.grid(True)
plt.show()





#test pour l'erreur pas sûr du tout



import scipy.integrate as integration #ref :https://docs.scipy.org/doc/scipy/tutorial/integrate.html 
import scipy.interpolate as interpolation #ref: https://docs.scipy.org/doc/scipy/reference/interpolate.html

e = u_ana(x_ana)- u   

# Interpolation polynomiale de e
p = 1
#help(np.polyfit) #degré du polynôme qui va approximer e
coef = np.polyfit(x, e, p) 
poly = np.poly1d(coef) 
e_deri = np.polyder(poly) 

erreur = e_deri(x)**2 + e**2 #ça c'est l'expression dans l'intégrale de la formule de l'erreur énergétique 
integrale = np.trapz(erreur, x) #grace à scipy on peut intégrer avec la méthode des trapèzes comme ça 
e_norme = np.sqrt(integrale)#norme énergétique de e
print(f"Norme énergétique : {e_norme}")
plt.figure(figsize=(8, 5))
plt.plot(x, e, label="Erreur e(x)")
plt.plot(x, e_deri(x), label="Dérivée de l'erreur e'(x)")
plt.xlabel("x")
plt.ylabel("Valeurs")
plt.title("Erreur et dérivée de l'erreur")
plt.legend()
plt.grid(True)
plt.show()


# Ã  faire 
#pour chaque élémetn de calculker la vraie position des points de quadrature FAIT
# implementer une mesure de cette erreur : comment l'erreur évolue avec le nombre d'elt 
# vecteur force 
#on regarde pas au niveau du noeud mais dans l'élément 
Reply
#2
Hi,

My first advice is to tell you that the community will help you if you ask for short requests, in specific technical python topics, and not asking someone to spend a lot of time in trying to understand what you want to do (the longest your code is, the smallest people want to help ... at least in my case).

Let me being honnest with you, i was not in your code and if i'm not mistaken, you just want to calculate displacements from the stiffness matrix and the Force vector ... Ku = F ... so u = K^(-1)*F (in elasticity)

et voilà (but it's not for a sofa Smile )

Paul

from scipy.sparse.linalg import inv
# u = K^-1 * F
def deplacement(rigidite, force):
    return inv(rigidite)@force
u2 = deplacement(rigidite = K, force = F)
print(f"diff(u - u2) = {u - u2}")
Reply
#3
The finite element method (FEM) is a numerical technique that uses math to solve differential equations in engineering and science. It's a popular method for solving problems in areas such as: structural analysis, heat transfer, fluid flow, mass transport, and electromagnetic potential. While FEM is a mathematical technique, FEA is the interpretation of the results FEM provides. FEA gives engineers insights into complex systems and structures, helping them make more informed design decisions. FEM allows for easier modeling of complex geometrical and irregular shapes. Because the designer is able to model both the interior and exterior, he or she can determine how critical factors might affect the entire structure and why failures might occur.
buran write Dec-18-2024, 01:09 PM:
Spam link removed
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020