<?xml version="1.0" encoding="UTF-8"?>

<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:wfw="http://wellformedweb.org/CommentAPI/"
     >
  <channel>
    <atom:link href="http://kitchingroup.cheme.cmu.edu/blog/feed/index.xml" rel="self" type="application/rss+xml" />
    <title>The Kitchin Research Group</title>
    <link>https://kitchingroup.cheme.cmu.edu/blog</link>
    <description>Chemical Engineering at Carnegie Mellon University</description>
    <pubDate>Sat, 01 Nov 2025 13:47:46 GMT</pubDate>
    <generator>Blogofile</generator>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    
    <item>
      <title>Transient diffusion - partial differential equations</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/04/02/Transient-diffusion-partial-differential-equations</link>
      <pubDate>Tue, 02 Apr 2013 09:27:42 EDT</pubDate>
      <category><![CDATA[pde]]></category>
      <guid isPermaLink="false">VRp1cPaRr2c73g7BnG_yYjVBeiA=</guid>
      <description>Transient diffusion - partial differential equations</description>
      <content:encoded><![CDATA[


&lt;p&gt;

We want to solve for the concentration profile of  component that diffuses into a 1D rod, with an impermeable barrier at the end. The PDE governing this situation is:
&lt;/p&gt;

&lt;p&gt;
\(\frac{\partial C}{\partial t} = D \frac{\partial^2 C}{\partial x^2}\)
&lt;/p&gt;

&lt;p&gt;
at \(t=0\), in this example we have \(C_0(x) = 0\) as an initial condition, with boundary conditions \(C(0,t)=0.1\) and \(\partial C/ \partial x(L,t)=0\).
&lt;/p&gt;

&lt;p&gt;
We are going to discretize this equation in both time and space to arrive at the solution. We will let \(i\) be the index for the spatial discretization, and \(j\) be the index for the temporal discretization. The discretization looks like this.
&lt;/p&gt;

&lt;p&gt;&lt;img src="/img/./images/pde-diffusion-discretization-scheme.png"&gt;&lt;p&gt;

&lt;p&gt;
Note that we cannot use the method of lines as we did before because we have the derivative-based boundary condition at one of the boundaries.
&lt;/p&gt;

&lt;p&gt;
We approximate the time derivative as:
&lt;/p&gt;

&lt;p&gt;
\(\frac{\partial C}{\partial t} \bigg| _{i,j} \approx \frac{C_{i,j+1} - C_{i,j}}{\Delta t} \)
&lt;/p&gt;

&lt;p&gt;
\(\frac{\partial^2 C}{\partial  x^2} \bigg| _{i,j} \approx \frac{C_{i+1,j} - 2 C_{i,j} + C_{i-1,j}}{h^2} \)
&lt;/p&gt;

&lt;p&gt;
We define \(\alpha = \frac{D \Delta t}{h^2}\), and from these two approximations and the PDE, we solve for the unknown solution at a later time step as:
&lt;/p&gt;

&lt;p&gt;
\(C_{i, j+1} = \alpha C_{i+1,j} + (1 - 2 \alpha) C_{i,j}  + \alpha C_{i-1,j} \)
&lt;/p&gt;

&lt;p&gt;
We know \(C_{i,j=0}\) from the initial conditions, so we simply need to iterate to evaluate \(C_{i,j}\), which is the solution at each time step.
&lt;/p&gt;

&lt;p&gt;
See also: &lt;a href="http://www3.nd.edu/~jjwteach/441/PdfNotes/lecture16.pdf" &gt;http://www3.nd.edu/~jjwteach/441/PdfNotes/lecture16.pdf&lt;/a&gt;
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-python"&gt;&lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; numpy &lt;span style="color: #8b0000;"&gt;as&lt;/span&gt; np
&lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; matplotlib.pyplot &lt;span style="color: #8b0000;"&gt;as&lt;/span&gt; plt

N = 20  &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;number of points to discretize&lt;/span&gt;
L = 1.0
X = np.linspace(0, L, N) &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;position along the rod&lt;/span&gt;
h = L / (N - 1)          &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;discretization spacing&lt;/span&gt;

C0t = 0.1  &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;concentration at x = 0&lt;/span&gt;
D = 0.02

tfinal = 50.0
Ntsteps = 1000
dt = tfinal / (Ntsteps - 1)
t = np.linspace(0, tfinal, Ntsteps)

alpha = D * dt / h**2
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; alpha

C_xt = [] &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;container for all the time steps&lt;/span&gt;

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;initial condition at t = 0&lt;/span&gt;
C = np.zeros(X.shape)
C[0] = C0t

C_xt += [C]

&lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; j &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; &lt;span style="color: #8b0000;"&gt;range&lt;/span&gt;(1, Ntsteps):
    N = np.zeros(C.shape)
    N[0] =  C0t
    N[1:-1] = alpha*C[2:] + (1 - 2 * alpha) * C[1:-1] + alpha * C[0:-2]
    N[-1] = N[-2]  &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;derivative boundary condition flux = 0&lt;/span&gt;
    C[:] = N
    C_xt += [N]

    &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;plot selective solutions&lt;/span&gt;
    &lt;span style="color: #8b0000;"&gt;if&lt;/span&gt; j &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; [1,2,5,10,20,50,100,200,500]:
        plt.plot(X, N, label=&lt;span style="color: #228b22;"&gt;'t={0:1.2f}'&lt;/span&gt;.format(t[j]))

plt.xlabel(&lt;span style="color: #228b22;"&gt;'Position in rod'&lt;/span&gt;)
plt.ylabel(&lt;span style="color: #228b22;"&gt;'Concentration'&lt;/span&gt;)
plt.title(&lt;span style="color: #228b22;"&gt;'Concentration at different times'&lt;/span&gt;)
plt.legend(loc=&lt;span style="color: #228b22;"&gt;'best'&lt;/span&gt;)
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/transient-diffusion-temporal-dependence.png'&lt;/span&gt;)

C_xt = np.array(C_xt)
plt.figure()
plt.plot(t, C_xt[:,5], label=&lt;span style="color: #228b22;"&gt;'x={0:1.2f}'&lt;/span&gt;.format(X[5]))
plt.plot(t, C_xt[:,10], label=&lt;span style="color: #228b22;"&gt;'x={0:1.2f}'&lt;/span&gt;.format(X[10]))
plt.plot(t, C_xt[:,15], label=&lt;span style="color: #228b22;"&gt;'x={0:1.2f}'&lt;/span&gt;.format(X[15]))
plt.plot(t, C_xt[:,19], label=&lt;span style="color: #228b22;"&gt;'x={0:1.2f}'&lt;/span&gt;.format(X[19]))
plt.legend(loc=&lt;span style="color: #228b22;"&gt;'best'&lt;/span&gt;)
plt.xlabel(&lt;span style="color: #228b22;"&gt;'Time'&lt;/span&gt;)
plt.ylabel(&lt;span style="color: #228b22;"&gt;'Concentration'&lt;/span&gt;)
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/transient-diffusion-position-dependence.png'&lt;/span&gt;)

plt.show()
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
0.361361361361
&lt;/pre&gt;

&lt;p&gt;&lt;img src="/img/./images/transient-diffusion-temporal-dependence.png"&gt;&lt;p&gt;

&lt;p&gt;&lt;img src="/img/./images/transient-diffusion-position-dependence.png"&gt;&lt;p&gt;

&lt;p&gt;
The solution is somewhat sensitive to the choices of time step and spatial discretization. If you make the time step too big, the method is not stable, and large oscillations may occur.
&lt;/p&gt;
&lt;p&gt;Copyright (C) 2013 by John Kitchin. See the &lt;a href="/copying.html"&gt;License&lt;/a&gt; for information about copying.&lt;p&gt;&lt;p&gt;&lt;a href="/org/2013/04/02/Transient-diffusion---partial-differential-equations.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
  </channel>
</rss>
