<?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>Line integrals in Python with autograd</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2018/11/16/Line-integrals-in-Python-with-autograd</link>
      <pubDate>Fri, 16 Nov 2018 08:39:44 EST</pubDate>
      <category><![CDATA[integration]]></category>
      <category><![CDATA[autograd]]></category>
      <category><![CDATA[python]]></category>
      <guid isPermaLink="false">8pnl2uSxymXf56S8frpp6psrwys=</guid>
      <description>Line integrals in Python with autograd</description>
      <content:encoded><![CDATA[


&lt;div id="table-of-contents"&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div id="text-table-of-contents"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#org107f13d"&gt;1. Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;
A line integral is an integral of a function along a curve in space. We usually represent the curve by a parametric equation, e.g. \(\mathbf{r}(t) = [x(t), y(t), z(t)] = x(t)\mathbf{i} + y(t)\mathbf{j} + z(t)\mathbf{k}\).  So, in general the curve will be a vector function, and the function we want to integrate will also be a vector function.
&lt;/p&gt;

&lt;p&gt;
Then, we can write the line integral definition as:
&lt;/p&gt;

&lt;p&gt;
\(\int_C \mathbf{F(r)}\cdot d\mathbf{r} = \int_a^b \mathbf{F}({\mathbf{r}(t)) \cdot \mathbf{r'}(t) dt\) where \(\mathbf{r'}(t) = \frac{d\mathbf{r}}{dt}\). This integrand is a scalar function, because of the dot product.
&lt;/p&gt;

&lt;p&gt;
The following examples are adapted from Chapter 10 in Advanced Engineering Mathematics by Kreysig.
&lt;/p&gt;

&lt;p&gt;
The first example is the evaluation of  a line integral in the plane. We want to evaluate the integral of \(\mathbf{F(r)}=[-y, -xy]\) on the curve \(\mathbf{r(t)}=[-sin(t), cos(t)]\) from t=0 to t = &amp;pi;/2. The answer in the book is given as 0.4521. Here we evaluate this numerically, using autograd for the relevant derivative. Since the curve has multiple outputs, we have to use the jacobian function to get the derivatives. After that, it is a simple bit of matrix multiplication, and a call to the quad function.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-ipython"&gt;&lt;span style="color: #0000FF;"&gt;import&lt;/span&gt; autograd.numpy &lt;span style="color: #0000FF;"&gt;as&lt;/span&gt; np
&lt;span style="color: #0000FF;"&gt;from&lt;/span&gt; autograd &lt;span style="color: #0000FF;"&gt;import&lt;/span&gt; jacobian
&lt;span style="color: #0000FF;"&gt;from&lt;/span&gt; scipy.integrate &lt;span style="color: #0000FF;"&gt;import&lt;/span&gt; quad

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;F&lt;/span&gt;(X):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #BA36A5;"&gt;x&lt;/span&gt;, &lt;span style="color: #BA36A5;"&gt;y&lt;/span&gt; = X
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; -y, -x * y

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;r&lt;/span&gt;(t):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; np.array([-np.sin(t), np.cos(t)])

&lt;span style="color: #BA36A5;"&gt;drdt&lt;/span&gt; = jacobian(r)

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;integrand&lt;/span&gt;(t):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; F(r(t)) @ drdt(t)

&lt;span style="color: #BA36A5;"&gt;I&lt;/span&gt;, &lt;span style="color: #BA36A5;"&gt;e&lt;/span&gt; = quad(integrand, 0.0, np.pi / 2)
&lt;span style="color: #0000FF;"&gt;print&lt;/span&gt;(f&lt;span style="color: #008000;"&gt;'The integral is {I:1.4f}.'&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
The integral is 0.4521.


&lt;/pre&gt;

&lt;p&gt;
We get the same result as the analytical solution.
&lt;/p&gt;


&lt;p&gt;
The next example is in three dimensions. Find the line integral along \(\mathbf{r}(t)=[cos(t), sin(t), 3t]\) of the function \(\mathbf{F(r)}=[z, x, y]\) from t=0 to t=2 &amp;pi;. The solution is given as 21.99.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-ipython"&gt;&lt;span style="color: #0000FF;"&gt;import&lt;/span&gt; autograd.numpy &lt;span style="color: #0000FF;"&gt;as&lt;/span&gt; np
&lt;span style="color: #0000FF;"&gt;from&lt;/span&gt; autograd &lt;span style="color: #0000FF;"&gt;import&lt;/span&gt; elementwise_grad, grad, jacobian

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;F&lt;/span&gt;(X):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #BA36A5;"&gt;x&lt;/span&gt;, &lt;span style="color: #BA36A5;"&gt;y&lt;/span&gt;, &lt;span style="color: #BA36A5;"&gt;z&lt;/span&gt; = X
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; [z, x, y]

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;C&lt;/span&gt;(t):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; np.array([np.cos(t), np.sin(t), 3 * t])

&lt;span style="color: #BA36A5;"&gt;dCdt&lt;/span&gt; = jacobian(C, 0)

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;integrand&lt;/span&gt;(t):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; F(C(t)) @ dCdt(t)

&lt;span style="color: #BA36A5;"&gt;I&lt;/span&gt;, &lt;span style="color: #BA36A5;"&gt;e&lt;/span&gt; = quad(integrand, 0, 2 * np.pi)
&lt;span style="color: #0000FF;"&gt;print&lt;/span&gt;(f&lt;span style="color: #008000;"&gt;'The integral is {I:1.2f}.'&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
The integral is 21.99.


&lt;/pre&gt;

&lt;p&gt;
That is also the same as the analytical solution. Note the real analytical solution was 7 &amp;pi;, which is nearly equivalent to our answer.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-ipython"&gt;7 * np.pi - I
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
3.552713678800501e-15

&lt;/pre&gt;


&lt;p&gt;
As a final example, we consider an alternate form of the line integral. In this form we do not use a dot product, so the integral results in a vector. This doesn't require anything from autograd, but does require us to be somewhat clever in how to do the integrals since quad can only integrate scalar functions. We need to integrate each component of the integrand independently. Here is one approach where we use lambda functions for each component. You could also manually separate the components.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-ipython"&gt;&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;F&lt;/span&gt;(r):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #BA36A5;"&gt;x&lt;/span&gt;, &lt;span style="color: #BA36A5;"&gt;y&lt;/span&gt;, &lt;span style="color: #BA36A5;"&gt;z&lt;/span&gt; = r
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; x * y, y * z, z

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;r&lt;/span&gt;(t):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; np.array([np.cos(t), np.sin(t), 3 * t])

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;integrand&lt;/span&gt;(t):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; F(r(t))

[quad(&lt;span style="color: #0000FF;"&gt;lambda&lt;/span&gt; t: integrand(t)[i], 0, 2 * np.pi)[0] &lt;span style="color: #0000FF;"&gt;for&lt;/span&gt; i &lt;span style="color: #0000FF;"&gt;in&lt;/span&gt; [0, 1, 2]]
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
[-6.9054847581172525e-18, -18.849555921538755, 59.21762640653615]

&lt;/pre&gt;

&lt;p&gt;
The analytical solution in this case was given as:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-ipython"&gt;[0, -6 * np.pi, 6 * np.pi**2]
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
[0, -18.84955592153876, 59.21762640653615]

&lt;/pre&gt;

&lt;p&gt;
which is evidently the same as our numerical solution.
&lt;/p&gt;

&lt;p&gt;
Maybe an alternative, but more verbose is this vectorized integrate function. We still make temporary functions for integrating, and the vectorization is essentially like the list comprehension above, but we avoid the lambda functions.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-ipython"&gt;&lt;span style="color: #6434A3;"&gt;@np.vectorize&lt;/span&gt;
&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;integrate&lt;/span&gt;(i):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;integrand&lt;/span&gt;(t):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; F(r(t))[i]
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #BA36A5;"&gt;I&lt;/span&gt;, &lt;span style="color: #BA36A5;"&gt;e&lt;/span&gt; = quad(integrand, 0, 2 * np.pi)
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; I

integrate([0, 1, 2])
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
array([ -6.90548476e-18,  -1.88495559e+01,   5.92176264e+01])

&lt;/pre&gt;

&lt;div id="outline-container-org107f13d" class="outline-2"&gt;
&lt;h2 id="org107f13d"&gt;&lt;span class="section-number-2"&gt;1&lt;/span&gt; Summary&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
Once again, autograd provides a convenient way to compute function jacobians which make it easy to evaluate line integrals in Python.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Copyright (C) 2018 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/2018/11/16/Line-integrals-in-Python-with-autograd.org"&gt;org-mode source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Org-mode version = 9.1.14&lt;/p&gt;]]></content:encoded>
    </item>
    <item>
      <title>Adding a GSL integration function to Emacs with a dynamic module</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2017/07/09/Adding-a-GSL-integration-function-to-Emacs-with-a-dynamic-module</link>
      <pubDate>Sun, 09 Jul 2017 07:22:01 EDT</pubDate>
      <category><![CDATA[dynamic-module]]></category>
      <category><![CDATA[emacs]]></category>
      <category><![CDATA[integration]]></category>
      <guid isPermaLink="false">Puc-UXuQT5BoMD9UjDsEBorq17Y=</guid>
      <description>Adding a GSL integration function to Emacs with a dynamic module</description>
      <content:encoded><![CDATA[


&lt;p&gt;
Here we work out how to run this program: &lt;a href="https://www.gnu.org/software/gsl/doc/html/integration.html#adaptive-integration-example"&gt;https://www.gnu.org/software/gsl/doc/html/integration.html#adaptive-integration-example&lt;/a&gt; in a dynamic module in emacs. The goal is to be able to evaluate \(\int_0^1 x^{-1/2} \log(x) dx\). According to the example page the answer is -4. We will define an integration function that takes at least a function and integration bounds as arguments, and several optional arguments to specify tolerances and limits. In other words we want to evaluate integrals of the form:
&lt;/p&gt;

&lt;p&gt;
\(\int_a^b f(x; params) dx\)
&lt;/p&gt;

&lt;p&gt;
I want that to happen in an elisp function with a signature like:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-emacs-lisp"&gt;(gsl-integration-qags (&lt;span style="color: #0000FF;"&gt;lambda&lt;/span&gt; (x params) body) a b &lt;span style="color: #6434A3;"&gt;&amp;amp;optional&lt;/span&gt; params epsabs epsrel limit)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And that function will return a list containing (result error-estimate). Here is the C-code that makes this happen. It is more complex that the &lt;a href="http://kitchingroup.cheme.cmu.edu/blog/2017/07/08/Adding-numerical-methods-to-emacs-with-dynamic-modules/"&gt;last example&lt;/a&gt;, and only compiles with gcc that allows nested functions. I don't know how to write this without that feature. This is more complex also because you have to create a workspace to do the integration inside the function that does the integration. The C-module also has extra code in it to allow for optional arguments.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c"&gt;&lt;span style="color: #808080;"&gt;#include&lt;/span&gt; &lt;span style="color: #008000;"&gt;&amp;lt;gsl/gsl_integration.h&amp;gt;&lt;/span&gt;
&lt;span style="color: #808080;"&gt;#include&lt;/span&gt; &lt;span style="color: #008000;"&gt;"emacs-module.h"&lt;/span&gt;

&lt;span style="color: #6434A3;"&gt;int&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;plugin_is_GPL_compatible&lt;/span&gt;;

&lt;span style="color: #0000FF;"&gt;static&lt;/span&gt; &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #006699;"&gt;F_gsl_integrate&lt;/span&gt; (&lt;span style="color: #6434A3;"&gt;emacs_env&lt;/span&gt; *&lt;span style="color: #BA36A5;"&gt;env&lt;/span&gt;, &lt;span style="color: #6434A3;"&gt;ptrdiff_t&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;nargs&lt;/span&gt;, &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;args&lt;/span&gt;[], &lt;span style="color: #6434A3;"&gt;void&lt;/span&gt; *&lt;span style="color: #BA36A5;"&gt;data&lt;/span&gt;)
{
  &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;nested function - only supported as an extension in gcc&lt;/span&gt;
  &lt;span style="color: #6434A3;"&gt;double&lt;/span&gt; &lt;span style="color: #006699;"&gt;f&lt;/span&gt; (&lt;span style="color: #6434A3;"&gt;double&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;x&lt;/span&gt;, &lt;span style="color: #6434A3;"&gt;void&lt;/span&gt; *&lt;span style="color: #BA36A5;"&gt;params&lt;/span&gt;) 
  {
    &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;fn&lt;/span&gt; = args[0];  &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;function we will integrate&lt;/span&gt;
    &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;x2&lt;/span&gt;[] = { env-&amp;gt;make_float(env, x), params };
    &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;y&lt;/span&gt; = env-&amp;gt;funcall(env, fn, 2, &amp;amp;x2);   
    
    &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; env-&amp;gt;extract_float (env, y);
  }

  &lt;span style="color: #6434A3;"&gt;double&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;a&lt;/span&gt; = env-&amp;gt;extract_float (env, args[1]);
  &lt;span style="color: #6434A3;"&gt;double&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;b&lt;/span&gt; = env-&amp;gt;extract_float (env, args[2]);

  &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;default values for optional arguments&lt;/span&gt;
  &lt;span style="color: #6434A3;"&gt;double&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;epsabs&lt;/span&gt; = 0.0;
  &lt;span style="color: #6434A3;"&gt;double&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;epsrel&lt;/span&gt; = 1e-7;
  &lt;span style="color: #6434A3;"&gt;size_t&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;limit&lt;/span&gt; = 1000;
  &lt;span style="color: #6434A3;"&gt;double&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;result&lt;/span&gt;, &lt;span style="color: #BA36A5;"&gt;error&lt;/span&gt;; 

  &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;Here is how I handle the optional arguments&lt;/span&gt;
  &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;(gsl-integrate func a b params epsabs epsrel limit)&lt;/span&gt;
  &lt;span style="color: #6434A3;"&gt;gsl_function&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;F&lt;/span&gt;;
  F.function = &amp;amp;f;
  &lt;span style="color: #0000FF;"&gt;if&lt;/span&gt; (nargs &amp;gt;= 4) {F.params = args[3];}
  &lt;span style="color: #0000FF;"&gt;if&lt;/span&gt; (nargs &amp;gt;= 5 &amp;amp;&amp;amp; env-&amp;gt;is_not_nil(env, args[4])) {epsabs = env-&amp;gt;extract_float(env, args[4]);}
  &lt;span style="color: #0000FF;"&gt;if&lt;/span&gt; (nargs &amp;gt;= 6 &amp;amp;&amp;amp; env-&amp;gt;is_not_nil(env, args[5])) {epsrel = env-&amp;gt;extract_float(env, args[5]);}
  &lt;span style="color: #0000FF;"&gt;if&lt;/span&gt; (nargs &amp;gt;= 7 &amp;amp;&amp;amp; env-&amp;gt;is_not_nil(env, args[6])) {limit = env-&amp;gt;extract_integer(env, args[6]);}

  &lt;span style="color: #6434A3;"&gt;gsl_integration_workspace&lt;/span&gt; * &lt;span style="color: #BA36A5;"&gt;w&lt;/span&gt; = gsl_integration_workspace_alloc (limit);

  gsl_integration_qags (&amp;amp;F, &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;gsl_function pointer&lt;/span&gt;
                        a, &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;lower integration bound&lt;/span&gt;
                        b, &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;upper integration bound&lt;/span&gt;
                        epsabs, &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;absolute error tolerance&lt;/span&gt;
                        epsrel, &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;relative error tolerance&lt;/span&gt;
                        limit, &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;max number of subintervals for integration&lt;/span&gt;
                        w, &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;the workspace&lt;/span&gt;
                        &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;pointers to put results and error in&lt;/span&gt;
                        &amp;amp;result, &amp;amp;error);

  gsl_integration_workspace_free (w);
    
  &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;make a list of (result error) to return&lt;/span&gt;
  &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;Qlist&lt;/span&gt; = env-&amp;gt;intern(env, &lt;span style="color: #008000;"&gt;"list"&lt;/span&gt;);
  &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;Qresult&lt;/span&gt; = env-&amp;gt;make_float (env, result);
  &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;Qerror&lt;/span&gt; = env-&amp;gt;make_float (env, error);
  &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;list_args&lt;/span&gt;[] = { Qresult, Qerror };
  &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; env-&amp;gt;funcall(env, Qlist, 2, list_args);
}

&lt;span style="color: #6434A3;"&gt;int&lt;/span&gt; &lt;span style="color: #006699;"&gt;emacs_module_init&lt;/span&gt;(&lt;span style="color: #0000FF;"&gt;struct&lt;/span&gt; &lt;span style="color: #6434A3;"&gt;emacs_runtime&lt;/span&gt; *&lt;span style="color: #BA36A5;"&gt;ert&lt;/span&gt;)
{
  &lt;span style="color: #6434A3;"&gt;emacs_env&lt;/span&gt; *&lt;span style="color: #BA36A5;"&gt;env&lt;/span&gt; = ert-&amp;gt;get_environment(ert);
  
  &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;Here we create the function.&lt;/span&gt;
  &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;fset&lt;/span&gt; = env-&amp;gt;intern(env, &lt;span style="color: #008000;"&gt;"fset"&lt;/span&gt;);
  &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;args&lt;/span&gt;[2];
  args[0] = env-&amp;gt;intern(env, &lt;span style="color: #008000;"&gt;"gsl-integration-qags"&lt;/span&gt;); &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;symbol to create for function&lt;/span&gt;
  &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;The function we set that symbol to.&lt;/span&gt;
  args[1] = env-&amp;gt;make_function(env,
                               3, &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;min nargs&lt;/span&gt;
                               7, &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;max nargs&lt;/span&gt;
                               F_gsl_integrate,
                               &lt;span style="color: #008000;"&gt;"(gsl-integration-qags F A B &amp;amp;optional PARAMS EPSABS EPSREL LIMIT)\n"&lt;/span&gt; \
                               &lt;span style="color: #008000;"&gt;"Integrate F(x; params) from A to B.\n"&lt;/span&gt; \
                               &lt;span style="color: #008000;"&gt;"F is a function of a single variable and parameters.\n"&lt;/span&gt; \
                               &lt;span style="color: #008000;"&gt;"A is the lower bound of integration\n"&lt;/span&gt;  \
                               &lt;span style="color: #008000;"&gt;"B is the upper bound of integration.\n"&lt;/span&gt; \
                               &lt;span style="color: #008000;"&gt;"Optional parameters:\n"&lt;/span&gt;\
                               &lt;span style="color: #008000;"&gt;"PARAMS is a list of params to pass to F.\n"&lt;/span&gt; \
                               &lt;span style="color: #008000;"&gt;"EPSABS is a float (default 0.0) and is the absolute error tolerance.\n"&lt;/span&gt; \
                               &lt;span style="color: #008000;"&gt;"EPSREL is a float (default 1e-7) and is the relative error tolerance.\n"&lt;/span&gt; \
                               &lt;span style="color: #008000;"&gt;"LIMIT is the maximum number of subintervals for the integration (default 1000).\n"&lt;/span&gt; \
                               &lt;span style="color: #008000;"&gt;"Returns (list result error-estimate).\n"&lt;/span&gt; \
                               &lt;span style="color: #008000;"&gt;"See https://www.gnu.org/software/gsl/manual/html_node/QAGS-adaptive-integration-with-singularities.html."&lt;/span&gt;,
                               0);
  &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;This is basically (fset 'gsl-integration-qags (lambda func))&lt;/span&gt;
  env-&amp;gt;funcall(env, fset, 2, args);
  
  &lt;span style="color: #8D8D84;"&gt;// &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;This is what allows the shared library to provide a feature &lt;/span&gt;
  &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;provide&lt;/span&gt; = env-&amp;gt;intern(env, &lt;span style="color: #008000;"&gt;"provide"&lt;/span&gt;);
  &lt;span style="color: #6434A3;"&gt;emacs_value&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;provide_args&lt;/span&gt;[] = { env-&amp;gt;intern(env, &lt;span style="color: #008000;"&gt;"gsl-integration"&lt;/span&gt;) };
  env-&amp;gt;funcall(env, provide, 1, provide_args);
  
  &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; 0;
}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Building this was moderately tricky. It appears the first gcc on my path uses clang which does not support nested functions in C. I don't know enough C to figure out how to do this without a nested function though, since the function has to be defined at run-time based on the emacs env and args. gcc does support inline functions, so the code below uses a gcc that does compile it.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-sh"&gt;rm -f gsl-integration.so gsl-integration.o
/usr/local/Cellar/gcc/6.1.0/bin/gcc-6 -Wall -I/usr/local/include -fPIC -c gsl-integration.c
/usr/local/Cellar/gcc/6.1.0/bin/gcc-6  -shared -L/usr/local/include -lgsl -o gsl-integration.so gsl-integration.o
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Now we add this directory to our path since it is not on it and require our new module.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-emacs-lisp"&gt;(add-to-list 'load-path &lt;span style="color: #008000;"&gt;"/Users/jkitchin/vc/blogofile-jkitchin.github.com/_blog/dynamic-module/"&lt;/span&gt;)
(&lt;span style="color: #0000FF;"&gt;require&lt;/span&gt; '&lt;span style="color: #D0372D;"&gt;gsl-integration&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
gsl-integration

&lt;/pre&gt;

&lt;p&gt;
Let us see our new function in action. We evaluate \(\int_0^1 x^{-1/2} \log(x) dx\). According to the example page the answer is -4. Here is an example where we ignore the parameters. You have to be careful; Emacs sometimes segfaults and crashes if you use an integer or float argument when it expects the other type.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-emacs-lisp"&gt;(gsl-integration-qags (&lt;span style="color: #0000FF;"&gt;lambda&lt;/span&gt; (x params) (/ (log x) (sqrt x))) 0.0 1.0)
&lt;/pre&gt;
&lt;/div&gt;

&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;


&lt;colgroup&gt;
&lt;col  class="org-right" /&gt;

&lt;col  class="org-right" /&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="org-right"&gt;-4.000000000000085&lt;/td&gt;
&lt;td class="org-right"&gt;1.354472090042691e-13&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
Here are some optional arguments. 
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-emacs-lisp"&gt;(gsl-integration-qags (&lt;span style="color: #0000FF;"&gt;lambda&lt;/span&gt; (x params) (/ (log x) (sqrt x))) 0.0 1.0 nil nil 0.01)
&lt;/pre&gt;
&lt;/div&gt;

&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;


&lt;colgroup&gt;
&lt;col  class="org-right" /&gt;

&lt;col  class="org-right" /&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="org-right"&gt;-4.000000000000075&lt;/td&gt;
&lt;td class="org-right"&gt;0.019526557540360034&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
Nice, with a larger epsrel argument we get a larger error. Note the arguments are positional, so we have to include them all just to set the epsrel argument. How about an easier example with parameters that we actually use. Here we integrate a constant, and set the value of the constant from the params arg. The integral should be the area of a rectangle of length 1 and width of the param we use.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-emacs-lisp"&gt;(list
 (gsl-integration-qags (&lt;span style="color: #0000FF;"&gt;lambda&lt;/span&gt; (x params) (first params)) 0.0 1.0 '(1.0))
 (gsl-integration-qags (&lt;span style="color: #0000FF;"&gt;lambda&lt;/span&gt; (x params) (first params)) 0.0 1.0 '(0.5)))
&lt;/pre&gt;
&lt;/div&gt;

&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;


&lt;colgroup&gt;
&lt;col  class="org-right" /&gt;

&lt;col  class="org-right" /&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="org-right"&gt;1.0&lt;/td&gt;
&lt;td class="org-right"&gt;1.1102230246251565e-14&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-right"&gt;0.5&lt;/td&gt;
&lt;td class="org-right"&gt;5.551115123125783e-15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
Wow! It actually works!!! That was harder won success than usual for me. I am claiming victory for now and leaving the following notes to future me:
&lt;/p&gt;

&lt;ol class="org-ol"&gt;
&lt;li&gt;It would be nice to have optional keyword arguments. This would take some handling of the arguments beyond what I know how to do for now, unless it is possible to pull in something like plist-get the way we pull in fset, provide and list in this example.&lt;/li&gt;
&lt;li&gt;Error checking on types would be helpful. It is not good for Emacs to crash because 0 is not 0.0!&lt;/li&gt;
&lt;li&gt;In numpy there is often a feature to get full_output. Here, the workspace created in the function has more information available in a struct that might be helpful to have access to at times. It seems like it might be possible to get that here too.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Copyright (C) 2017 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/2017/07/09/Adding-a-GSL-integration-function-to-Emacs-with-a-dynamic-module.org"&gt;org-mode source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Org-mode version = 9.0.7&lt;/p&gt;]]></content:encoded>
    </item>
    <item>
      <title>Numerical Simpsons rule</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/03/08/Numerical-Simpsons-rule</link>
      <pubDate>Fri, 08 Mar 2013 18:18:55 EST</pubDate>
      <category><![CDATA[math]]></category>
      <category><![CDATA[integration]]></category>
      <guid isPermaLink="false">vmtnHrNkUcTa2PmQCOP7XnVGrO0=</guid>
      <description>Numerical Simpsons rule</description>
      <content:encoded><![CDATA[


&lt;p&gt;

A more accurate numerical integration than the trapezoid method is &lt;a href="http://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.simps.html" &gt;Simpson's rule&lt;/a&gt;. The syntax is similar to trapz, but the method is in scipy.integrate.
&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; simps, romb

a = 0.0; b = np.pi / 4.0;
N = 10  &lt;span style="color: #ff0000; font-weight: bold;"&gt;# this is the number of intervals&lt;/span&gt;

x = np.linspace(a, b, N)
y = np.cos(x)

t = np.trapz(y, x)
s = simps(y, x)
a = np.sin(b) - np.sin(a)

&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt;
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; &lt;span style="color: #228b22;"&gt;'trapz = {0} ({1:%} error)'&lt;/span&gt;.format(t, (t - a)/a)
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; &lt;span style="color: #228b22;"&gt;'simps = {0} ({1:%} error)'&lt;/span&gt;.format(s, (s - a)/a)
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; &lt;span style="color: #228b22;"&gt;'analy = {0}'&lt;/span&gt;.format(a)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
&amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt;
trapz = 0.70665798038 (-0.063470% error)
simps = 0.707058914216 (-0.006769% error)
analy = 0.707106781187
&lt;/pre&gt;

&lt;p&gt;
You can see the Simpson's method is more accurate than the trapezoid method.
&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/03/08/Numerical-Simpsons-rule.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
    <item>
      <title>Integrating the Fermi distribution to compute entropy</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/03/06/Integrating-the-Fermi-distribution-to-compute-entropy</link>
      <pubDate>Wed, 06 Mar 2013 09:39:42 EST</pubDate>
      <category><![CDATA[gotcha]]></category>
      <category><![CDATA[integration]]></category>
      <category><![CDATA[dft]]></category>
      <guid isPermaLink="false">8aXB-dJ08KwHSy4vBZ0lsSIkMoU=</guid>
      <description>Integrating the Fermi distribution to compute entropy</description>
      <content:encoded><![CDATA[



&lt;p&gt;
The Fermi distribution is defined by \(f(\epsilon) = \frac{1}{e^{(\epsilon - \mu)/(k T)} + 1}\). This function describes the occupation of energy levels at temperatures above absolute zero. We use this function to compute electronic entropy in a metal, which contains an integral of \(\int n(\epsilon) (f \ln f + (1 - f) \ln (1-f)) d\epsilon\), where \(n(\epsilon)\) is the electronic density of states. Here we plot the Fermi distribution function. It shows that well below the Fermi level the states are fully occupied, and well above the Fermi level, they are unoccupied. Near the Fermi level, the states go from occupied to unoccupied smoothly.
&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

mu = 0
k = 8.6e-5
T = 1000

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;f&lt;/span&gt;(e):
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; 1.0 / (np.exp((e - mu)/(k*T)) + 1)

espan = np.linspace(-10, 10, 200)
plt.plot(espan, f(espan))
plt.ylim([-0.1, 1.1])
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/fermi-entropy-integrand-1.png'&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src="/img/./images/fermi-entropy-integrand-1.png"&gt;&lt;p&gt;

&lt;p&gt;
Let us consider a simple density of states function, just a parabola. This could represent a s-band for example. We will use this function to explore the integral.
&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

mu = 0
k = 8.6e-5
T = 1000

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;f&lt;/span&gt;(e):
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; 1.0 / (np.exp((e - mu)/(k*T)) + 1)

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;dos&lt;/span&gt;(e):
    d = (np.ones(e.shape) - 0.03 * e**2) 
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; d * (d &amp;gt; 0)
espan = np.linspace(-10, 10)

plt.plot(espan, dos(espan), label=&lt;span style="color: #228b22;"&gt;'Total dos'&lt;/span&gt;)
plt.plot(espan, f(espan) * dos(espan), label=&lt;span style="color: #228b22;"&gt;'Occupied states'&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/fermi-entropy-integrand-2.png'&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;p&gt;&lt;img src="/img/./images/fermi-entropy-integrand-2.png"&gt;&lt;p&gt;
Now, we consider the integral to compute the electronic entropy. The entropy is proportional to this integral.
&lt;/p&gt;

&lt;p&gt;
\( \int n(\epsilon) (f \ln f + (1 - f) \ln (1-f)) d\epsilon \)
&lt;/p&gt;

&lt;p&gt;
It looks straightforward to compute, but it turns out there is a wrinkle. Evaluating the integrand leads to &lt;code&gt;nan&lt;/code&gt; elements because the ln(0) is -&amp;infin;. 
&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
mu = 0
k = 8.6e-5
T = 100

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;fermi&lt;/span&gt;(e):
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; 1.0 / (np.exp((e - mu)/(k*T)) + 1)

espan = np.array([-20, -10, -5, 0.0, 5, 10])
f = fermi(espan)

&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; f * np.log(f)
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; (1 - f) * np.log(1 - f)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
[  0.00000000e+000   0.00000000e+000   0.00000000e+000  -3.46573590e-001
  -1.85216532e-250               nan]
[        nan         nan         nan -0.34657359  0.          0.        ]
&lt;/pre&gt;

&lt;p&gt;
In this case, these &lt;code&gt;nan&lt;/code&gt; elements should be equal to zero (x ln(x) goes to zero as x goes to zero). So, we can just ignore those elements in the integral. Here is how to do that.
&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

mu = 0
k = 8.6e-5
T = 1000

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;fermi&lt;/span&gt;(e):
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; 1.0 / (np.exp((e - mu)/(k*T)) + 1)

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;dos&lt;/span&gt;(e):
    d = (np.ones(e.shape) - 0.03 * e**2) 
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; d * (d &amp;gt; 0)

espan = np.linspace(-20, 10)
f = fermi(espan)
n = dos(espan)

g = n * (f * np.log(f) + (1 - f) * np.log(1 - f))

&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; np.trapz(espan, g) &lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;nan because of the nan in the g vector&lt;/span&gt;
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; g

plt.plot(espan, g)
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/fermi-entropy-integrand-3.png'&lt;/span&gt;)

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;find the elements that are not nan&lt;/span&gt;
ind = np.logical_not(np.isnan(g))

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# &lt;/span&gt;&lt;span style="color: #ff0000; font-weight: bold;"&gt;evaluate the integrand for only those points&lt;/span&gt;
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; np.trapz(espan[ind], g[ind])
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
nan
[             nan              nan              nan              nan
              nan              nan              nan              nan
              nan              nan              nan              nan
              nan              nan              nan              nan
              nan              nan              nan              nan
              nan              nan              nan              nan
              nan              nan              nan              nan
  -9.75109643e-14  -1.05987106e-10  -1.04640574e-07  -8.76265644e-05
  -4.92684641e-02  -2.91047740e-01  -7.75652579e-04  -1.00962241e-06
  -1.06972936e-09  -1.00527877e-12  -8.36436686e-16  -6.48930917e-19
  -4.37946336e-22  -2.23285389e-25  -1.88578082e-29   0.00000000e+00
   0.00000000e+00   0.00000000e+00   0.00000000e+00   0.00000000e+00
   0.00000000e+00   0.00000000e+00]
0.208886080897
&lt;/pre&gt;

&lt;p&gt;&lt;img src="/img/./images/fermi-entropy-integrand-3.png"&gt;&lt;p&gt;

&lt;p&gt;
The integrand is pretty well behaved in the figure above. You do not see the full range of the x-axis, because the integrand evaluates to &lt;code&gt;nan&lt;/code&gt; for very negative numbers. This causes the &lt;code&gt;trapz&lt;/code&gt; function to return &lt;code&gt;nan&lt;/code&gt; also. We can solve the problem by only integrating the parts that are not &lt;code&gt;nan&lt;/code&gt;. We have to use numpy.logical&lt;sub&gt;not&lt;/sub&gt; to get an element-wise array of which elements are not &lt;code&gt;nan&lt;/code&gt;. In this example, the integrand is not well sampled, so the area under that curve may not be very accurate. 
&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/03/06/Integrating-the-Fermi-distribution-to-compute-entropy.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
    <item>
      <title>The trapezoidal method of integration</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/02/23/The-trapezoidal-method-of-integration</link>
      <pubDate>Sat, 23 Feb 2013 09:00:00 EST</pubDate>
      <category><![CDATA[math]]></category>
      <category><![CDATA[integration]]></category>
      <guid isPermaLink="false">OoBUv1RqAYMTrxhLlW5Lh-Ll43I=</guid>
      <description>The trapezoidal method of integration</description>
      <content:encoded><![CDATA[


&lt;p&gt;
&lt;a href="http://matlab.cheme.cmu.edu/2011/10/14/the-trapezoidal-method-of-integration/" &gt;Matlab post&lt;/a&gt;

See &lt;a href="http://en.wikipedia.org/wiki/Trapezoidal_rule" &gt;http://en.wikipedia.org/wiki/Trapezoidal_rule&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
$$\int_a^b f(x) dx \approx \frac{1}{2}\displaystyle\sum\limits_{k=1}^N(x_{k+1}-x_k)(f(x_{k+1}) + f(x_k))$$
&lt;/p&gt;

&lt;p&gt;
Let us compute the integral of sin(x) from x=0 to \(\pi\). To approximate the integral, we need to divide the interval from \(a\) to \(b\) into \(N\) intervals. The analytical answer is 2.0.
&lt;/p&gt;

&lt;p&gt;
We will use this example to illustrate the difference in performance between loops and vectorized operations 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;import&lt;/span&gt; time

a = 0.0; b = np.pi;
N = 1000; &lt;span style="color: #ff0000; font-weight: bold;"&gt;# this is the number of intervals&lt;/span&gt;

h = (b - a)/N; &lt;span style="color: #ff0000; font-weight: bold;"&gt;# this is the width of each interval&lt;/span&gt;
x = np.linspace(a, b, N) 
y = np.sin(x); &lt;span style="color: #ff0000; font-weight: bold;"&gt;# the sin function is already vectorized&lt;/span&gt;

t0 = time.time()
f = 0.0
&lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; k &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; &lt;span style="color: #8b0000;"&gt;range&lt;/span&gt;(len(x) - 1):
    f += 0.5 * ((x[k+1] - x[k]) * (y[k+1] + y[k]))

tf = time.time() - t0
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; &lt;span style="color: #228b22;"&gt;'time elapsed = {0} sec'&lt;/span&gt;.format(tf)

&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; f
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
&amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; ... ... &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; time elapsed = 0.0780000686646 sec
&amp;gt;&amp;gt;&amp;gt; 1.99999835177
&lt;/pre&gt;

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

&lt;pre class="src src-python"&gt;t0 = time.time()
Xk = x[1:-1] - x[0:-2] &lt;span style="color: #ff0000; font-weight: bold;"&gt;# vectorized version of (x[k+1] - x[k])&lt;/span&gt;
Yk = y[1:-1] + y[0:-2] &lt;span style="color: #ff0000; font-weight: bold;"&gt;# vectorized version of (y[k+1] + y[k])&lt;/span&gt;

f = 0.5 * np.sum(Xk * Yk) &lt;span style="color: #ff0000; font-weight: bold;"&gt;# vectorized version of the loop above&lt;/span&gt;
tf = time.time() - t0
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; &lt;span style="color: #228b22;"&gt;'time elapsed = {0} sec'&lt;/span&gt;.format(tf)

&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; f
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
&amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; time elapsed = 0.077999830246 sec
&amp;gt;&amp;gt;&amp;gt; 1.99999340709
&lt;/pre&gt;

&lt;p&gt;
In the last example, there may be loop buried in the sum command. Let us do one final method, using linear algebra, in a single line. The key to understanding this is to recognize the sum is just the result of a dot product of the x differences and y sums. 
&lt;/p&gt;

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

&lt;pre class="src src-python"&gt;t0 = time.time()
f = 0.5 * np.dot(Xk, Yk)
tf = time.time() - t0
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; &lt;span style="color: #228b22;"&gt;'time elapsed = {0} sec'&lt;/span&gt;.format(tf)

&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; f
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
&amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; time elapsed = 0.0310001373291 sec
&amp;gt;&amp;gt;&amp;gt; 1.99999340709
&lt;/pre&gt;

&lt;p&gt;
The loop method is straightforward to code, and looks alot like the formula that defines the trapezoid method. the vectorized methods are not as easy to read, and take fewer lines of code to write. However, the vectorized methods are much faster than the loop, so the loss of readability could be worth it for very large problems.
&lt;/p&gt;

&lt;p&gt;
The times here are considerably slower than in Matlab. I am not sure if that is a totally fair comparison. Here I am running python through emacs, which may result in slower performance. I also used a very crude way of timing the performance which lumps some system performance in too.
&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/23/The-trapezoidal-method-of-integration.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
    <item>
      <title>On the quad or trapz'd in ChemE heaven</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/02/02/On-the-quad-or-trapz-d-in-ChemE-heaven</link>
      <pubDate>Sat, 02 Feb 2013 09:00:00 EST</pubDate>
      <category><![CDATA[integration]]></category>
      <category><![CDATA[python]]></category>
      <guid isPermaLink="false">EqaOpRz5kzU1FTJtv24K73sy730=</guid>
      <description>On the quad or trapz'd in ChemE heaven</description>
      <content:encoded><![CDATA[


&lt;p&gt;


&lt;a href="http://matlab.cheme.cmu.edu/2011/09/12/on-the-quad-or-trapzd-in-cheme-heaven/" &gt;Matlab post&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
What is the difference between quad and trapz? The short answer is that quad integrates functions (via a function handle) using numerical quadrature, and trapz performs integration of arrays of data using the trapezoid method.
&lt;/p&gt;

&lt;p&gt;
Let us look at some examples. We consider the example of computing \(\int_0^2 x^3 dx\). the analytical integral is \(1/4 x^4\), so we know the integral evaluates to 16/4 = 4. This will be our benchmark for comparison to the numerical methods.
&lt;/p&gt;

&lt;p&gt;
We use the scipy.integrate.quad command  to evaluate this \(\int_0^2 x^3 dx\).
&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; scipy.integrate &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; quad

ans, err = quad(&lt;span style="color: #8b0000;"&gt;lambda&lt;/span&gt; x: x**3, 0, 2)
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; ans
&lt;/pre&gt;
&lt;/div&gt;

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

&lt;p&gt;
you can also define a function for the integrand.
&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; scipy.integrate &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; quad

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;integrand&lt;/span&gt;(x):
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; x**3

ans, err = quad(integrand, 0, 2)
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; ans
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
4.0
&lt;/pre&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; Numerical data integration&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
if we had numerical data like this, we use trapz to integrate it
&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

x = np.array([0, 0.5, 1, 1.5, 2])
y = x**3

i2 = np.trapz(y, x)

error = (i2 - 4)/4

&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; i2, error
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
4.25 0.0625
&lt;/pre&gt;

&lt;p&gt;
Note the integral of these vectors is greater than 4! You can see why here.
&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
x = np.array([0, 0.5, 1, 1.5, 2])
y = x**3

x2 = np.linspace(0, 2)
y2 = x2**3

plt.plot(x, y, label=&lt;span style="color: #228b22;"&gt;'5 points'&lt;/span&gt;)
plt.plot(x2, y2, label=&lt;span style="color: #228b22;"&gt;'50 points'&lt;/span&gt;)
plt.legend()
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/quad-1.png'&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

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

&lt;p&gt;
The trapezoid method is overestimating the area significantly. With more points, we get much closer to the analytical 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

x2 = np.linspace(0, 2, 100)
y2 = x2**3

&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; np.trapz(y2, x2)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
4.00040812162
&lt;/pre&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; Combining numerical data with quad&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-2"&gt;
&lt;p&gt;
You might want to combine numerical data with the quad function if you want to perform integrals easily. Let us say you are given this data:
&lt;/p&gt;

&lt;p&gt;
x = [0 0.5 1 1.5 2];
y = [0    0.1250    1.0000    3.3750    8.0000];
&lt;/p&gt;

&lt;p&gt;
and you want to integrate this from x = 0.25 to 1.75. We do not have data in those regions, so some interpolation is going to be needed. Here is one approach.
&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; scipy.interpolate &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; interp1d
&lt;span style="color: #8b0000;"&gt;from&lt;/span&gt; scipy.integrate &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; quad
&lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; numpy &lt;span style="color: #8b0000;"&gt;as&lt;/span&gt; np

x = [0, 0.5, 1, 1.5, 2]
y = [0,    0.1250,    1.0000,    3.3750,    8.0000]

f = interp1d(x, y)

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# numerical trapezoid method&lt;/span&gt;
xfine = np.linspace(0.25, 1.75)
yfine = f(xfine)
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; np.trapz(yfine, xfine)

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# quadrature with interpolation&lt;/span&gt;
ans, err = quad(f, 0.25, 1.75)
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; ans
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
2.53199187838
2.53125
&lt;/pre&gt;

&lt;p&gt;
These approaches are very similar, and both rely on linear interpolation. The second approach is simpler, and uses fewer lines of code.
&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; Summary&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-3"&gt;
&lt;p&gt;
trapz and quad are functions for getting integrals. Both can be used with numerical data if interpolation is used. The syntax for the quad and trapz function is different in scipy than in Matlab.
&lt;/p&gt;

&lt;p&gt;
Finally, see this &lt;a href="http://matlab.cheme.cmu.edu/2011/08/30/solving-integral-equations/" &gt;post&lt;/a&gt; for an example of solving an integral equation using quad and fsolve.&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/02/On-the-quad-or-trapz'd-in-ChemE-heaven.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
    <item>
      <title>Integrating equations in python</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/01/20/Integrating-equations-in-python</link>
      <pubDate>Sun, 20 Jan 2013 09:00:00 EST</pubDate>
      <category><![CDATA[integration]]></category>
      <category><![CDATA[python]]></category>
      <guid isPermaLink="false">x_Smua6QCafcMbwEEDQEQxOy_cQ=</guid>
      <description>Integrating equations in python</description>
      <content:encoded><![CDATA[



&lt;p&gt;
A common need in engineering calculations is to integrate an equation over some range to determine the total change. For example, say we know the volumetric flow changes with time according to \(d\nu/dt = \alpha t\), where \(\alpha = 1\) L/min and we want to know how much liquid flows into a tank over 10 minutes if the volumetric flowrate is \(\nu_0 = 5\) L/min at \(t=0\). The answer to that question is the value of this integral: \(V = \int_0^{10} \nu_0 + \alpha t dt\). 
&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; scipy
&lt;span style="color: #8b0000;"&gt;from&lt;/span&gt; scipy.integrate &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; quad

nu0 = 5     &lt;span style="color: #ff0000; font-weight: bold;"&gt;# L/min&lt;/span&gt;
alpha = 1.0 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# L/min&lt;/span&gt;
&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;integrand&lt;/span&gt;(t):
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; nu0 + alpha * t

t0 = 0.0
tfinal = 10.0
V, estimated_error = quad(integrand, t0, tfinal)
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt;(&lt;span style="color: #228b22;"&gt;'{0:1.2f} L flowed into the tank over 10 minutes'&lt;/span&gt;.format(V))
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
100.00 L flowed into the tank over 10 minutes
&lt;/pre&gt;

&lt;p&gt;
That is all there is too it!&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/01/20/Integrating-equations-in-python.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
    <item>
      <title>Integrating a batch reactor design equation</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/01/06/Integrating-a-batch-reactor-design-equation</link>
      <pubDate>Sun, 06 Jan 2013 09:00:00 EST</pubDate>
      <category><![CDATA[integration]]></category>
      <guid isPermaLink="false">S-ZeHf9XdaSncqKZbrn1BFFx3HE=</guid>
      <description>Integrating a batch reactor design equation</description>
      <content:encoded><![CDATA[


&lt;p&gt;
For a constant volume batch reactor where \(A \rightarrow B\) at a rate of \(-r_A = k C_A^2\), we derive the following design equation for the length of time required to achieve a particular level of conversion :
&lt;/p&gt;

&lt;p&gt;
\(t(X) = \frac{1}{k C_{A0}} \int_{X=0}^X \frac{dX}{(1-X)^2}\)
&lt;/p&gt;

&lt;p&gt;
if \(k = 10^{-3}\) L/mol/s and \(C_{A0}\) = 1 mol/L, estimate the time to achieve 90% conversion.
&lt;/p&gt;

&lt;p&gt;
We could analytically solve the integral and evaluate it, but instead we will numerically evaluate it using scipy.integrate.quad. This function returns two values: the evaluated integral, and an estimate of the absolute error in the answer.
&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; scipy.integrate &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; quad

&lt;span style="color: #8b0000;"&gt;def&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;integrand&lt;/span&gt;(X):
    k = 1.0e-3
    Ca0 = 1.0  &lt;span style="color: #ff0000; font-weight: bold;"&gt;# mol/L&lt;/span&gt;
    &lt;span style="color: #8b0000;"&gt;return&lt;/span&gt; 1./(k*Ca0)*(1./(1-X)**2)

sol, abserr = quad(integrand, 0, 0.9)
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; &lt;span style="color: #228b22;"&gt;'t = {0} seconds ({1} hours)'&lt;/span&gt;.format(sol, sol/3600)
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; &lt;span style="color: #228b22;"&gt;'Estimated absolute error = {0}'&lt;/span&gt;.format(abserr)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
t = 9000.0 seconds (2.5 hours)
Estimated absolute error = 2.12203274482e-07
&lt;/pre&gt;


&lt;p&gt;
You can see the estimate error is very small compared to the 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/01/06/Integrating-a-batch-reactor-design-equation.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
  </channel>
</rss>
