<?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>Plug flow reactor with a pressure drop</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/02/18/Plug-flow-reactor-with-a-pressure-drop</link>
      <pubDate>Mon, 18 Feb 2013 09:00:00 EST</pubDate>
      <category><![CDATA[ode]]></category>
      <guid isPermaLink="false">64GviaCGZ1MfVbLqmhN8NSume9U=</guid>
      <description>Plug flow reactor with a pressure drop</description>
      <content:encoded><![CDATA[



&lt;p&gt;
If there is a pressure drop in a plug flow reactor, &lt;sup&gt;&lt;a id="fnr.1" name="fnr.1" class="footref" href="#fn.1"&gt;1&lt;/a&gt;&lt;/sup&gt; there are two equations needed to determine the exit conversion: one for the conversion, and one from the pressure drop.
&lt;/p&gt;

\begin{eqnarray}
\frac{dX}{dW} &amp;=&amp; \frac{k'}{F_A0} \left ( \frac{1-X}{1 + \epsilon X} \right) y\\
\frac{dX}{dy} &amp;=&amp; -\frac{\alpha (1 + \epsilon X)}{2y}
\end{eqnarray}

&lt;p&gt;
Here is how to integrate these equations numerically in python.
&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;from&lt;/span&gt; scipy.integrate &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; odeint
&lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; matplotlib.pyplot &lt;span style="color: #8b0000;"&gt;as&lt;/span&gt; plt

kprime = 0.0266
Fa0 = 1.08
alpha = 0.0166
epsilon = -0.15

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;dFdW&lt;/span&gt;(F, W):
    &lt;span style="color: #228b22;"&gt;'set of ODEs to integrate'&lt;/span&gt;
    X = F[0]
    y = F[1]
    dXdW = kprime / Fa0 * (1-X) / (1 + epsilon*X) * y
    dydW = -alpha * (1 + epsilon * X) / (2 * y)
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; [dXdW, dydW]

Wspan = np.linspace(0,60)
X0 = 0.0
y0 = 1.0
F0 = [X0, y0]
sol = odeint(dFdW, F0, Wspan)

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# now plot the results&lt;/span&gt;
plt.plot(Wspan, sol[:,0], label=&lt;span style="color: #228b22;"&gt;'Conversion'&lt;/span&gt;)
plt.plot(Wspan, sol[:,1], &lt;span style="color: #228b22;"&gt;'g--'&lt;/span&gt;, label=&lt;span style="color: #228b22;"&gt;'y=$P/P_0$'&lt;/span&gt;)
plt.legend(loc=&lt;span style="color: #228b22;"&gt;'best'&lt;/span&gt;)
plt.xlabel(&lt;span style="color: #228b22;"&gt;'Catalyst weight (lb_m)'&lt;/span&gt;)
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/2013-01-08-pdrop.png'&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Here is the resulting figure.
&lt;/p&gt;

&lt;p&gt;&lt;img src="/img/./images/2013-01-08-pdrop.png"&gt;&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/02/18/Plug-flow-reactor-with-a-pressure-drop.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
    <item>
      <title>Plane Poiseuille flow - BVP solve by shooting method</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/02/15/Plane-Poiseuille-flow-BVP-solve-by-shooting-method</link>
      <pubDate>Fri, 15 Feb 2013 09:00:00 EST</pubDate>
      <category><![CDATA[bvp]]></category>
      <guid isPermaLink="false">bqorDWLjDORIcAN56YRsNlcEif0=</guid>
      <description>Plane Poiseuille flow - BVP solve by shooting method</description>
      <content:encoded><![CDATA[


&lt;p&gt;
&lt;a href="http://matlab.cheme.cmu.edu/2011/09/08/plane-poiseuille-flow-bvp-solve-by-shooting-method/" &gt;Matlab post&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
One approach to solving BVPs is to use the shooting method. The reason we cannot use an initial value solver for a BVP is that there is not enough information at the initial value to start. In the shooting method, we take the function value at the initial point, and guess what the function derivatives are so that we can do an integration. If our guess was good, then the solution will go through the known second boundary point. If not, we guess again, until we get the answer we need. In this example we repeat the pressure driven flow example, but illustrate the shooting method.
&lt;/p&gt;

&lt;p&gt;
In the pressure driven flow of a fluid with viscosity \(\mu\) between two stationary plates separated by distance \(d\) and driven by a pressure drop \(\Delta P/\Delta x\), the governing equations on the velocity \(u\) of the fluid are (assuming flow in the x-direction with the velocity varying only in the y-direction):
&lt;/p&gt;

&lt;p&gt;
$$\frac{\Delta P}{\Delta x} = \mu \frac{d^2u}{dy^2}$$
&lt;/p&gt;

&lt;p&gt;
with boundary conditions \(u(y=0) = 0\) and \(u(y=d) = 0\), i.e. the no-slip condition at the edges of the plate.
&lt;/p&gt;

&lt;p&gt;
we convert this second order BVP to a system of ODEs by letting \(u_1 = u\), \(u_2 = u_1'\) and then \(u_2' = u_1''\). This leads to:
&lt;/p&gt;

&lt;p&gt;
\(\frac{d u_1}{dy} = u_2\)
&lt;/p&gt;

&lt;p&gt;
\(\frac{d u_2}{dy} = \frac{1}{\mu}\frac{\Delta P}{\Delta x}\)
&lt;/p&gt;

&lt;p&gt;
with boundary conditions \(u_1(y=0) = 0\) and \(u_1(y=d) = 0\).
&lt;/p&gt;

&lt;p&gt;
for this problem we let the plate separation be d=0.1, the viscosity \(\mu = 1\), and \(\frac{\Delta P}{\Delta x} = -100\).
&lt;/p&gt;

&lt;div id="outline-container-1" class="outline-2"&gt;
&lt;h2 id="sec-1"&gt;&lt;span class="section-number-2"&gt;1&lt;/span&gt; First guess&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
We need u_1(0) and u_2(0), but we only have u_1(0). We need to guess a value for u_2(0) and see if the solution goes through the u_2(d)=0 boundary value.
&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;from&lt;/span&gt; scipy.integrate &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; odeint
&lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; matplotlib.pyplot &lt;span style="color: #8b0000;"&gt;as&lt;/span&gt; plt

d = 0.1 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;plate thickness&lt;/span&gt;

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;odefun&lt;/span&gt;(U, y):
    u1, u2 = U
    mu = 1
    Pdrop = -100
    du1dy = u2
    du2dy = 1.0 / mu * Pdrop
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; [du1dy, du2dy]

u1_0 = 0 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;known&lt;/span&gt;
u2_0 = 1 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;guessed&lt;/span&gt;

dspan = np.linspace(0, d)

U = odeint(odefun, [u1_0, u2_0], dspan)

plt.plot(dspan, U[:,0])
plt.plot([d],[0], &lt;span style="color: #228b22;"&gt;'ro'&lt;/span&gt;)
plt.xlabel(&lt;span style="color: #228b22;"&gt;'d'&lt;/span&gt;)
plt.ylabel(&lt;span style="color: #228b22;"&gt;'$u_1$'&lt;/span&gt;)
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/bvp-shooting-1.png'&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src="/img/./images/bvp-shooting-1.png"&gt;&lt;p&gt;

&lt;p&gt;
Here we have undershot the boundary condition. Let us try a larger guess.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="outline-container-2" class="outline-2"&gt;
&lt;h2 id="sec-2"&gt;&lt;span class="section-number-2"&gt;2&lt;/span&gt; Second guess&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-2"&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;from&lt;/span&gt; scipy.integrate &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; odeint
&lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; matplotlib.pyplot &lt;span style="color: #8b0000;"&gt;as&lt;/span&gt; plt

d = 0.1 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;plate thickness&lt;/span&gt;

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;odefun&lt;/span&gt;(U, y):
    u1, u2 = U
    mu = 1
    Pdrop = -100
    du1dy = u2
    du2dy = 1.0 / mu * Pdrop
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; [du1dy, du2dy]

u1_0 = 0 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;known&lt;/span&gt;
u2_0 = 10 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;guessed&lt;/span&gt;

dspan = np.linspace(0, d)

U = odeint(odefun, [u1_0, u2_0], dspan)

plt.plot(dspan, U[:,0])
plt.plot([d],[0], &lt;span style="color: #228b22;"&gt;'ro'&lt;/span&gt;)
plt.xlabel(&lt;span style="color: #228b22;"&gt;'d'&lt;/span&gt;)
plt.ylabel(&lt;span style="color: #228b22;"&gt;'$u_1$'&lt;/span&gt;)
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/bvp-shooting-2.png'&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src="/img/./images/bvp-shooting-2.png"&gt;&lt;p&gt;

&lt;p&gt;
Now we have clearly overshot. Let us now make a function that will iterate for us to find the right value.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="outline-container-3" class="outline-2"&gt;
&lt;h2 id="sec-3"&gt;&lt;span class="section-number-2"&gt;3&lt;/span&gt; Let fsolve do the work&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-3"&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;from&lt;/span&gt; scipy.integrate &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; odeint
&lt;span style="color: #8b0000;"&gt;from&lt;/span&gt; scipy.optimize &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; fsolve
&lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; matplotlib.pyplot &lt;span style="color: #8b0000;"&gt;as&lt;/span&gt; plt

d = 0.1 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;plate thickness&lt;/span&gt;
Pdrop = -100
mu = 1

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;odefun&lt;/span&gt;(U, y):
    u1, u2 = U
    du1dy = u2
    du2dy = 1.0 / mu * Pdrop
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; [du1dy, du2dy]

u1_0 = 0 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;known&lt;/span&gt;
dspan = np.linspace(0, d)

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;objective&lt;/span&gt;(u2_0):
    dspan = np.linspace(0, d)
    U = odeint(odefun, [u1_0, u2_0], dspan)
    u1 = U[:,0]
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; u1[-1]

u2_0, = fsolve(objective, 1.0)

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;now solve with optimal u2_0&lt;/span&gt;
U = odeint(odefun, [u1_0, u2_0], dspan)

plt.plot(dspan, U[:,0], label=&lt;span style="color: #228b22;"&gt;'Numerical solution'&lt;/span&gt;)
plt.plot([d],[0], &lt;span style="color: #228b22;"&gt;'ro'&lt;/span&gt;)

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;plot an analytical solution&lt;/span&gt;
u = -(Pdrop) * d**2 / 2 / mu * (dspan / d - (dspan / d)**2)
plt.plot(dspan, u, &lt;span style="color: #228b22;"&gt;'r--'&lt;/span&gt;, label=&lt;span style="color: #228b22;"&gt;'Analytical solution'&lt;/span&gt;)


plt.xlabel(&lt;span style="color: #228b22;"&gt;'d'&lt;/span&gt;)
plt.ylabel(&lt;span style="color: #228b22;"&gt;'$u_1$'&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/bvp-shooting-3.png'&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src="/img/./images/bvp-shooting-3.png"&gt;&lt;p&gt;

&lt;p&gt;
You can see the agreement is excellent!
&lt;/p&gt;

&lt;p&gt;
This also seems like a useful bit of code to not have to reinvent regularly, so it has been added to pycse as BVP_sh. Here is an example usage.
&lt;/p&gt;

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

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

d = 0.1 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;plate thickness&lt;/span&gt;
Pdrop = -100
mu = 1

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;odefun&lt;/span&gt;(U, y):
    u1, u2 = U
    du1dy = u2
    du2dy = 1.0 / mu * Pdrop
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; [du1dy, du2dy]

x1 = 0.0; alpha = 0.0
x2 = 0.1; beta = 0.0
init = 2.0 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;initial guess of slope at x=0&lt;/span&gt;

X,Y = BVP_sh(odefun, x1, x2, alpha, beta, init)
plt.plot(X, Y[:,0])
plt.ylim([0, 0.14])

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;plot an analytical solution&lt;/span&gt;
u = -(Pdrop) * d**2 / 2 / mu * (X / d - (X / d)**2)
plt.plot(X, u, &lt;span style="color: #228b22;"&gt;'r--'&lt;/span&gt;, label=&lt;span style="color: #228b22;"&gt;'Analytical solution'&lt;/span&gt;)
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/bvp-shooting-4.png'&lt;/span&gt;)
plt.show()
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src="/img/./images/bvp-shooting-4.png"&gt;&lt;p&gt;
&lt;/div&gt;
&lt;/div&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/02/15/Plane-Poiseuille-flow---BVP-solve-by-shooting-method.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
    <item>
      <title>Plane poiseuelle flow solved by finite difference</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/02/14/Plane-poiseuelle-flow-solved-by-finite-difference</link>
      <pubDate>Thu, 14 Feb 2013 09:00:00 EST</pubDate>
      <category><![CDATA[bvp]]></category>
      <guid isPermaLink="false">qGeCcu8in1U5XgH9_cNB2-ynl4s=</guid>
      <description>Plane poiseuelle flow solved by finite difference</description>
      <content:encoded><![CDATA[


&lt;p&gt;
&lt;a href="http://matlab.cheme.cmu.edu/2011/09/30/plane-poiseuelle-flow-solved-by-finite-difference/" &gt;Matlab post&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Adapted from &lt;a href="http://www.physics.arizona.edu/~restrepo/475B/Notes/sourcehtml/node24.html" &gt;http://www.physics.arizona.edu/~restrepo/475B/Notes/sourcehtml/node24.html&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
We want to solve a linear boundary value problem of the form: y'' = p(x)y' + q(x)y + r(x) with boundary conditions y(x1) = alpha and y(x2) = beta.
&lt;/p&gt;

&lt;p&gt;
For this example, we solve the plane poiseuille flow problem using a finite difference approach. An advantage of the approach we use here is we do not have to rewrite the second order ODE as a set of coupled first order ODEs, nor do we have to provide guesses for the solution. We do, however, have to discretize the derivatives and formulate a linear algebra problem.
&lt;/p&gt;

&lt;p&gt;
we want to solve u'' = 1/mu*DPDX with u(0)=0 and u(0.1)=0. for this problem we let the plate separation be d=0.1, the viscosity \(\mu = 1\), and \(\frac{\Delta P}{\Delta x} = -100\).
&lt;/p&gt;

&lt;p&gt;
The idea behind the finite difference method is to approximate the derivatives by finite differences on a grid. See here for details. By discretizing the ODE, we arrive at a set of linear algebra equations of the form \(A y = b\), where \(A\) and \(b\) are defined as follows.
&lt;/p&gt;

&lt;p&gt;
\[A = \left [ \begin{array}{ccccc} %
 2 + h^2 q_1         &amp; -1 + \frac{h}{2} p_1 &amp; 0                    &amp; 0 &amp; 0 \\
-1 - \frac{h}{2} p_2 &amp; 2 + h^2 q_2          &amp; -1 + \frac{h}{2} p_2 &amp; 0 &amp; 0 \\
0                    &amp; \ddots               &amp; \ddots               &amp; \ddots &amp; 0 \\
0                    &amp; 0                    &amp; -1 - \frac{h}{2} p_{N-1} &amp; 2 + h^2 q_{N-1} &amp; -1 + \frac{h}{2} p_{N-1} \\
0                    &amp; 0                    &amp; 0  &amp; -1 - \frac{h}{2} p_N &amp; 2 + h^2 q_N \end{array} \right ] \]
&lt;/p&gt;

&lt;p&gt;
\[ y = \left [ \begin{array}{c} y_i \\ \vdots \\ y_N \end{array} \right ] \]
&lt;/p&gt;

&lt;p&gt;
\[ b = \left [ \begin{array}{c} -h^2 r_1 + ( 1 + \frac{h}{2} p_1) \alpha \\
-h^2 r_2 \\
\vdots \\
-h^2 r_{N-1} \\
-h^2 r_N + (1 - \frac{h}{2} p_N) \beta \end{array} \right] \]
&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: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;we use the notation for y'' = p(x)y' + q(x)y + r(x)&lt;/span&gt;
&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;p&lt;/span&gt;(x): &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; 0
&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;q&lt;/span&gt;(x): &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; 0
&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;r&lt;/span&gt;(x): &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; -100

&lt;span style="color: #ff0000; font-weight: bold;"&gt;#&lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;we use the notation y(x1) = alpha and y(x2) = beta&lt;/span&gt;

x1 = 0; alpha = 0.0
x2 = 0.1; beta = 0.0

npoints = 100

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;compute interval width&lt;/span&gt;
h = (x2-x1)/npoints;

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;preallocate and shape the b vector and A-matrix&lt;/span&gt;
b = np.zeros((npoints - 1, 1));
A = np.zeros((npoints - 1, npoints - 1));
X = np.zeros((npoints - 1, 1));

&lt;span style="color: #ff0000; font-weight: bold;"&gt;#&lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;now we populate the A-matrix and b vector elements&lt;/span&gt;
&lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; i &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; &lt;span style="color: #8b0000;"&gt;range&lt;/span&gt;(npoints - 1):
    X[i,0] = x1 + (i + 1) * h

    &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;get the value of the BVP Odes at this x&lt;/span&gt;
    pi = p(X[i])
    qi = q(X[i])
    ri = r(X[i])

    &lt;span style="color: #8b0000;"&gt;if&lt;/span&gt; i == 0:
        &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;first boundary condition&lt;/span&gt;
        b[i] = -h**2 * ri + (1 + h / 2 * pi)*alpha; 
    &lt;span style="color: #8b0000;"&gt;elif&lt;/span&gt; i == npoints - 1:
        &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;second boundary condition&lt;/span&gt;
        b[i] = -h**2 * ri + (1 - h / 2 * pi)*beta; 
    &lt;span style="color: #8b0000;"&gt;else:&lt;/span&gt;
        b[i] = -h**2 * ri &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;intermediate points&lt;/span&gt;
    
    &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;(npoints - 1):
        &lt;span style="color: #8b0000;"&gt;if&lt;/span&gt; j == i: &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;the diagonal&lt;/span&gt;
            A[i,j] = 2 + h**2 * qi
        &lt;span style="color: #8b0000;"&gt;elif&lt;/span&gt; j == i - 1: &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;left of the diagonal&lt;/span&gt;
            A[i,j] = -1 - h / 2 * pi
        &lt;span style="color: #8b0000;"&gt;elif&lt;/span&gt; j == i + 1: &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;right of the diagonal&lt;/span&gt;
            A[i,j] = -1 + h / 2 * pi
        &lt;span style="color: #8b0000;"&gt;else:&lt;/span&gt;
            A[i,j] = 0 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;off the tri-diagonal&lt;/span&gt;
 
&lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;solve the equations A*y = b for Y&lt;/span&gt;
Y = np.linalg.solve(A,b)

x = np.hstack([x1, X[:,0], x2])
y = np.hstack([alpha, Y[:,0], beta])

&lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; matplotlib.pyplot &lt;span style="color: #8b0000;"&gt;as&lt;/span&gt; plt

plt.plot(x, y)

mu = 1
d = 0.1
x = np.linspace(0,0.1);
Pdrop = -100 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;this is DeltaP/Deltax&lt;/span&gt;
u = -(Pdrop) * d**2 / 2.0 / mu * (x / d - (x / d)**2)
plt.plot(x,u,&lt;span style="color: #228b22;"&gt;'r--'&lt;/span&gt;)

plt.xlabel(&lt;span style="color: #228b22;"&gt;'distance between plates'&lt;/span&gt;)
plt.ylabel(&lt;span style="color: #228b22;"&gt;'fluid velocity'&lt;/span&gt;)
plt.legend((&lt;span style="color: #228b22;"&gt;'finite difference'&lt;/span&gt;, &lt;span style="color: #228b22;"&gt;'analytical soln'&lt;/span&gt;))
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/pp-bvp-fd.png'&lt;/span&gt;)
plt.show()
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src="/img/./images/pp-bvp-fd.png"&gt;&lt;p&gt;

&lt;p&gt;
You can see excellent agreement here between the numerical and analytical solution.&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/02/14/Plane-poiseuelle-flow-solved-by-finite-difference.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
    <item>
      <title>Computing a pipe diameter</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/02/12/Computing-a-pipe-diameter</link>
      <pubDate>Tue, 12 Feb 2013 09:00:00 EST</pubDate>
      <category><![CDATA[nonlinear algebra]]></category>
      <guid isPermaLink="false">MwL0NFhpVR7fMeQObtSTJCEFmBI=</guid>
      <description>Computing a pipe diameter</description>
      <content:encoded><![CDATA[


&lt;p&gt;
&lt;a href="http://matlab.cheme.cmu.edu/2011/10/27/compute-pipe-diameter/" &gt;Matlab post&lt;/a&gt;
A heat exchanger must handle 2.5 L/s of water through a smooth pipe with length of 100 m. The pressure drop cannot exceed 103 kPa at 25 degC. Compute the minimum pipe diameter required for this application.
&lt;/p&gt;

&lt;p&gt;
Adapted from problem 8.8 in Problem solving in chemical and Biochemical Engineering with Polymath, Excel, and Matlab. page 303.
&lt;/p&gt;

&lt;p&gt;
We need to estimate the Fanning friction factor for these conditions so we can estimate the frictional losses that result in a pressure drop for a uniform, circular pipe. The frictional forces are given by \(F_f = 2f_F \frac{\Delta L v^2}{D}\), and the corresponding pressure drop is given by \(\Delta P = \rho F_f\). In these equations, \(\rho\) is the fluid density, \(v\) is the fluid velocity, \(D\) is the pipe diameter, and \(f_F\) is the Fanning friction factor. The average fluid velocity is given by \(v = \frac{q}{\pi D^2/4}\).
&lt;/p&gt;

&lt;p&gt;
For laminar flow, we estimate \(f_F = 16/Re\), which is a linear equation, and for turbulent flow (\(Re &gt; 2100\)) we have the implicit equation \(\frac{1}{\sqrt{f_F}}=4.0 \log(Re \sqrt{f_F})-0.4\). Of course, we define \(Re = \frac{D v\rho}{\mu}\) where \(\mu\) is the viscosity of the fluid.
&lt;/p&gt;

&lt;p&gt;
It is known that \(\rho(T) = 46.048 + 9.418 T -0.0329 T^2 +4.882\times10^{-5}-2.895\times10^{-8}T^4\) and \(\mu = \exp\left({-10.547 + \frac{541.69}{T-144.53}}\right)\) where \(\rho\) is in kg/m^3 and \(\mu\) is in kg/(m*s).
&lt;/p&gt;

&lt;p&gt;
The aim is to find \(D\) that solves: \(\Delta p = \rho 2 f_F \frac{\Delta L v^2}{D}\). This is a nonlinear equation in \(D\), since D affects the fluid velocity, the Re, and the Fanning friction factor. Here is the solution
&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;from&lt;/span&gt; scipy.optimize &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; fsolve
&lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; matplotlib.pyplot &lt;span style="color: #8b0000;"&gt;as&lt;/span&gt; plt

T = 25 + 273.15
Q = 2.5e-3       &lt;span style="color: #ff0000; font-weight: bold;"&gt;# m^3/s&lt;/span&gt;
deltaP = 103000  &lt;span style="color: #ff0000; font-weight: bold;"&gt;# Pa&lt;/span&gt;
deltaL = 100     &lt;span style="color: #ff0000; font-weight: bold;"&gt;# m&lt;/span&gt;

&lt;span style="color: #ff0000; font-weight: bold;"&gt;#Note these correlations expect dimensionless T, where the magnitude&lt;/span&gt;
&lt;span style="color: #ff0000; font-weight: bold;"&gt;# of T is in K&lt;/span&gt;

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;rho&lt;/span&gt;(T):
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; 46.048 + 9.418 * T -0.0329 * T**2 +4.882e-5 * T**3 - 2.895e-8 * T**4

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;mu&lt;/span&gt;(T):
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; np.exp(-10.547 + 541.69 / (T - 144.53))

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;fanning_friction_factor_&lt;/span&gt;(Re):
    &lt;span style="color: #8b0000;"&gt;if&lt;/span&gt; Re &amp;lt; 2100:
        &lt;span style="color: #8b0000;"&gt;raise&lt;/span&gt; &lt;span style="color: #cd0000;"&gt;Exception&lt;/span&gt;(&lt;span style="color: #228b22;"&gt;'Flow is probably not turbulent, so this correlation is not appropriate.'&lt;/span&gt;)
    &lt;span style="color: #ff0000; font-weight: bold;"&gt;# solve the Nikuradse correlation to get the friction factor&lt;/span&gt;
    &lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;fz&lt;/span&gt;(f): &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; 1.0/np.sqrt(f) - (4.0*np.log10(Re*np.sqrt(f))-0.4)
    sol, = fsolve(fz, 0.01)
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; sol

fanning_friction_factor = np.vectorize(fanning_friction_factor_)

Re = np.linspace(2200, 9000)
f = fanning_friction_factor(Re)

plt.plot(Re, f)
plt.xlabel(&lt;span style="color: #228b22;"&gt;'Re'&lt;/span&gt;)
plt.ylabel(&lt;span style="color: #228b22;"&gt;'fanning friction factor'&lt;/span&gt;)
&lt;span style="color: #ff0000; font-weight: bold;"&gt;# You can see why we use 0.01 as an initial guess for solving for the&lt;/span&gt;
&lt;span style="color: #ff0000; font-weight: bold;"&gt;# Fanning friction factor; it falls in the middle of ranges possible&lt;/span&gt;
&lt;span style="color: #ff0000; font-weight: bold;"&gt;# for these Re numbers.&lt;/span&gt;
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/pipe-diameter-1.png'&lt;/span&gt;)

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;objective&lt;/span&gt;(D):
    v = Q / (np.pi * D**2 / 4)
    Re = D * v * rho(T) / mu(T)

    fF = fanning_friction_factor(Re)

    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; deltaP - 2 * fF * rho(T) * deltaL * v**2 / D
    
D, = fsolve(objective, 0.04)

&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt;(&lt;span style="color: #228b22;"&gt;'The minimum pipe diameter is {0} m\n'&lt;/span&gt;.format(D))
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
The minimum pipe diameter is 0.0389653369531 m
&lt;/pre&gt;
&lt;p&gt;
Any pipe diameter smaller than that value will result in a larger pressure drop at the same volumetric flow rate, or a smaller volumetric flowrate at the same pressure drop. Either way, it will not meet the design specification.
&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/02/12/Computing-a-pipe-diameter.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
  </channel>
</rss>
