Monday, February 10, 2025
Damage Modeling using Convection Diffusion Equation
Saturday, February 1, 2025
Multi-Physics Simulations using in-House Code
One fine morning, in abundant spare time, yours truly decided to adapt the CFD and LE code that has been developed over the years in to sort of a code capable of simulating one-way FSI. In this post details about Fluid Structure Interaction (FSI) code are made available.
The code is very simple and basic form is less than 150 lines. A total of 50 lines for the CFD portion and around about 75 lines for the LE (Linear Elasticity) portion. The both CFD and LE codes have be thoroughly validated undependably. For CFD validation, please refer to here and here. Meanwhile the validation for LE portion is available here.
As of now, the work process is CFD→LE. Which means the flow-field has to be calculated first. More details about the CFD portion are available here, here, and here. The pressure and shear stress on the walls of interest are extracted. This pressure and shear stress is then applied as stress loads on the a boundaries of the object in the LE code.
The flow-field is simulated around a thin slander beam which is fixed with the floor. Within Fig. 1, From left to right u and v components of velocity are shown. Meanwhile, pressure and streamlines are shown on the bottom row. Within Fig. 2, the deflected thin beam is shown. The deflection is consistent with the intuition i.e. towards the flow and slightly below.
![]() |
Fig. 1, The CFD results |
![]() |
Fig. 2, The results of LE simulation! |
Thank you for reading. If you want to hire me as your shining new post-doc, do reach out!
Friday, January 31, 2025
Linear Elasticity in 75 Lines!
In abundant spare time ⏳, yours truly has implemented the linear elasticity 〰️ equations in a finite difference method code. The code 🖳 in it's simplest form is less than 75 lines including importing libraries and plotting! 😲 For validation, refer here. More validation examples will be shared in this blog post as these become available. The purpose of this code is not to run linear elasticity simulations but this code is supposed to be used for teaching and has another higher purpose, which will be made available shortly 😁. Happy codding❗❕
Code
Flat Plate in Tension
![]() |
Fig. 1, The animation of plate displacement |
Dog-Bone Specimen in Tension
![]() |
Fig. 2, The results! |
Friday, January 17, 2025
Finite Difference Method for Linear Elasticity Update 03: Plates with Holes
Welcome to my third blog! 👋 In this new adventure 👣, I try to solve linear elasticity 🩹 and fracture mechanics ⚡ problems and using the finite difference method. As with the other two, this blog is also created for my digital CV. The code shared here is heavily influenced by FOANSS 🌬️ and FOAMNE 🧠, the CFD and PINN codes 🖥️ developed by yours truly, in abundant spare time ⏳ of course. 😇 This finite difference based code can run easily on computers with even the modest specifications!
The first case shared here is an example of a flat plate in plain strain under tensile loading. The boundary conditions are mentioned here. While using the finite difference method, artificial diffusion had to added to the iterative loop to stabilize the solution. As is the case with FOANSS, this code is also fully vectorized. Fig. 1 shows the results. The results can be compared to the results shown in Fig. 1 here. The results from a leading linear elasticity solver are also shown within Fig. 1, for a comparison. It can be seen that results are in excellent agreement with each other. For example, the maximum u displacement corresponding to the applied load is at 0.0043 mm as compared to 0.0044 mm from the commercial software that shall not be named 😁.
![]() |
Fig. 1, Current code VS commercial software |
Code
#Copyright <2025> <FAHAD BUTT>
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#%% import necessary libraries
import numpy as np
import matplotlib.pyplot as plt
#%% define parameters
L = 1 # domain length
D = 0.5 # domain width
h = 1 / 50 # grid spacing
Nx = round(L / h) + 1 # grid points in x-axis
Ny = round(D / h) + 1 # grid points in y-axis
nu = 0.3 # Poisson's ratio
s_0 = 1e6 # applied load [Pa]
E = 2e11 # Young's modulus [Pa]
E_0 = E / s_0 # non dimensional Young's modulus
U = s_0 * L / E # scaled u displacement
V = s_0 * D / E # scaled v displacement
e_0 = s_0 / E # scaled strain
alpha = 1 / ((1 + nu) * (1 - (2 * nu))) # non dimensional parameter
beta = 1 / (2 * (1 + nu)) # non dimensional parameter
#%% initialize fields
u = np.zeros((Nx, Ny)) # u displacement
v = np.zeros((Nx, Ny)) # v displacement
s_xx = np.zeros((Nx, Ny)) # normal stress x
s_yy = np.zeros((Nx, Ny)) # normal stress y
s_xy = np.zeros((Nx, Ny)) # shear stress xy
e_xx = np.zeros((Nx, Ny)) # normal strain x
e_yy = np.zeros((Nx, Ny)) # normal strain y
e_xy = np.zeros((Nx, Ny)) # shear strain xy
#%% stability paremeters
relaxation = 0.1 # stabilization factor displacement
relaxation_I = relaxation / 10 # stabilization factor compatibility
epsilon_s = 1e-4 # relaxation factor artifitial diffusion (strain)
epsilon_d = 1e-7 # relaxation factor artifitial diffusion (displacement)
epsilon_r = 1e-10 # relaxation factor artifitial diffusion (compatibility)
#%% solve equations of motion
for nt in range(1000000):
u[1:-1, 1:-1] += relaxation * h**2 * (D * ((s_xx[2:, 1:-1] - s_xx[:-2, 1:-1]) / (2 * h)) + L * ((s_xy[1:-1, 2:] - s_xy[1:-1, :-2]) / (2 * h))) # x momentum
u[1:-1, 1:-1] += epsilon_d * (u[2:, 1:-1] - 2 * u[1:-1, 1:-1] + u[:-2, 1:-1]) / h**2 # artificial diffusion
# boundary conditions for u
u[0, :] = 0 # u = 0 at x = 0
u[-1, :] = u[-2, :] # du/dx = 0 at x = L
u[:, 0] = u[:, 1] # du/dy = 0 at y = 0
u[:, -1] = u[:, -2] # du/dy = 0 at y = D
v[1:-1, 1:-1] += relaxation * h**2 * (D * ((s_xy[2:, 1:-1] - s_xy[:-2, 1:-1]) / (2 * h)) + L * ((s_yy[1:-1, 2:] - s_yy[1:-1, :-2]) / (2 * h))) # y momentum
v[1:-1, 1:-1] += epsilon_d * (v[2:, 1:-1] - 2 * v[1:-1, 1:-1] + v[:-2, 1:-1]) / h**2 # artificial diffusion
# boundary conditions for v
v[0, :] = 0 # v = 0 at x = 0
v[-1, :] = v[-2, :] # dv/dx = 0 at x = L
v[:, 0] = v[:, 1] # dv/dy = 0 at y = 0
v[:, -1] = v[:, -2] # dv/dy = 0 at y = D
e_xx[1:-1, 1:-1] = (u[2:, 1:-1] - u[:-2, 1:-1]) / (2 * h) # normal strain x
e_xx[1:-1, 1:-1] += epsilon_s * (e_xx[2:, 1:-1] - 2 * e_xx[1:-1, 1:-1] + e_xx[:-2, 1:-1]) / h**2 # artificial diffusion
# boundary conditions for e_xx
e_xx[0, :] = e_xx[1, :] # de_xx/dx = 0 at x = 0
e_xx[-1, :] = e_xx[-2, :] # de_xx/dx = 0 at x = L
e_xx[:, 0] = e_xx[:, 1] # de_xx/dy = 0 at y = 0
e_xx[:, -1] = e_xx[:, -2] # de_xx/dy = 0 at y = D
exx_yy = (e_xx[1:-1, 2:] - 2 * e_xx[1:-1, 1:-1] + e_xx[1:-1, :-2]) / h**2
e_yy[1:-1, 1:-1] = (v[1:-1, 2:] - v[1:-1, :-2]) / (2 * h) # normal strain y
e_yy[1:-1, 1:-1] += epsilon_s * (e_yy[2:, 1:-1] - 2 * e_yy[1:-1, 1:-1] + e_yy[:-2, 1:-1]) / h**2 # artificial diffusion
# boundary conditions for e_yy
e_yy[0, :] = e_yy[1, :] # de_yy/dx = 0 at x = 0
e_yy[-1, :] = e_yy[-2, :] # de_yy/dx = 0 at x = L
e_yy[:, 0] = e_yy[:, 1] # de_yy/dy = 0 at y = 0
e_yy[:, -1] = e_yy[:, -2] # de_yy/dy = 0 at y = D
eyy_xx = (e_yy[2:, 1:-1] - 2 * e_yy[1:-1, 1:-1] + e_yy[:-2, 1:-1]) / h**2
e_xy[1:-1, 1:-1] = (u[1:-1, 2:] - u[1:-1, :-2] + v[2:, 1:-1] - v[:-2, 1:-1]) / (2 * h) # shear strain xy
e_xy[1:-1, 1:-1] += epsilon_s * (e_xy[2:, 2:] - 2 * e_xy[1:-1, 1:-1] + e_xy[:-2, :-2]) / h**2 # artificial diffusion
# boundary conditions for e_xy
e_xy[0, :] = e_xy[1, :] # de_xy/dx = 0 at x = 0
e_xy[-1, :] = e_xy[-2, :] # de_xy/dx = 0 at x = L
e_xy[:, 0] = e_xy[:, 1] # de_xy/dy = 0 at y = 0
e_xy[:, -1] = e_xy[:, -2] # de_xy/dy = 0 at y = D
exy_xy = (e_xy[2:, 2:] - e_xy[2:, :-2] - e_xy[:-2, 2:] + e_xy[:-2, :-2]) / (4 * h**2)
R = (L / D) * exx_yy - 2 * exy_xy + (D / L) * eyy_xx # compatibility factor
R[1:-1, 1:-1] += epsilon_r * (R[2:, 2:] - 2 * R[1:-1, 1:-1] + R[:-2, :-2]) / h**2 # artificial diffusion
e_xx[1:-1, 1:-1] -= relaxation_I * h**2 * R * (D / L) # corrected normal strain x
e_yy[1:-1, 1:-1] -= relaxation_I * h**2 * R * (L / D) # corrected normal strain y
e_xy[1:-1, 1:-1] -= relaxation_I * h**2 * R / 2 # corrected shear strain xy
s_xx[1:-1, 1:-1] = alpha * ((1 - nu) * e_xx[1:-1, 1:-1] + nu * e_yy[1:-1, 1:-1]) # normal stress x
# boundary conditions for s_xy
s_xx[0, :] = s_xx[1, :] # ds_xx/dx = 0 at x = 0
s_xx[-1, :] = 1 # s_xx = 1 at x = L
s_xx[:, 0] = s_xx[:, 1] # ds_xx/dy = 0 at y = 0
s_xx[:, -1] = s_xx[:, -2] # ds_xx/dy = 0 at y = D
s_yy[1:-1, 1:-1] = alpha * ((1 - nu) * e_yy[1:-1, 1:-1] + nu * e_xx[1:-1, 1:-1]) # normal stress y
# boundary conditions for s_xy
s_yy[0, :] = s_yy[1, :] # ds_yy/dy = 0 at x = 0
s_yy[-1, :] = s_yy[-2, :] # ds_yy/dy = 0 at x = L
s_yy[:, 0] = 0 # s_yy = 0 at y = 0
s_yy[:, -1] = 0 # s_yy = 0 at y = D
s_xy[1:-1, 1:-1] = beta * e_xy[1:-1, 1:-1] # shear stress xy
# boundary conditions for s_xy
s_xy[0, :] = s_xy[1, :] # ds_xy/dx = 0 at x = 0
s_xy[-1, :] = s_xy[-2, :] # ds_xy/dx = 0 at x = L
s_xy[:, 0] = s_xy[:, 1] # ds_xy/dx = 0 at y = 0
s_xy[:, -1] = s_xy[:, -2] # ds_xy/dx = 0 at y = D
#%% plotting
x = np.linspace(0, L, Nx) # initialize x
y = np.linspace(0, D, Ny) # initialize y
X, Y = np.meshgrid(x, y) # create combinations of sample points along the domain
plt.figure(dpi = 200) # make a nice crisp image :)
plt.contourf(X, Y, (u * U * 1000).T, cmap = 'jet', levels = 256) # contour plot
plt.colorbar(orientation = 'vertical')
plt.gca().set_aspect('equal', adjustable = 'box')
plt.title('u')
plt.show()
plt.figure(dpi = 200) # make a nice crisp image :)
plt.contourf(X, Y, (v * V * 1000).T, cmap = 'jet', levels = 256) # contour plot
plt.colorbar(orientation = 'vertical')
plt.gca().set_aspect('equal', adjustable = 'box')
plt.title('v')
plt.show()
plt.figure(dpi = 200) # make a nice crisp image :)
plt.contourf(X, Y, (s_xx * s_0 * 1e-6).T, cmap = 'jet', levels = 256) # contour plot
plt.colorbar(orientation = 'vertical')
plt.gca().set_aspect('equal', adjustable = 'box')
plt.title('s_xx')
plt.show()
plt.figure(dpi = 200) # make a nice crisp image :)
plt.contourf(X, Y, (s_yy * s_0 * 1e-6).T, cmap = 'jet', levels = 256) # contour plot
plt.colorbar(orientation = 'vertical')
plt.gca().set_aspect('equal', adjustable = 'box')
plt.title('s_yy')
plt.show()
plt.figure(dpi = 200) # make a nice crisp image :)
plt.contourf(X, Y, (s_xy * s_0 * 1e-6).T, cmap = 'jet', levels = 256) # contour plot
plt.colorbar(orientation = 'vertical')
plt.gca().set_aspect('equal', adjustable = 'box')
plt.title('s_xy')
plt.show()
plt.figure(dpi = 200) # make a nice crisp image :)
plt.contourf(X, Y, (e_xx * e_0).T, cmap = 'jet', levels = 256) # contour plot
plt.colorbar(orientation = 'vertical')
plt.gca().set_aspect('equal', adjustable = 'box')
plt.title('e_xx')
plt.show()
plt.figure(dpi = 200) # make a nice crisp image :)
plt.contourf(X, Y, (e_yy * e_0).T, cmap = 'jet', levels = 256) # contour plot
plt.colorbar(orientation = 'vertical')
plt.gca().set_aspect('equal', adjustable = 'box')
plt.title('e_yy')
plt.show()
plt.figure(dpi = 200) # make a nice crisp image :)
plt.contourf(X, Y, (e_xy * e_0).T, cmap = 'jet', levels = 256) # contour plot
plt.colorbar(orientation = 'vertical')
plt.gca().set_aspect('equal', adjustable = 'box')
plt.title('e_xy')
plt.show()
Update 01
![]() |
Fig. 2, Results of the vanilla code |
Update 02: Bending Problems
![]() |
Fig. 3, The results!! |
![]() |
Fig. 4, The results! |
![]() |
Fig. 5, The boundary conditions! |
Update 03: Objects with Holes
![]() |
Fig. 6, The results! |
If you want to hire me as you new shining post-doc or collaborate in research, do reach out.
GPU Accelerated Linear Elasticity Solver
In abundant spare time ⏳, yours truly has implemented GPU accelerated linear elasticity solver [1]. The solver uses finite difference...

-
Welcome to my third blog! 👋 In this new adventure 👣, I try to solve linear elasticity 🩹 and fracture mechanics ⚡ problems and using...
-
One fine morning, in abundant spare time, yours truly decided to adapt the CFD and LE code that has been developed over the years in to...
-
Inspired from fluid dynamics, where the convection diffusion equation is used in several areas including to model turbulence and shocks...