#!/usr/bin/env python # coding: utf-8 # In[1]: from IPython.display import HTML s="""

2D Poisson Equation


"""; h=HTML(s); h # *** # # Understand the Problem # # * What is the 2D pressure field for the **Poisson equation** when the initial conditions are **zero everywhere** and the boundary conditions are 0 and two source term spikes exists? # # * The Poisson Equation for pressure is described as follows: # # $$ {\partial^2 p \over \partial x^2} + {\partial^2 p \over \partial y^2} = b $$ # # *** # # # Formulate the Problem # # ## Input Data: # # This problem has **no temporal component**, so we use a number of iterations. # # * `niter` = 51 (maximum number of iterations) # * `nx` = 21 (number of x spatial points) # * `ny` = 11 (number of y spatial points) # * `xmax` = 2 # * `ymax` = 1 # # ## Initial Conditions: # # * $\forall (n, x, y) \quad n = 0 \rightarrow p = 0$ # # ## Boundary Conditions: # # * $\forall (n, x, y) \quad x = 0 \lor x = 2 \lor y = 0 \lor y = 1 \rightarrow p = 0$ # # ## Source Term # # * $\forall (n, i, j) \quad {i = {{nx} \over 4}} \lor {j = {{ny} \over 4}} \rightarrow b_{i,j}=100 $ # * $\forall (n, i, j) \quad {i = {{3nx} \over 4}} \lor {j = {{3ny} \over 4}} \rightarrow b_{i,j}=-100 $ # * $\forall (n, i, j) \quad \lnot \left( {i = {{3nx} \over 4}} \lor {j = {{3nj} \over 4}} \lor {i = {{3nx} \over 4}} \lor {j = {{3nj} \over 4}} \right ) \rightarrow b_{i,j}=0 $ # # ## Output Data: # # * $\forall (n,x,y) \quad \ p = ?$ # # *** # # # Design Algorithm to Solve Problem # # ## Space-time discretisation: # # * i $\rightarrow$ index of grid in x # * j $\rightarrow$ index of grid in y # * n $\rightarrow$ index of iterations # # ## Numerical scheme # * 1st order FD in time (actually a number of iterations) # * 2nd order CD in space # # ## Discrete equation # # Since no terms have a temporal component - bring source term to other side and equate to forward differencing (as we did for Laplace Equation). Then take steady state after a certain number of iterations. # # $$ {{\partial p} \over {\partial t}} = {\partial^2 p \over \partial x^2} + {\partial^2 p \over \partial y^2} - b $$ # # $$ {{p_{i,j}^{n+1}-p_{i,j}^n} \over {\Delta t}} = {{p_{i+1,j}^n -2p_{i,j}^n + p_{i-1,j}^n} \over \Delta x^2} + {{p_{i,j+1}^n -2p_{i,j}^n + p_{i,j-1}^n} \over \Delta y^2} - b_{i,j}^n $$ # # Also assume that $ \Delta x = \Delta y = h $ # # $$ {{p_{i,j}^{n+1}-p_{i,j}^n} \over {\Delta t}} = {{p_{i+1,j}^n -2p_{i,j}^n + p_{i-1,j}^n} \over h^2} + {{p_{i,j+1}^n -2p_{i,j}^n + p_{i,j-1}^n} \over h^2} - b_{i,j}^n $$ # # # ## Transpose # # $$ p_{i,j}^{n+1} = p_{i,j}^n + {{\Delta t} \over {h^2}} \left( # p_{i+1,j}^n + p_{i-1,j}^n + p_{i,j+1}^n + p_{i,j-1}^n - 4p_{i,j}^n \right) - b_{i,j}^n \Delta t $$ # # For the fastest convergence $ r = {{\Delta t} \over {h^2}} = {1 \over 4} $ and $ \Delta t = {{h^2} \over 4} $ # # Hence: # # $$ p_{i,j}^{n+1} = {1 \over 4} \left( p_{i+1,j}^n + p_{i-1,j}^n + p_{i,j+1}^n + p_{i,j-1}^n - b_{i,j}^n h^2 \right) $$ # # ## Pseudo-code # # niter = 500 # nx = 21 # xmax = 2 # ny = 11 # ymax = 1 # dx = xmax/(nx-1) # dy = ymax/(ny-1) # error_target = 1*10^-2 # # # Dirichlet Boundary Conditions # # p(0,:,:) = p(nx-1,:,:) = p(:,0,:) = p(:,nj-1,:) = 0 # # # Initial Conditions # # p(:,:,0) = 0 # # # Numerical Computation # # while error GT error_target: # # for n between 0 and niter-2 # for i between 1 and nx-2 # for j between 1 and ny-2 # # if(i == (nx / 4) OR j == (ny / 4)) # b(i,j,n) = 100 # else if (i == (3nx / 4) OR j == (3ny / 4)) # b(i,j,n) = -100 # else # b(i,j,n) = 0 # # p(i,j,n+1) = p(i+1,j,n) + p(i-1,j,n) + p(i,j+1,n) + p(i,j-1,n) - b(i,j,n) * (h**2)/4 # # error = sum[abs(p(i,j,n+1)) - abs(p(i,j,n)) ] / sum[abs(p(i,j,n))] # # *** # # # Implement algorithm in Python # # I initially tried this with the laplace equation from the previous case # In[1]: def laplace_equation(error_target, niter, nx, xmax, ymax): """ Returns the velocity field and distance for 2D Poisson Equation """ # Increments in x and y are the same - h, dy & dx should be rational numbers: h = dy = dx = xmax/(nx-1) # Compute ny from dy and ymax - ny should be an integer ny = int(ymax/dy) + 1 # Initialise data structures: import numpy as np p = b = np.zeros(((nx,ny,niter))) x = np.zeros(nx) y = np.zeros(ny) jpos = np.zeros(ny) jneg = np.zeros(ny) # X Loop for i in range(0,nx): x[i] = i*dx # Y Loop for j in range(0,ny): y[j] = j*dy # Initial conditions p[:,:,0] = 0 # Dirichlet Boundary Conditions: # p boundary, left: p[0,:,:] = 0 # p boundary, right: for j in range(0,ny): p[nx-1,j,:] = y[j] # von Neumann Boundary Conditions: # Values for correction at boundaries: for j in range(0,ny): jpos[j] = j + 1 jneg[j] = j - 1 # Set Reflection: jpos[ny-1] = ny-2 jneg[0] = 1 while True: for n in range(0,niter-1): for i in range(1,nx-1): for j in range(0,ny): p[i,j,n+1] = 0.25*( p[i+1,j,n]+p[i-1,j,n]+p[i,jpos[j],n]+p[i,jneg[j],n]) error = (np.sum(np.abs(p[i,j,n+1])-np.abs(p[i,j,n]) ) / np.sum(np.abs(p[i,j,n+1]) )) if(error < error_target): print "n = " + str(n) + " completed" break break return p, x, y # In[2]: p1, x1, y1 = laplace_equation(1.0e-6, 5000, 51, 2.0, 1.0) # In[11]: def plot_3D(p,x,y,time,title,label): """ Plots the 2D velocity field """ import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D fig=plt.figure(figsize=(11,7),dpi=100) ax=fig.gca(projection='3d') ax.set_xlabel('x (m)') ax.set_ylabel('y (m)') ax.set_zlabel(label) ax.set_xlim(0,2) ax.set_ylim(0,1) ax.view_init(30,225) Y,X=np.meshgrid(y,x) #note meshgrid uses y,x not x,y!!! surf=ax.plot_surface(X,Y,p[:,:,time], rstride=1, cstride=1) plt.title(title) plt.show() # In[12]: plot_3D(p1,x1,y1,0,'Figure 1: Initial Conditions: n=0, nx=51','p (Pa)') plot_3D(p1,x1,y1,3129,'Figure 2: Final Conditions: n=3129, nx=51','p (Pa)') # ## Now implement the solution to the Poisson Equation # In[25]: def poisson_equation(error_target, niter, nx, xmax, ymax): """ Returns the velocity field and distance for 2D Poisson Equation """ # Increments in x and y are the same - h, dy & dx should be rational numbers: h = dy = dx = xmax/(nx-1) # Compute ny from dy and ymax - ny should be an integer ny = int(ymax/dy) + 1 # Initialise data structures: import numpy as np p = np.zeros(((nx,ny,niter))) b = np.zeros((nx,ny)) x = np.zeros(nx) y = np.zeros(ny) b[nx/4,ny/4] = 100 b[3*nx/4,3*ny/4] = -100 # X Loop for i in range(0,nx): x[i] = i*dx # Y Loop for j in range(0,ny): y[j] = j*dy # Initial conditions p[:,:,0] = 0 # Dirichlet Boundary Conditions: p[0,:,:] = p[nx-1,:,:] = p[:,0,:] = p[:,ny-1,:] = 0 #Loop while True: for n in range(0,niter-1): for i in range(1,nx-1): for j in range(1,ny-1): p[i,j,n+1] = 0.25*( p[i+1,j,n]+p[i-1,j,n]+p[i,j+1,n]+p[i,j-1,n] - b[i,j]*h**2) error = (np.sum(np.abs(p[i,j,n+1])-np.abs(p[i,j,n]) ) / np.sum(np.abs(p[i,j,n+1]) )) if(error < error_target): print "n = " + str(n) + " completed" break break return p, x, y # In[41]: p2, x2, y2 = poisson_equation(1.0e-6, 3000, 51, 2.0, 1.0) # In[42]: plot_3D(p2,x2,y2,0,'Figure 1: Initial Conditions: n=0, nx=51','p (Pa)') plot_3D(p2,x2,y2,17,'Figure 2: Final Conditions: n=17, nx=51','p (Pa)') # ## What is this? # # * We have two sources (spikes) and the rest is the effect of diffusion # * Did run into trouble with memory in certain cases # * Seemed unstable at some points - but might have been a resolution problem? # In[ ]: