# Solving Partial Differential Equation ## Poisson's equation Poisson equation has the form of \begin{equation} \dfrac{\partial^2 u}{\partial x^2}+\dfrac{\partial^2 u}{\partial y^2} = -\dfrac{q}{K} \tag{1} \end{equation} In this particular case $u$ is the steady-state temperature. Given the boundary condition we can apply finite difference into this PDE and solve it numerically. The finite difference has the following relation. \begin{equation} w_{ij} = \dfrac{1}{2+2\left( \dfrac{h}{j}\right)^2} \left[ w_{i+1,j} + w_{i-1,j} + (\dfrac{h}{k})^2(w_{i,j+1}+w_{i,j-1}) -h^2f_{ij} \right] \tag{2} \end{equation} Instead of solving this linear equation, we were using relaxation method to solve it which is let the solution iterate until it reach a desired tolerance. Follow figure show the temperature distribution in x-y plane with line represent the isotherm area. The area around x and y axis is what we expected that the center are the hottest and gradually cool down to another two boundary. ![](https://i.imgur.com/zhsco8o.png) ### Code ```fortran= program poisson implicit none ! double precision, parameter :: h=.4, k=1./3. double precision, parameter :: a=0., b=6., c=0., d=5. double precision, parameter :: f=-1.5/1.04 double precision, parameter :: tol=1. integer, parameter :: n=15, m=15 double precision :: h, k double precision :: w((n+1), (n+1)), x((n+1)), y((n+1)) double precision :: z double precision :: NORM, lamb, mu integer :: i, j, l, Nmax=100 h = (b-a) / n k = (d-c) / m ! x and y do i = 1, n-1 x(i) = a + dble(i) * h end do do j = 1, m-1 y(j) = c + dble(j) * k end do ! init w w = 0. l = 1 lamb = h * h / k / k mu = 2. * (1. + lamb) ! step 6 do ! step 7 z = (-h*h*f + y(m-1)*(6.-y(m-1)) + lamb * 0. + lamb * & w(1, m-2) + w(2, m-1)) / mu NORM = abs(z - w(1, m-1)) w(1, m-1) = z ! step 8 do i = 2, n-2 z = (-h*h*f + lamb * 0. + w(i-1, m-1) + w(i+1, m-1)& + lamb * w(i, m-2)) / mu if (abs(w(i, m-1) - z).gt.NORM) then NORM = abs(w(i, m-1) - z) end if w(i, m-1) = z end do ! step 9 z = (-h*h*f + 0. + lamb * 0. + w(n-2, m-1) + & lamb * w(n-1, m-2)) / mu if (abs(w(n-1, m-1) - z).gt.NORM) then NORM = abs(w(n-1, m-1) - z) end if w(n-1, m-1) = z ! step 10 do j = m-2, 2, -1 ! step 11 z = (-h*h*f + y(j) * (5. - y(j)) + lamb * w(1, j+1)& + lamb * w(1, j-1) + w(2, j)) / mu if (abs(w(1, j) - z).gt.NORM) then NORM = abs(w(1, j) - z) end if w(1, j) = z ! step 12 do i = 2, n-2 z = (-h*h*f + w(i-1, j) + lamb * w(i, j+1) + & w(i+1, j) + lamb * w(i, j-1)) / mu if (abs(w(i, j) - z).gt.NORM) then NORM = abs(w(i, j) - z) end if w(i, j) = z end do ! step 13 z = (-h*h*f + 0. + w(n-2, j) + lamb * w(n-1, j) & + lamb * w(n-1, j+1) + lamb * w(n-1, j-1)) / mu if (abs(w(n-1, j) - z).gt.NORM) then NORM = abs(w(n-1, j) - z) end if w(n-1, j) = z end do ! step 14 z = (-h*h*f + y(1)*(5.-y(1)) + lamb * x(1)*(6.-x(1)) + & lamb * w(1, 2) + w(2, 1))/ mu if (abs(w(1, 1) - z).gt.NORM) then NORM = abs(w(1, 1) - z) end if w(1, 1) = z ! step 15 do i = 2, n-2 z = (-h*h*f + lamb * x(i) * (6. - x(i)) + w(i-1, 1) & + lamb * w(i, 2) + w(i+1, 1)) / mu if (abs(w(i, 1) - z).gt.NORM) then NORM = abs(w(i, 1) - z) end if w(i, 1) = z end do ! step 16 z = (-h*h*f + 0. + lamb * x(n-1) * (6. - x(n-1)) + w(n-2, 1)& + lamb * w(n-1, 2)) / mu if (abs(w(n-1, 1) - z).gt.NORM) then NORM = abs(w(n-1, 1) - z) end if w(n-1, 1) = z if (NORM.le.tol) then open(66, file='data.dat', status='unknown') do i = 1, n+1 do j = 1, m+1 write(66, *) w(i, j) end do end do close(66) print *, 'success' exit end if if (l.gt.Nmax) then exit end if l = l + 1 print*, NORM end do end program poisson ``` ## Diffusion equation Similarly, when solving diffusion equation which is another type of PDE. We use finite difference to approximate the derivative terms. The equation of stress-strain of a cylindrical material can be written as \begin{equation} \dfrac{\partial^2 T}{\partial r^2} + \dfrac{1}{r}\dfrac{\partial T}{\partial r} = \dfrac{1}{4K}\dfrac{\partial T}{\partial t} \tag{3} \end{equation} In this case the finite difference equation can be written as \begin{equation} \left( 4kK/h^2 - 2kK/hr_i \right)w_{i-1j} + \left( -8kK/h^2 +1\right)w_{ij}+ \left( 4kK/h^2 + 2kK/hr_i\right)w_{i+1j} = w_{ij+1} \tag{4} \end{equation} which can also be written as \begin{equation} \textbf{A}w^j = w^{j+1} \tag{5} \end{equation} However another approach is combining forward difference and backward difference when dealing with time derivative term. In this case the finite difference has the form of \begin{equation} \begin{split} &\left( 2/h^2 - 1/4hr_i \right)w_{i-1j}+\left( -1/h^2 + 1/4kK \right)w_{ij}+\left( 1/2h^2 + 1/4hr_i \right)w_{i+1j}\\&= \left( -2/h^2 + 1/4hr_i \right)w_{i-1j+1}+\left( 1/h^2 + 1/4kK \right)w_{ij+1}+\left( -1/2h^2 + 1/4hr_i \right)w_{i+1j+1} \end{split} \tag{6} \end{equation} which looks a lit bit intimidating, however we can transform this into matrix form of \begin{equation} \textbf{A}w^{j+1}=\textbf{B}w^{j} \tag{7} \end{equation} Consider a boundary condition is written as a array then this can be further write as \begin{equation} w^{j+1} = \textbf{A}^{-1}( \textbf{B} w^{j} + D^j - D^{j+1} ) \tag{8} \end{equation} Above two method has similar solution given by below figure. ![](https://i.imgur.com/SvCgiYY.png) Strain $I$ can be evaluated by \begin{equation} I = \int_{0.5}^{1}\alpha T(r,t)rdr \tag{9} \end{equation} Two methods give following result. | | Forward | Crank Nicolson | | -------- | -------- | -------- | | I | 1501.30 | 1368.58 | ### Code ### Forward finite-difference ```fortran= program diffusion implicit none double precision, parameter :: diff=.1, k=.5, h=.1 double precision, parameter :: rmin=.5, rmax=1., tmax=10. integer, parameter :: n=nint(tmax/k), m=nint((rmax-rmin)/h) double precision :: a, b, c double precision :: w((m-1), n), r((m-1)), D((m-1), n) integer :: i, j ! step 1: r do i = 1, m-1 r(i) = rmin + h * dble(i) end do ! step 2: initial w(r, 0) do i = 1, m-1 w(i, 1) = 200. * (r(i) - .5) end do do j = 1, n-1 a = 4. * diff * k / h / h - 2. * diff * k / h / r(1) b = -8. * diff * k / h / h + 1. c = 4. * diff * k / h / h + 2. * diff * k / h / r(1) D(1, j+1) = a * (dble(j) * k) + b * w(1, j) + c * w(2, j) do i = 2, m-2 a = 4. * diff * k / h / h - 2. * diff * k / h / r(i) b = -8. * diff * k / h / h + 1. c = 4. * diff * k / h / h + 2. * diff * k / h / r(i) D(i, j+1) = a * w(i-1, j) + b * w(i, j) + c * w(i+1, j) end do a = 4. * diff * k / h / h - 2. * diff * k / h / r(m-1) b = -8. * diff * k / h / h + 1. c = 4. * diff * k / h / h + 2. * diff * k / h / r(m-1) D(m-1, j+1) = a * w(m-2, j) + b * w(m-1, j) + c * (100. + 40. * (dble(j) * k)) w(:, j+1) = D(:, j+1) end do ! step 6: write w(r, 10) open(66, file='out.dat', status='unknown') j = 20 write(66, *) rmin, dble(j) * k do i = 1, m-1 write(66, *) r(i), w(i, j) end do write(66, *) rmax, 100. + 40. * (dble(j) * k) ``` ```fortran= program diffusion implicit none double precision, parameter :: diff=.1, k=.5, h=.1 double precision, parameter :: rmin=.5, rmax=1., tmax=10. integer, parameter :: n=nint(tmax/k), m=nint((rmax-rmin)/h) double precision :: a, b, c double precision :: w((m-1), n), r((m-1)), D((m-1), n) integer :: i, j ! step 1: r do i = 1, m-1 r(i) = rmin + h * dble(i) end do ! step 2: initial w(r, 0) do i = 1, m-1 w(i, 1) = 200. * (r(i) - .5) end do do j = 1, n-1 a = 4. * diff * k / h / h - 2. * diff * k / h / r(1) b = -8. * diff * k / h / h + 1. c = 4. * diff * k / h / h + 2. * diff * k / h / r(1) D(1, j+1) = a * (dble(j) * k) + b * w(1, j) + c * w(2, j) do i = 2, m-2 a = 4. * diff * k / h / h - 2. * diff * k / h / r(i) b = -8. * diff * k / h / h + 1. c = 4. * diff * k / h / h + 2. * diff * k / h / r(i) D(i, j+1) = a * w(i-1, j) + b * w(i, j) + c * w(i+1, j) end do a = 4. * diff * k / h / h - 2. * diff * k / h / r(m-1) b = -8. * diff * k / h / h + 1. c = 4. * diff * k / h / h + 2. * diff * k / h / r(m-1) D(m-1, j+1) = a * w(m-2, j) + b * w(m-1, j) + c * (100. + 40. * (dble(j) * k)) w(:, j+1) = D(:, j+1) end do ! step 6: write w(r, 10) open(66, file='out.dat', status='unknown') j = 20 write(66, *) rmin, dble(j) * k do i = 1, m-1 write(66, *) r(i), w(i, j) end do write(66, *) rmax, 100. + 40. * (dble(j) * k) end program diffusion ``` #### Crank-Nicolson ```fortran= program diffusion implicit none double precision, parameter :: diff=.1, k=.5, h=.1 double precision, parameter :: rmin=.5, rmax=1., tmax=10. integer, parameter :: n=nint(tmax/k), m=nint((rmax-rmin)/h) double precision :: w((m-1), n), r((m-1)), A((m-1), (m-1)), B((m-1), (m-1)), C((m-1)) double precision :: work2((m-1)), pivot((m-1)) integer :: i, j, rc ! step 1: r do i = 1, m-1 r(i) = rmin + h * dble(i) end do ! step 2: initial w(r, 0) do i = 1, m-1 w(i, 1) = 200. * (r(i) - .5) end do do j = 1, n-1 A(1, 1) = 1. / h / h + 1. / 4. / diff / k A(1, 2) = - 1. / 2. / h / h - 1. / 4. / h / r(1) B(1, 1) = - 1. / h / h + 1. / 4. / diff / k B(1, 2) = 1. / 2. / h / h + 1. / 4. / h / r(1) do i = 2, m-2 A(i, i-1) = - 1. / 2. / h / h + 1. / 4. / h / r(i) A(i, i) = 1. / h / h + 1. / 4. / diff / k A(i, i+1) = - 1. / 2. / h / h - 1. / 4. / h / r(i) B(i, i-1) = 1. / 2. / h / h - 1. / 4. / h / r(i) B(i, i) = - 1. / h / h + 1. / 4. / diff / k B(i, i+1) = 1. / 2. / h / h + 1. / 4. / h / r(i) end do A(m-1, m-2) = - 1. / 2. / h / h + 1. / 4. / h / r(m-1) A(m-1, m-1) = 1. / h / h + 1. / diff / k B(m-1, m-2) = 1. / 2. / h / h - 1. / 4. / h / r(m-1) B(m-1, m-1) = - 1. / h / h + 1. / 4. / diff / k call dgetrf(m-1, m-1, A, m-1, pivot, rc) call dgetri(m-1, A, m-1, pivot, work2, m-1, rc) C = matmul(B, w(:, j)) C(1) = C(1) + (1. / 2. / h / h - 1. / 4. / h / r(1)) * dble(j) * k & - (- 1. / 2. / h / h + 1. / 4. / h / r(1)) * dble(j+1) * k C(m-1) = C(m-1) + (1. / 2. / h / h + 1. / 4. / h / r(m-1)) * (100. + 40. * dble(j) * k) & - (- 1. / 2. / h / h - 1. / 4. / h / r(m-1)) * (100. + 40. * dble(j+1) * k) w(:, j+1) = matmul(A, C) end do ! step 6: write w(r, 10) open(66, file='out2.dat', status='unknown') j = 20 write(66, *) rmin, dble(j) * k do i = 1, m-1 write(66, *) r(i), w(i, j) end do write(66, *) rmax, 100. + 40. * (dble(j) * k) end program diffusion ``` ## Wave equation Wave equation has similar approach again! Turning a PDE into finite difference equation and iterate it over and over. The finite difference equation is \begin{equation} w_{ij+1} = 2(1-\lambda^2)w_{ij} + \lambda^2(w_{i+1j}+w_{i-1j})-w_{ij-1} \tag{10} \end{equation} where $\lambda \equiv \alpha k/h$. Then it can be represent as matrix form of \begin{equation} w^{j+1}=\textbf{C}w^j-w^{j-1} \tag{11} \end{equation} And since it required two step to evaluate the next step, we'll have difficulty staring, so we need another equation to start off. \begin{equation} w_{i1} = (1-\lambda^2)f(x_i) + dfrac{\lambda^2}{2}f(x_{i+1})+dfrac{\lambda^2}{2}f(x_{i-1})+kg(x_i) \tag{12} \end{equation} The solution at $t=0.2$ and $t=0.5$ of $V$ and $i$ are given by below figure. ![](https://i.imgur.com/t5NM5hO.png) ### Code ```fortran= program wave implicit none double precision, external :: f, g double precision, parameter :: L=.3d0, C=.1d0, alpha=1.d0/(L*C) double precision, parameter :: lmax=200.d0, tmax=.2d0 double precision, parameter :: h=10.d0, k=.1d0 double precision, parameter :: lambda=k*alpha/h integer, parameter :: m=int(lmax/h), n=int(tmax/k) double precision :: w(0:m, 0:n)=0.d0 integer :: i, j w(0, 0) = f(0.d0) w(m, 0) = f(lmax) do i = 1, m-1 w(i, 0) = f(dble(i) * h) w(i, 1) = (1.d0 - lambda * lambda) * f(dble(i) * h) + & + .5d0 * (lambda * lambda) * (f(dble(i+1) * h) & + f(dble(i-1) * h)) + k * g(dble(i) * h) end do do j = 1, n-1 do i = 1, m-1 w(i, j+1) = 2.d0 * (1.d0 - lambda * lambda) * w(i, j) & + lambda * lambda * (w(i+1, j) + w(i-1, j)) & + w(i, j-1) end do end do ! output open(66, file='1_1.dat', status='unknown') j = n do i = 0, m write(66, *) dble(j-2) * k, dble(i-1) * h, w(i, j) end do end program wave function f(x) implicit none double precision, parameter :: pi=4.d0*atan(1.d0) double precision :: f, x f = 110.d0 * dsin(pi * x / 200.d0) end function function g(x) implicit none double precision :: g, x g = 0.d0 * x end function ```