Jekyll2023-05-22T06:30:19-05:00https://mitchellt.com/feed.xmlMitchell PaulusHVAC ⋅ Building Commissioning ⋅ Software EngineeringMitchell T. PaulusDerivation of the counterflow effectiveness-NTU relation2023-05-19T00:00:00-05:002023-05-19T00:00:00-05:00https://mitchellt.com/counterflow<p> <strong>Target Audience:</strong> Engineers or engineering students who've got some understanding of Calculus and Thermo. </p> <p> In order to really grasp some engineering concepts, I need to derive them myself, in a way that makes sense to me. One of these concepts is the ε-NTU method for counterflow heat exchangers. </p> <p> The final result that relates a counterflow heat exchanger's effectiveness with $$N_{tu}$$ (or <em>NTU</em>) for those who haven't seen it before is: </p> <p> $\epsilon = \frac{1 - e^{-NTU(1-\frac{C_{min}}{C_{max}})}}{1 - \frac{C_{min}}{C_{max}} e^{-NTU(1 - \frac{C_min{}}{C_{max}} )}}$ </p> <p> So here's a diagram of the setup. We've got two fluids, one hot and one cold, flowing in opposite directions. I've drawn the hot fluid on top going to the right and the cold fluid on the bottom going to the left. This is one dimensional, and the dimension is $$x$$, going to the right. </p> <img src="/assets/img/counterflow.png" alt="counterflow heat exchanger" style="width: 100%;"/> <p> The basic essence of this derivation is that we have two energy balances (equations that look like $$Q=mcΔT$$) and a form of the heat transfer rate (like $$Q=UAΔT$$). </p> <p> For a simple fluid experiencing only heat transfer, we have: </p> <p> $\dot{Q} = \dot{m} c_p \left(T_{out} - T_{in} \right)$ </p> <p> However, I'm going to write it a little differently, using $$x$$ as our variable representing 1-D distance along the heat exchanger. Important to note here is that $$T$$ is a function of $$x$$ (I'm going to color this function syntax red like this for clarity: $$T{\color{red}(x)}$$). </p> <p> $\dot{Q} = \dot{m} c_p \left(T{\color{red}(x + Δx)} - T{\color{red}(x)}\right)$ </p> <p> For reasons that will make more sense later, I'm going to divide both sides by that $$Δx$$. </p> <p> $\frac{\dot{Q}}{Δx} = \dot{m} c_p \left(\frac{T{\color{red} (x + Δx) } - T{\color{red}(x)}}{Δx}\right)$ </p> <p> Now, If you take the limit as $$Δx$$ goes to zero, you get the definition of the derivative of $$T$$ with respect to $$x$$ in that parenthesis. </p> <p> $\lim_{Δx \to 0} \frac{\dot{Q}}{Δx} = \dot{m} c_p \frac{dT}{dx}$ </p> <p> That $$\lim_{Δx \to 0} \dot{Q}/Δx$$ is a little strange, but in the other derivations I've seen, most just call it the "differential" of $$\dot{Q}$$. But we're going to want to give it a name, so I'm going to call it $$\delta{} \dot{Q}$$. </p> <p> $\delta \dot{Q} = \dot{m} c_p \frac{dT}{dx}$ </p> <p> Let's also replace the $$\dot{m} c_p$$ with $$C$$, which is the heat capacity rate of the fluid. We have this formula for both the hot and cold fluids, so we'll subscript them with $$h$$ and $$c$$. </p> <p> \begin{equation}\label{eq:eb} \delta \dot{Q} = C_h \frac{dT_h}{dx} = C_c \frac{dT_c}{dx} \end{equation} </p> <p> At this point, I've not given any focus to the <em>sign</em> of the heat transfer rate. It may seem strange at first, but I'm going to add a negative to the RHS of each of these. Once you look at a plot of the temperature profiles, it will make more sense. </p> <p> As you go from left to right (increasing $$x$$) on the plot, <em>both temperatures are decreasing</em>. This means that the $$dT/dx$$ is <em>negative</em> for both fluids. We'd prefer to deal with a positive heat transfer rate everywhere, so this means that we need to add a negative sign to both equations. </p> <p> $\delta \dot{Q} = -C_h \frac{dT_h}{dx} = -C_c \frac{dT_c}{dx}$ </p> <p> Now we can look at another way to calculate the heat transfer rate. This is simply using the definition of the overall heat transfer coefficient $$U$$. I'm using $$W$$ to represent some constant "width" of the heat exchanger, such that the cross-sectional area is $$A = W Δx$$. </p> <p> I'm also going to assume some length that this heat transfer is happening over, $$x_{0}$$ to $$x_{0} + \Delta x$$. The $$UA$$ value is going to be multiplied by the average temperature difference between the two fluids (using the calculus definition of average). </p> <p> $\dot{Q} = U W Δx \int_{x_{0}}^{x_{0} + \Delta x} \frac{\left(T_{h} {\color{red}(x)}- T_{c}{\color{red}(x)}\right)}{ \Delta x}$ </p> <p> Again, we can divide both sides by $$Δx$$. </p> <p> $\frac{\dot{Q}}{Δx} = U W \int_{x_{0}}^{x_{0} + \Delta x} \frac{\left(T_{h} {\color{red}(x)}- T_{c}{\color{red}(x)}\right)}{ \Delta x}$ </p> <p> But before we take the limit as $$Δx$$ goes to zero, let's evaluate that integral. I'm going to use $$T^{*}$$ to represent some antiderivative of $$T$$. </p> <p> $\frac{\dot{Q}}{Δx} = U W \left( \frac{T^{*}_{h} {\color{red}(x_{0} + \Delta x)} - T^{*}_{h} {\color{red}(x_{0})}}{Δx} - \frac{T^{*}_{c} {\color{red}(x_{0} + \Delta x)} - T^{*}_{c} {\color{red}(x_{0})}}{Δx}\right)$ </p> <p> Now let's take that limit. We can use our definition of $$\delta \dot{Q}$$ to replace the left side. The temperature differences are again in the form of the definition of the derivative. </p> <p> $\delta \dot{Q} = U W \left( \frac{dT^{*}_{h}}{dx} - \frac{dT^{*}_{c}}{dx} \right)$ </p> <p> But $$T^{*}$$ was assumed to be an antiderivative of $$T$$, so we can replace the derivatives with the original functions. </p> <p> \begin{equation}\label{eq:deltaq} \delta \dot{Q} = U W \left(T_{h}{\color{red}(x)}- T_{c}{\color{red}(x)}\right) \end{equation} </p> <p style="font-size: 0.7em"> <em>(Ok, some of you probably didn't need that level of detail to accept the above equation at face value. But I did.)</em> </p> <p> Let's rearrange the energy balance equations, to get a differential of the temperature differences. $$\def\dq{\delta \dot{Q}}$$ </p> <p> $\frac{dT_h}{dx} - \frac{dT_c}{dx} = \left( \frac{1}{-C_h} - \frac{1}{-C_c} \right) \dq$ </p> <p> Differentiation is a linear operator, this means that the difference of derivatives is equal to the derivative of the difference. Also moving some things around on the RHS to deal with the double negative. $$\def\Cdiff{\left( \frac{1}{C_c} - \frac{1}{C_h} \right)}$$ </p> <p> $\frac{d}{dx} \left(T_h - T_c \right) = \left( \frac{1}{C_c} - \frac{1}{C_h} \right) \dq$ </p> <p> We can now substitute \eqref{eq:deltaq} for $$\dq$$ (dropping the function syntax). </p> <p>$\frac{d}{dx} \left(T_h - T_c \right) = \Cdiff U W \left(T_{h} - T_{c}\right)$ </p> <p> Let's move all the temperature differences to the LHS. </p> <p> $\frac{\frac{d}{dx} \left(T_h - T_c \right)}{T_h - T_c} = \Cdiff U W$ </p> <p> I'm going to define a new variable for the temperature difference, $$D$$ (standing for <em>difference</em>). </p> <p> $D = T_h - T_c$ $\frac{1}{D} \frac{dD}{dx} = \Cdiff U W$ </p> <p> Integrating both sides with respect to $$x$$, we get: </p> <p> $\int_1^2 \frac{1}{D} \frac{dD}{dx} dx = \int_1^2 \Cdiff U W dx$ $\left.\ln D \right|_1^2 = \Cdiff U W L$ $\ln D_2 - \ln D_1 = \ln \frac{D_2}{D_1} = \Cdiff U A$ </p> Multiply and divide RHS by $$-C_h$$. <p> $\ln \frac{D_2}{D_1} = \left(1 - \frac{C_h}{C_c} \right) \frac{UA}{-C_h}$ $\ln \frac{D_2}{D_1} = -N_{tu} \left( 1 - \frac{C_h}{C_c} \right)$ </p> <p> Let's bring back the temperatures by replacing the definition of $$D$$. </p> <p> \begin{equation}\label{eq:ln} \ln \frac{T_{h2} - T_{c2}}{T_{h1} - T_{c1}} = -N_{tu} \left( 1 - \frac{C_h}{C_c} \right) \end{equation} </p> <p> Note that we replaced $$\frac{UA}{C_h}$$ with $$N_{tu}$$. $$N_{tu}$$ is defined as $$UA/C_{min}$$, where $$C_{min}$$ is the minimum heat capacity rate of the fluids. </p> <p> At this point, the hard part is done. What remains is algebraic manipulation with the definition of effectiveness. </p> <p> The eventual goal is to get this in terms of effectiveness, ε, removing all the temperatures. The original Kays and London text with this derivation relies on a geometric interpretation that is really nice. However, I'm going to show a different line of algebraic thought that also works. </p> <p> First we need to <em>define</em> effectiveness. We have implicitly assumed that $$C_{h} < C_{c}$$, meaning that the hot flow has a lower heat capacity rate. Whichever fluid has the lower heat capacity rate will have the maximum temperature difference, with the largest possible difference being the difference between the inlet hot temperature and the inlet cold temperature. So effectiveness is defined as the ratio of the actual temperature difference to the maximum possible temperature difference. </p> <p> \begin{equation}\label{eq:eff} ε = \frac{T_{h1} - T_{h2}}{T_{h1} - T_{c2}} \end{equation} </p> <p> In \eqref{eq:ln}, we have differences in temperatures at the same end, 1's with 1's and 2's with 2's. Our effectiveness definition has differences in temperatures at <em>opposite</em> ends, 1's with 2's. The other key insight here is that we know the temperatures differences from end 1 to end 2 are <em>proportional</em>, based on \eqref{eq:eb}. Using \eqref{eq:eb}, we can write: </p> <p> $\frac{dT_h}{dT_c} = \frac{C_c}{C_h} = \frac{T_{h1} - T_{h2}}{T_{c1} - T_{c2}}$ </p> <p> Looking at the numerator of \eqref{eq:ln}, we have temperatures at end 2 ($$T_{h2} - T_{c2}$$). The simplest way to get the temperature differences between ends 1 and 2 is to add and subtract one of the temperatures at end 1, either $$T_{h1}$$ or $$T_{c1}$$. Arbitrarily, let's try adding and subtracting $$T_{h1}$$. </p> <p> $T_{h2} - T_{c2} = T_{h2} - T_{c2} + T_{h1} - T_{h1} = \left( T_{h1} - T_{c2} \right) - \left(T_{h1} - T_{h2}\right)$ </p> <p> This is good because the terms $$\left( T_{h1} - T_{c2} \right)$$ and $$\left(T_{h1} - T_{h2}\right)$$ are both in the definition of effectiveness \eqref{eq:eff}. </p> Doing a similar thing for the bottom, but this time adding and subtracting $$T_{c1}$$. <p> $T_{h1} - T_{c1} = T_{h1} - T_{c1} + T_{c2} - T_{c2} = \left( T_{h1} - T_{c2} \right) - \left(T_{c1} - T_{c2}\right)$ </p> As a reminder, we are trying to simply the fraction on the LHS of \eqref{eq:ln}. <p> $\frac{T_{h2} - T_{c2}}{T_{h1} - T_{c1}} = \frac{\left( T_{h1} - T_{c2} \right) - \left(T_{h1} - T_{h2}\right)}{\left( T_{h1} - T_{c2} \right) - \left(T_{c1} - T_{c2}\right)}$ </p> Hopefully you can see that by dividing the top and bottom by $$\left( T_{h1} - T_{c2} \right)$$, we are going to cancel the first terms in the numerator and denominator and get 1's, and then we'll get an effectiveness term right away on the top. <p> $\frac{\left( T_{h1} - T_{c2} \right) - \left(T_{h1} - T_{h2}\right)}{\left( T_{h1} - T_{c2} \right) - \left(T_{c1} - T_{c2}\right)} = \frac{ \frac{\left( T_{h1} - T_{c2} \right) - \left(T_{h1} - T_{h2}\right)}{ \left( T_{h1} - T_{c2} \right) } }{ \frac{\left( T_{h1} - T_{c2} \right) - \left(T_{c1} - T_{c2}\right)}{ \left( T_{h1} - T_{c2} \right) } } = \frac{1 - ε}{1 - \frac{ T_{c1} - T_{c2}}{ T_{h1} - T_{c2} } }$ </p> <p> We're close now. If that $$T_{c1} - T_{c2}$$ term just had $$h$$'s instead, we'd have another effectiveness term. Fortunately, we have a conversion factor for that, $$\frac{C_c}{C_h}$$. </p> <p> Replace $$T_{c1} - T_{c2}$$ with $$\frac{C_h}{C_c} \left( T_{h1} - T_{h2} \right)$$, and we now have an expression with no temperatures. </p> <p> $\frac{T_{h2} - T_{c2}}{T_{h1} - T_{c1}} = \frac{1 - ε}{1 - \frac{C_h}{C_c} ε}$ </p> <p> Now it's just algebra time - let's substitute this back into \eqref{eq:ln} and solve for $$ε$$. </p> <p> $\ln \left( \frac{1 - ε}{1 - \frac{C_h}{C_c} ε} \right) = -N_{tu} \left( 1 - \frac{C_h}{C_c} \right)$ $\left( \frac{1 - ε}{1 - \frac{C_h}{C_c} ε} \right) = e^{-N_{tu} \left( 1 - \frac{C_h}{C_c} \right) }$ $1 - ε = \left( 1 - \frac{C_h}{C_c} ε \right) e^{-N_{tu} \left( 1 - \frac{C_h}{C_c} \right) }$ $1 - ε = e^{-N_{tu} \left( 1 - \frac{C_h}{C_c} \right) } - \frac{C_h}{C_c} ε e^{-N_{tu} \left( 1 - \frac{C_h}{C_c} \right) }$ $1 - e^{-N_{tu} \left( 1 - \frac{C_h}{C_c} \right) } = ε \left( 1 - \frac{C_h}{C_c} e^{-N_{tu} \left( 1 - \frac{C_h}{C_c} \right) } \right)$ $ε = \frac{1 - e^{-N_{tu} \left( 1 - \frac{C_h}{C_c} \right) }}{1 - \frac{C_h}{C_c} e^{-N_{tu} \left( 1 - \frac{C_h}{C_c} \right) } }$ </p> <p> Now, since our assumption throughout was that $$C_h \lt C_c$$, we can account for that here, and we get the final result that you'll see in your heat transfer textbooks. </p> <p> $\epsilon = \frac{1 - e^{-NTU(1-\frac{C_{min}}{C_{max}})}}{1 - \frac{C_{min}}{C_{max}} e^{-NTU(1 - \frac{C_min{}}{C_{max}} )}}$ </p>Mitchell T. PaulusTarget Audience: Engineers or engineering students who've got some understanding of Calculus and Thermo.What is the value of -2^2?2023-01-28T00:00:00-06:002023-01-28T00:00:00-06:00https://mitchellt.com/unary_minus_and_exponentiation<p>tldr: Depends on who you ask. And watch out for Excel – it takes the minority position that this is 4.</p> <p>This is not meant to be a trick question – it is simply a matter of arbitrary choice in the order of operations.</p> <p>So while <a href="https://en.wikipedia.org/wiki/Order_of_operations#Mnemonics">PEMDAS</a> is pretty well agreed upon, the relationship between <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unary_negation">unary minus</a> and exponentiation is not. Different programming languages or spreadsheets handle it differently.</p> <p>Here’s an example of how I ran into this bug. I was using <a href="https://maxima.sourceforge.io/">Maxima</a>, a free computer algebra system to solve a very complicated set of simultaneous equations. I then was copying this symbolic solution to a programming language of my own that I’ve been developing for engineering. I evaluated the expressions in both, and one of the evaluated expressions was completely different between the two.</p> <p>I knew that the evaluated Maxima solution was correct based on the value. I then tried to copy the expression in Excel.</p> <p>What then shocked me was that Excel matched <em>my</em> solution!</p> <p>After evaluating what seemed like a thousand sub expressions, I found the culprit: <code class="language-plaintext highlighter-rouge">-a^b</code>.</p> <p>It turns out I had decided (without any thought) that unary minus should bind more tightly than exponentiation. Maxima, and from what I can tell, most other languages, has exponentiation at a higher precedence.</p> <p>I proceeded to check how every language that I use handles this. The only two that evaluated to 4 were Excel and the <code class="language-plaintext highlighter-rouge">math</code> function in the fish shell.</p> <p>Programs that evaluate <code class="language-plaintext highlighter-rouge">-2^2</code> to 4:</p> <ul> <li>Excel</li> <li><a href="https://fishshell.com/docs/current/cmds/math.html"><code class="language-plaintext highlighter-rouge">math</code></a> <a href="https://fishshell.com/">(fish shell)</a></li> </ul> <p>Programs that evaluate <code class="language-plaintext highlighter-rouge">-2^2</code> to -4:</p> <ul> <li><a href="https://maxima.sourceforge.io/">Maxima</a></li> <li><a href="https://en.wikipedia.org/wiki/AWK">awk</a></li> <li><a href="https://www.python.org/">Python</a></li> <li><a href="https://www.lua.org">lua</a></li> <li><a href="https://en.wikipedia.org/wiki/Almquist_shell#dash">POSIX shell (dash)</a></li> <li><a href="https://www.typescriptlang.org/">JavaScript/TypeScript</a></li> <li><a href="https://www.mathworks.com/products/matlab.html">MATLAB</a></li> <li><a href="https://www.haskell.org/">Haskell</a></li> <li><a href="https://julialang.org/">Julia</a></li> </ul> <p>Languages that this doesn’t apply to:</p> <ul> <li><a href="https://dotnet.microsoft.com/en-us/languages/csharphttps://dotnet.microsoft.com/en-us/languages/csharp">C#</a>: Uses <code class="language-plaintext highlighter-rouge">Math.Pow</code> method</li> </ul> <p>As it appears <code class="language-plaintext highlighter-rouge">-2^2</code> is generally evaluated to <code class="language-plaintext highlighter-rouge">-4</code>, I updated my programming language grammar to match. What this does mean though, is that you have to be careful in formula generation or copying and pasting expression like this into Excel.</p>Mitchell T. Paulustldr: Depends on who you ask. And watch out for Excel – it takes the minority position that this is 4.WSL, Neovim, Lua, and the Windows Clipboard2022-05-15T00:00:00-05:002022-05-15T00:00:00-05:00https://mitchellt.com/2022/05/15/WSL-Neovim-Lua-and-the-Windows-Clipboard<p>tl;dr</p> <p><code class="language-plaintext highlighter-rouge">init.lua</code></p> <div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">in_wsl</span> <span class="o">=</span> <span class="nb">os.getenv</span><span class="p">(</span><span class="s1">'WSL_DISTRO_NAME'</span><span class="p">)</span> <span class="o">~=</span> <span class="kc">nil</span> <span class="k">if</span> <span class="n">in_wsl</span> <span class="k">then</span> <span class="n">vim</span><span class="p">.</span><span class="n">g</span><span class="p">.</span><span class="n">clipboard</span> <span class="o">=</span> <span class="p">{</span> <span class="n">name</span> <span class="o">=</span> <span class="s1">'wsl clipboard'</span><span class="p">,</span> <span class="n">copy</span> <span class="o">=</span> <span class="p">{</span> <span class="p">[</span><span class="s2">"+"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"clip.exe"</span> <span class="p">},</span> <span class="p">[</span><span class="s2">"*"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"clip.exe"</span> <span class="p">}</span> <span class="p">},</span> <span class="n">paste</span> <span class="o">=</span> <span class="p">{</span> <span class="p">[</span><span class="s2">"+"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"nvim_paste"</span> <span class="p">},</span> <span class="p">[</span><span class="s2">"*"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"nvim_paste"</span> <span class="p">}</span> <span class="p">},</span> <span class="n">cache_enabled</span> <span class="o">=</span> <span class="kc">true</span> <span class="p">}</span> <span class="k">end</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">nvim_paste</code> shell script:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span> <span class="c"># Check for win32yank.exe executable</span> <span class="k">if </span><span class="nb">command</span> <span class="nt">-v</span> win32yank.exe <span class="o">&gt;</span>/dev/null 2&gt;/dev/null<span class="p">;</span> <span class="k">then</span> <span class="c"># The --lf option pastes data unix style. Which is what I almost always want.</span> win32yank.exe <span class="nt">-o</span> <span class="nt">--lf</span> <span class="k">else</span> <span class="c"># Else rely on PowerShell being installed and available.</span> powershell.exe Get-Clipboard | <span class="nb">tr</span> <span class="nt">-d</span> <span class="s1">'\r'</span> | <span class="nb">sed</span> <span class="nt">-z</span> <span class="s1">'$s/\n$//'</span> <span class="k">fi</span> </code></pre></div></div> <p>The <a href="https://docs.microsoft.com/en-us/windows/wsl/about">Windows Subsystem for Linux (WSL)</a> has been great, however, there are often WSL specific tweaks that need to be made to make critical functionality possible.</p> <p>One of those critical functions is the clipboard interaction.</p> <p>In Neovim, a <em>clipboard provider</em> handles this interaction (see <code class="language-plaintext highlighter-rouge">:h clipboard</code>). There is a well-defined order of preference for finding this provider.</p> <ul> <li><code class="language-plaintext highlighter-rouge">g:clipboard</code> global variable</li> <li><code class="language-plaintext highlighter-rouge">pbcopy</code>, <code class="language-plaintext highlighter-rouge">pbpaste</code> (macOS)</li> <li><code class="language-plaintext highlighter-rouge">wl-copy</code>, <code class="language-plaintext highlighter-rouge">wl-paste</code> (if <code class="language-plaintext highlighter-rouge">$WAYLAND_DISPLAY</code> is set)</li> <li><code class="language-plaintext highlighter-rouge">xclip</code> (if <code class="language-plaintext highlighter-rouge">$DISPLAY</code> is set)</li> <li><code class="language-plaintext highlighter-rouge">xsel</code> (if <code class="language-plaintext highlighter-rouge">$DISPLAY</code> is set)</li> <li><code class="language-plaintext highlighter-rouge">lemonade</code> (for SSH) <a href="https://github.com/pocke/lemonade">https://github.com/pocke/lemonade</a></li> <li><code class="language-plaintext highlighter-rouge">doitclient</code> (for SSH) <a href="http://www.chiark.greenend.org.uk/~sgtatham/doit/">http://www.chiark.greenend.org.uk/~sgtatham/doit/</a></li> <li><code class="language-plaintext highlighter-rouge">win32yank</code> (Windows)</li> <li><code class="language-plaintext highlighter-rouge">termux</code> (via <code class="language-plaintext highlighter-rouge">termux-clipboard-set</code>, <code class="language-plaintext highlighter-rouge">termux-clipboard-set</code>)</li> <li><code class="language-plaintext highlighter-rouge">tmux</code> (if <code class="language-plaintext highlighter-rouge">$TMUX</code> is set)</li> </ul> <p>Things were working fine until something changed, and the <code class="language-plaintext highlighter-rouge">$DISPLAY</code> variable started getting set by some program. Following the order of preference above, Neovim attempted to use <code class="language-plaintext highlighter-rouge">xclip</code> as the clipboard provider. As there is no X-server out of the box in the WSL, <code class="language-plaintext highlighter-rouge">xclip</code> doesn’t work.</p> <p>So the solution is to explicitly define what I want in the global variable <code class="language-plaintext highlighter-rouge">g:clipboard</code>.</p> <p>There are several examples of setting this variable in the <em>Vim script</em> environment, but I didn’t come across much for the Lua environment for Neovim.</p> <p>The first task is defining what external commands I want to use for interacting with the Windows clipboard from the WSL.</p> <p>Between the copying or pasting operation, the choice for copying is simpler. Windows comes with the command line executable <code class="language-plaintext highlighter-rouge">clip.exe</code> which takes standard input and puts it in the clipboard. It works great. However, there is no counterpart executable for pasting data from the clipboard to standard output.</p> <p>In the list of preferences above, there is <code class="language-plaintext highlighter-rouge">win32yank</code> listed for Windows. <code class="language-plaintext highlighter-rouge">win32yank</code> is a quite simple copy/paste utility written in Rust (<a href="https://github.com/equalsraf/win32yank">GitHub</a>). Of course, since it’s written in Rust, it is <em>blazingly fast</em>. So if this executable is available, I’d prefer to use that.</p> <p>However, my setup can change such that <code class="language-plaintext highlighter-rouge">win32yank</code> may not be available. The always available backup choice in Windows is PowerShell.</p> <p>The cmdlet to paste from the Windows clipboard to standard output in PowerShell is <code class="language-plaintext highlighter-rouge">Get-Clipboard</code>:</p> <pre><code class="language-cmd">powershell.exe Get-Clipboard </code></pre> <p>The only issue is that it will add an extra newline that is not desired.</p> <p>The other tweak to these pasting commands is that I’m almost always wanting to paste into documents that have Unix newlines.</p> <p>For <code class="language-plaintext highlighter-rouge">win32yank</code> you can specify that the pasted data comes out with Unix newlines using the <code class="language-plaintext highlighter-rouge">--lf</code> option. With the PowerShell command, I just pipe the data through <code class="language-plaintext highlighter-rouge">tr -d '\r'</code> to remove all the carriage returns.</p> <p>With our desired pasting programs and logic determined, I put it together in a simple shell script that looks like this:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span> <span class="c"># Check for win32yank.exe executable</span> <span class="k">if </span><span class="nb">command</span> <span class="nt">-v</span> win32yank.exe <span class="o">&gt;</span>/dev/null 2&gt;/dev/null<span class="p">;</span> <span class="k">then</span> <span class="c"># The --lf option pastes data unix style. Which is what I almost always want.</span> win32yank.exe <span class="nt">-o</span> <span class="nt">--lf</span> <span class="k">else</span> <span class="c"># Else rely on PowerShell being installed and available.</span> powershell.exe Get-Clipboard | <span class="nb">tr</span> <span class="nt">-d</span> <span class="s1">'\r'</span> | <span class="nb">sed</span> <span class="nt">-z</span> <span class="s1">'$ s/\n$//'</span> <span class="k">fi</span> </code></pre></div></div> <p>I call this script <code class="language-plaintext highlighter-rouge">nvim_paste</code>, and it lives in my dotfiles so that it’s available on my <code class="language-plaintext highlighter-rouge">$PATH</code>.</p> <p>So in summary, we’ll use <code class="language-plaintext highlighter-rouge">clip.exe</code> for copying and <code class="language-plaintext highlighter-rouge">nvim_paste</code> for pasting.</p> <h3 id="gclipboard-and-initlua"><code class="language-plaintext highlighter-rouge">g:clipboard</code> and <code class="language-plaintext highlighter-rouge">init.lua</code></h3> <p>The final step is to correctly set the <code class="language-plaintext highlighter-rouge">g:clipboard</code> variable to use these commands. I use Lua for my Neovim configuration. So again, there is a bit of translation to get this to work.</p> <p>The first thing to do is check whether I’m in a WSL environment. None of this matters unless that is true. On my home Linux machines, I want to use <code class="language-plaintext highlighter-rouge">xclip</code>.</p> <p>To determine whether I’m in a WSL environment, I check for the presence of the environment variable <code class="language-plaintext highlighter-rouge">WSL_DISTRO_NAME</code>.</p> <div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">in_wsl</span> <span class="o">=</span> <span class="nb">os.getenv</span><span class="p">(</span><span class="s1">'WSL_DISTRO_NAME'</span><span class="p">)</span> <span class="o">~=</span> <span class="kc">nil</span> </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">g:clipboard</code> variable is Vim script dictionary. In Lua, the analogous data type is the table. The keys to set are <code class="language-plaintext highlighter-rouge">'name'</code>, <code class="language-plaintext highlighter-rouge">'copy'</code>, <code class="language-plaintext highlighter-rouge">'paste'</code>, and <code class="language-plaintext highlighter-rouge">'cache_enabled'</code>. For <code class="language-plaintext highlighter-rouge">'copy'</code> and <code class="language-plaintext highlighter-rouge">'paste'</code>, the values are dictionaries themselves, with keys <code class="language-plaintext highlighter-rouge">'+'</code> and <code class="language-plaintext highlighter-rouge">'*'</code>. For Windows, we don’t need to worry about the difference in the <code class="language-plaintext highlighter-rouge">'+'</code> and <code class="language-plaintext highlighter-rouge">'*'</code>.</p> <p>We can access the global vim variables through the <code class="language-plaintext highlighter-rouge">vim.g</code> dictionary.</p> <p>Putting it all together, the final Lua code that is in my <code class="language-plaintext highlighter-rouge">init.lua</code> is:</p> <div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">in_wsl</span> <span class="o">=</span> <span class="nb">os.getenv</span><span class="p">(</span><span class="s1">'WSL_DISTRO_NAME'</span><span class="p">)</span> <span class="o">~=</span> <span class="kc">nil</span> <span class="k">if</span> <span class="n">in_wsl</span> <span class="k">then</span> <span class="n">vim</span><span class="p">.</span><span class="n">g</span><span class="p">.</span><span class="n">clipboard</span> <span class="o">=</span> <span class="p">{</span> <span class="n">name</span> <span class="o">=</span> <span class="s1">'wsl clipboard'</span><span class="p">,</span> <span class="n">copy</span> <span class="o">=</span> <span class="p">{</span> <span class="p">[</span><span class="s2">"+"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"clip.exe"</span> <span class="p">},</span> <span class="p">[</span><span class="s2">"*"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"clip.exe"</span> <span class="p">}</span> <span class="p">},</span> <span class="n">paste</span> <span class="o">=</span> <span class="p">{</span> <span class="p">[</span><span class="s2">"+"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"nvim_paste"</span> <span class="p">},</span> <span class="p">[</span><span class="s2">"*"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"nvim_paste"</span> <span class="p">}</span> <span class="p">},</span> <span class="n">cache_enabled</span> <span class="o">=</span> <span class="kc">true</span> <span class="p">}</span> <span class="k">end</span> </code></pre></div></div> <p>You can find my entire <code class="language-plaintext highlighter-rouge">init.lua</code> on <a href="https://github.com/mitchpaulus/dotfiles/blob/master/.config/nvim/init.lua">GitHub</a>.</p>Mitchell T. Paulustl;drNotes from working with the NREL Building Component Library API2022-01-22T00:00:00-06:002022-01-22T00:00:00-06:00https://mitchellt.com/2022/01/22/working-with-the-bcl<p>I was programming against the <a href="https://bcl.nrel.gov/">Building Component Library from NREL</a>, and ran into a few gotchas that I’d like to share to make the next person’s experience easier.</p> <h2 id="required-request-headers">Required Request Headers</h2> <p>If you send an HTTP request with just the URL, you will be hit with a 500 status code, representing an internal server error.</p> <p>After some trial and error, it seems the only required header is the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept"><code class="language-plaintext highlighter-rouge">Accept</code> header</a>. It did not seem to matter what was used, other than that it exists. The actual format sent back depends on the URL – XML and JSON are supported, with JSON being the default. So I’ve used an <code class="language-plaintext highlighter-rouge">Accept</code> header, with a value of <code class="language-plaintext highlighter-rouge">application/json</code>, since I prefer JSON to XML.</p> <p>Other headers that weren’t required, but didn’t hurt to send along are the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host"><code class="language-plaintext highlighter-rouge">Host</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding"><code class="language-plaintext highlighter-rouge">Accept-Encoding</code></a>.</p> <h2 id="gzipped-response">Gzipped Response</h2> <p>For me, even without the <code class="language-plaintext highlighter-rouge">Accept-Encoding</code> header, the content is sent back in a compressed form, <code class="language-plaintext highlighter-rouge">gzip</code>.</p> <p>So to properly read the response, you’ll need to uncompress. The response header <code class="language-plaintext highlighter-rouge">Content-Encoding</code> does properly indicate that the response is gzipped.</p> <p>So here’s how that looks in C#. This is a sample method for looking up a BCL component or measure by UUID.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="kt">string</span> <span class="nf">GetByUUID</span><span class="p">(</span><span class="kt">string</span> <span class="n">uuid</span><span class="p">)</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">client</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">HttpClient</span><span class="p">();</span> <span class="n">client</span><span class="p">.</span><span class="n">DefaultRequestHeaders</span><span class="p">.</span><span class="n">Host</span> <span class="p">=</span> <span class="s">"bcl.nrel.gov"</span><span class="p">;</span> <span class="n">client</span><span class="p">.</span><span class="n">DefaultRequestHeaders</span><span class="p">.</span><span class="n">Accept</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">MediaTypeWithQualityHeaderValue</span><span class="p">(</span><span class="s">"application/json"</span><span class="p">));</span> <span class="n">client</span><span class="p">.</span><span class="n">DefaultRequestHeaders</span><span class="p">.</span><span class="n">AcceptEncoding</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">StringWithQualityHeaderValue</span><span class="p">(</span><span class="s">"gzip"</span><span class="p">));</span> <span class="n">HttpResponseMessage</span> <span class="n">response</span> <span class="p">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">GetAsync</span><span class="p">(</span><span class="s">"https://bcl.nrel.gov/api/search/?fq=uuid:"</span> <span class="p">+</span> <span class="n">uuid</span><span class="p">,</span> <span class="n">HttpCompletionOption</span><span class="p">.</span><span class="n">ResponseContentRead</span><span class="p">).</span><span class="n">Result</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">StatusCode</span> <span class="p">!=</span> <span class="n">HttpStatusCode</span><span class="p">.</span><span class="n">OK</span><span class="p">)</span> <span class="p">{</span> <span class="kt">string</span> <span class="n">message</span> <span class="p">=</span> <span class="s">$"Did not receive an OK back from bcl.nrel.gov HTTP request. Response items:\nStatus Code: </span><span class="p">{</span><span class="n">response</span><span class="p">.</span><span class="n">StatusCode</span><span class="p">}</span><span class="s">\nReason: </span><span class="p">{</span><span class="n">response</span><span class="p">.</span><span class="n">ReasonPhrase</span><span class="p">}</span><span class="s">\nHeaders: </span><span class="p">{</span><span class="n">response</span><span class="p">.</span><span class="n">Headers</span><span class="p">}</span><span class="s">"</span><span class="p">;</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">HttpRequestException</span><span class="p">(</span><span class="n">message</span><span class="p">);</span> <span class="p">}</span> <span class="n">Stream</span> <span class="n">responseStream</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="n">Headers</span><span class="p">.</span><span class="nf">TryGetValues</span><span class="p">(</span><span class="s">"Content-Encoding"</span><span class="p">,</span> <span class="k">out</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">values</span><span class="p">)</span> <span class="p">&amp;&amp;</span> <span class="n">values</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"gzip"</span><span class="p">))</span> <span class="p">{</span> <span class="c1">// Uncompress gzip response</span> <span class="n">responseStream</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">GZipStream</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">ReadAsStream</span><span class="p">(),</span> <span class="n">CompressionMode</span><span class="p">.</span><span class="n">Decompress</span><span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="n">responseStream</span> <span class="p">=</span> <span class="n">response</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">ReadAsStream</span><span class="p">();</span> <span class="p">}</span> <span class="n">StreamReader</span> <span class="n">reader</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span><span class="n">responseStream</span><span class="p">,</span> <span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">);</span> <span class="kt">string</span> <span class="n">allContents</span> <span class="p">=</span> <span class="n">reader</span><span class="p">.</span><span class="nf">ReadToEnd</span><span class="p">();</span> <span class="k">return</span> <span class="n">allContents</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div>Mitchell T. PaulusI was programming against the Building Component Library from NREL, and ran into a few gotchas that I’d like to share to make the next person’s experience easier.Keyboard shortcut to insert date in fish shell2021-05-30T00:00:00-05:002021-05-30T00:00:00-05:00https://mitchellt.com/2021/05/30/keyboard-shortcut-insert-date-fish-shell<p>When naming files, I often use the current ISO formatted date at the beginning. For example, if today is May 30, 2021, I would name a file something like: <code class="language-plaintext highlighter-rouge">2021-05-30_my_file.txt</code>.</p> <p>Having the date in an ISO format like this means that the files can be easily sorted by time. This works because the date format goes from largest time interval (year) to smallest (day) from left to right.</p> <p>The only issue with this is that it is often annoying to type this in manually.</p> <p>I have solved this issue in different ways in the past. I’ve used custom keyboard macros from specialty keyboards, I’ve used <a href="https://www.autohotkey.com/">AutoHotkey</a>, but I wanted something more native, especially when the in the shell, which I most often am.</p> <p>Fortunately, for the <a href="https://fishshell.com/">fish shell</a>, I can easily bind the insertion of <em>any text</em> to a keyboard shortcut.</p> <p>The way this is done is with the <a href="https://fishshell.com/docs/current/cmds/commandline.html"><code class="language-plaintext highlighter-rouge">commandline</code></a> built-in command. This command allows you to modify the current command line buffer contents however you please.</p> <p>The way this is often used is within a fish function, one that you bind to a keystroke. For this example, what I want to do is insert the output of the command:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">date</span> <span class="s1">'+%Y-%m-%d'</span> </code></pre></div></div> <p>to my current cursor position.</p> <p>All this takes is four lines of code.</p> <pre><code class="language-fish">function add_date commandline -i (date '+%Y-%m-%d') end bind \ed add_date </code></pre> <p>The <code class="language-plaintext highlighter-rouge">bind \ed</code> portion is binding this to <code class="language-plaintext highlighter-rouge">ALT-d</code>. Every terminal is its own little snowflake and so if you are binding a command to something in fish using the CTRL, ALT, WINDOWS, CMD, META, etc. key, I encourage you to use the command <a href="https://fishshell.com/docs/current/cmds/fish_key_reader.html"><code class="language-plaintext highlighter-rouge">fish_key_reader</code></a> to check what key sequence your shell is actually receiving from your terminal emulator.</p> <p>I’m sure there are some analogous methods to do something similar in other shells like bash, but fish makes it quite simple.</p>Mitchell T. PaulusWhen naming files, I often use the current ISO formatted date at the beginning. For example, if today is May 30, 2021, I would name a file something like: 2021-05-30_my_file.txt.Deriving a Dew Point Temperature Approximation Formula2020-11-22T00:00:00-06:002020-11-22T00:00:00-06:00https://mitchellt.com/2020/11/22/deriving-a-dew-point-temperature-approximation-formula<p>I was recently posed the following question: How do you derive the following approximation for the dew point temperature?</p> $T_{dp} = \frac{4030\left( T_{db} + 235 \right)}{ 4030 - \left(T_{db} + 235 \right) \ln \phi } - 235$ <p>Here the temperatures are in degrees Centigrade (°C) and relative humidity is a value between 0 and 1.</p> <p>I had come across this formula in other contexts, but had not seen a reference or derviation of it. So I saw this as a challenge to determine the origins of this.</p> <p>Not only will I present the mechanical derivation, but I’ll try to share some of the problem solving strategy that went along with it.</p> <p>The first thing to notice is that the formula is a function of the dry bulb temperature and relative humidity. The dry bulb temperature is a fundamental measurement, but we do have a definition for relative humidity ($$\phi$$).</p> <p>\begin{equation}\label{eq:relative_humidity} \phi = \frac{p_w}{p_{ws}} \end{equation}</p> <p>Since our formula is for dew point temperature, we’ll also need the definition for that. It’s the temperature at which the associated water vapor saturation pressure is the same as the current (usually not saturated) water vapor pressure. This means:</p> <p>\begin{equation}\label{eq:dew_point_temp_def} p_{ws @ T_{dp}} = p_{w} \end{equation}</p> <p>We can substitute the $$p_{w}$$ from Equation \eqref{eq:dew_point_temp_def} into the numerator of Equation \eqref{eq:relative_humidity}.</p> <p>\begin{equation} \phi = \frac{p_{ws @ T_{dp}} }{p_{ws @ T_{db}}} \label{eq:relative_humidity_2} \end{equation}</p> <p>So another way to look at the relative humidity is the ratio between the saturated water vapor pressure at the dew point temperature versus the dry bulb temperature.</p> <p>At this point, we need to find the correct formulation for the water vapor saturation pressure. This is the most difficult part as it requires some intuition as to what to search for.</p> <p>If you look at <a href="https://en.wikipedia.org/wiki/Vapour_pressure_of_water">the Wikipedia page for ‘Vapor Pressure of Water’</a>, you’ll see lots of different formulations. They are all approximations with varying accuracy in different temperature ranges.</p> <p>To find the right approximation for this derivation, I searched for a formula that had at least one of the constants in the original formula, 4030 or 235. I was especially looking for something that had the temperature portion plus 235 ($$T + 235$$), as that is a clear pattern in the original formula.</p> <p>On the Wikipedia page, the Antoine equation approximation for the saturation pressure had a $$C + T$$ term where the $$C$$ value was listed as being 233.426 between 1 and 99°C, and 244.485°C between 100°C and 374°C. That’s awfully close to the 235 value I was looking for. The Antoine equation has the form</p> <p>\begin{equation} \log_{10} ( p_{ws} ) = A - \frac{B}{T + C} \end{equation}</p> <p>This looks like the form to try. So I then started searching for ‘Antoine equation 235’ in internet searches.</p> <p>I came across a paper <em>The Use of Dew-Point Temperature in Humidity Calculations</em> by Lawrence A. Wood, published in 1970. In that paper, in section 4, it states (this paper used $$e$$ for saturation pressure):</p> <blockquote> <p>When $$e_w$$ is expressed in millimeters of mercury and $$T$$ in degrees Celsius, the constants for water between 0 and 60°C have been evaluated by Dreisbach [13a, 13b] as</p> <p>$$A=8.10765$$,</p> <p>$$B=1450.286 (°C)^{-1}$$,</p> <p>$$C=235.0°C$$,</p> </blockquote> <p>There’s the 235 we were looking for! Now this was a reference to other original publications. It appears the original references for these constants were:</p> <p><strong>Dreisbach, R.R. <em>Physical Properties of Chemical Compunds, Vol. III</em>, pg. 474 Advances in Chemistry Series No. 29. American Chemical Society, Washingtion, 1961.</strong></p> <p><strong>Lange, N.A. <em>Handbook of Chemistry, 10th Edition</em>, pg. 1438. McGraw-Hill, New York, 1961.</strong></p> <p>Here’s what that actually looks like in the <em>Physical Properties of Chemical Compunds, Vol. III</em> reference. The constants for the Antoine equation are in the red rectangle.</p> <p><img src="/assets/img/antoine_equation_constants.png" alt="Antoine Equation Constants" /></p> <p>So the Antoine equation for water vapor saturation pressure that will be used to derive the original formula is</p> <p>\begin{equation} \log_{10} ( p_{ws} ) = A - \frac{B}{T + C} = 8.10765 - \frac{1750.286}{T + 235.0} \end{equation}</p> <p>where $$p_{ws}$$ is in units of mmHg and $$T$$ is in °C.</p> <p>The formula for Antoine equation uses a logarithm to base 10, instead of the natural logarithm in the equations that we are trying to derive. We need to change from base 10 to base $$e$$,</p> <p>The change of base logarithm rule states:</p> $\ln(x) = \ln(10) \log_{10} (x)$ <p>So we can multiply both sides of the Antoine equation by $$ln(10)$$ to make the left hand side change from $$log_{10} ( p_{ws} )$$ to $$ln( p_{ws})$$.</p> $\log_{10} ( p_{ws} ) \ln(10) = \ln( p_{ws} ) = \ln(10) \left(8.10765 - \frac{1750.286}{T + 235.0}\right) = 18.67 - \frac{4030}{T+235}$ <p>So the 4030 comes from $$ln(10)$$ (which is approximately 2.3026) multiplied by the $$B$$ constant 1750.286.</p> <p>The Antoine equation is in a logarithm form, so the partial pressure can be explicitly solved for by exponentiating both sides.</p> <p>\begin{equation} \ln( p_{ws} ) = 18.67 - \frac{4030}{T+235} \end{equation} \begin{equation} p_{ws} = e^{18.67 - \frac{4030}{T+235} } \end{equation}</p> <p>Now we can substitute this into the original relative humidity formula (Equation \eqref{eq:relative_humidity_2}), and use algebra to solve for the dew point temperature and derive the original equation.</p> $\phi = \frac{p_w}{p_{ws}} = \frac{p_{ws @ T_{dp}} }{p_{ws}}$ $\phi = \frac{e^{18.67 - \frac{4030}{T_{dp}+235}}}{e^{18.67 - \frac{4030}{T_{db}+235}}}$ <p>Using laws of exponents, and canceling $$e^{18.67}$$</p> $\phi = \frac{e^{18.67 - \frac{4030}{T_{dp}+235}}}{e^{18.67 - \frac{4030}{T_{db}+235}}} = \frac{e^{18.67}e^{\frac{-4030}{T_{dp}+235}}}{e^{18.67}e^{\frac{-4030}{T_{db}+235}}} = \frac{e^{\frac{-4030}{T_{dp}+235}}}{e^{\frac{-4030}{T_{db}+235}}}$ <p>Take natural logarithm of both sides</p> $\ln(\phi) = \ln \left(\frac{e^{\frac{-4030}{T_{dp}+235}}}{e^{\frac{-4030}{T_{db}+235}}} \right)$ <p>Using logarithm rules</p> $\ln(\phi) = \ln(e^{\frac{-4030}{T_{dp}+235}}) - \ln( e^{\frac{-4030}{T_{db}+235}} ) = \frac{-4030}{T_{dp}+235} - \frac{-4030}{T_{db}+235}$ <p>Now solve for $$T_{dp}$$</p> $\ln(\phi) = \frac{-4030}{T_{dp}+235} - \frac{-4030}{T_{db}+235}$ $\frac{-4030}{T_{dp}+235} = \ln(\phi) - \frac{4030}{T_{db}+235}$ $T_{dp}+235 = \frac{-4030}{\ln(\phi) - \frac{4030}{T_{db}+235} }$ $T_{dp}+235 = \frac{-4030 \left(T_{db}+235 \right) }{\ln(\phi)\left(T_{db}+235 \right) - 4030 }$ $T_{dp}+235 = \frac{4030 \left(T_{db}+235 \right) }{ 4030 - \ln(\phi)\left(T_{db}+235 \right) }$ $T_{dp} = \frac{4030 \left(T_{db}+235 \right) }{ 4030 - \left(T_{db}+235 \right)\ln(\phi) } - 235$ <p>And the derivation is complete.</p>Mitchell T. PaulusI was recently posed the following question: How do you derive the following approximation for the dew point temperature?Hard-coded data validation list in Excel2020-09-06T00:00:00-05:002020-09-06T00:00:00-05:00https://mitchellt.com/2020/09/06/excel_data_validation_list<p>There are many times in which data validation is useful in Excel. If you’re sharing a spreadsheet with others or using data as input to another set of functions, it can save you a great deal of debugging time.</p> <p>If you are using the <code class="language-plaintext highlighter-rouge">List</code> validation type, the usual way it is shown is to have the items you want in the dropdown list to be in some range in the Excel Workbook. But if you don’t want to hide helper cells, you <em>can</em> enter your desired list options directly through the dialog.</p> <p>What you have to do is enter your items in the “Source:” location, comma separated. I’m not sure there is a way to escape a comma if you wanted that to be a part of the options, but that should be rare.</p> <p><img src="/assets/img/excel_list_data_validation.png" alt="Data Validation Dialog Box" /></p> <p>See <a href="https://docs.microsoft.com/en-us/office/vba/api/excel.validation.add">https://docs.microsoft.com/en-us/office/vba/api/excel.validation.add</a> and search for ‘comma’ in the <code class="language-plaintext highlighter-rouge">xlValidateList</code> validation type. You’ll see it in the definition for the <code class="language-plaintext highlighter-rouge">Formula1</code> parameter, giving you a sense of how these options are parsed behind the scenes. I wish Microsoft would have put “Range or Comma-delimited list” after “Source:” to let you know this. But I wouldn’t hold your breath waiting for Microsoft Office dialog boxes to update.</p>Mitchell T. PaulusThere are many times in which data validation is useful in Excel. If you’re sharing a spreadsheet with others or using data as input to another set of functions, it can save you a great deal of debugging time.SQL Query for number of records in all database tables2020-08-29T00:00:00-05:002020-08-29T00:00:00-05:00https://mitchellt.com/2020/08/29/database_record_counts<p>There have been several occasions in which I’ve been given SQL Server database backups to explore from various building automation systems or other building related software. An important first step in understanding what the structure of the database is to have a count of the number of records in the various tables.</p> <p>Oftentimes, the databases that I’m examining are related to trended building automation system data. It’s likely that the raw data is stored in one of the tables, and that table is going to have many more records than any other.</p> <p>There is not a way using SQL Server Management Studio (that I know of) to get the number of records across each table using the program interface directly.</p> <p>For a single table, you can count the records using a query like:</p> <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="p">[</span><span class="n">TableName</span><span class="p">]</span> </code></pre></div></div> <p>All I would need is a list of tables, and then print these queries out from something like a for loop. And in order to combine individual queries, you can use the <code class="language-plaintext highlighter-rouge">UNION</code> SQL statement. Adding an alias for the column headers, the final query we want looks like:</p> <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="s1">'Table Name 1'</span> <span class="k">as</span> <span class="p">[</span><span class="k">Table</span> <span class="n">Name</span><span class="p">],</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">as</span> <span class="p">[</span><span class="k">Count</span><span class="p">]</span> <span class="k">FROM</span> <span class="p">[</span><span class="n">TableName1</span><span class="p">]</span> <span class="k">UNION</span> <span class="k">SELECT</span> <span class="s1">'Table Name 2'</span> <span class="k">as</span> <span class="p">[</span><span class="k">Table</span> <span class="n">Name</span><span class="p">],</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">as</span> <span class="p">[</span><span class="k">Count</span><span class="p">]</span> <span class="k">FROM</span> <span class="p">[</span><span class="n">TableName2</span><span class="p">]</span> <span class="k">UNION</span> <span class="k">SELECT</span> <span class="s1">'Table Name 3'</span> <span class="k">as</span> <span class="p">[</span><span class="k">Table</span> <span class="n">Name</span><span class="p">],</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">as</span> <span class="p">[</span><span class="k">Count</span><span class="p">]</span> <span class="k">FROM</span> <span class="p">[</span><span class="n">TableName3</span><span class="p">]</span> </code></pre></div></div> <p>To get a list of all the tables, I used the script export feature of SQL Server Management Studio.</p> <p><img src="/assets/img/ssms_generate_scripts.png" alt="SSMS Context Menu" /></p> <p>You can then click through the wizard and get your data into a file.</p> <p>I then had a file that contained my table names, although it obviously is only a small portion of the file. See below for an example portion of the file.</p> <p><img src="/assets/img/ssms_sql_output.png" alt="SSMS Script" /></p> <p>In situations like this, I’ll reach for trusty <code class="language-plaintext highlighter-rouge">awk</code> and pattern match the table names out, along with printing my query.</p> <p>The <code class="language-plaintext highlighter-rouge">awk</code> program looks something like:</p> <div class="language-awk highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/awk -E</span> <span class="c1"># This function transforms the output from a SQL schema dump from</span> <span class="c1"># SQL Server Management Studio into a sql query that counts the number</span> <span class="c1"># of records for each table in the database.</span> <span class="kr">BEGIN</span> <span class="p">{</span> <span class="kc">FS</span><span class="o">=</span><span class="kc">OFS</span><span class="o">=</span><span class="s2">"."</span> <span class="p">}</span> <span class="sr">/CREATE TABLE/</span> <span class="p">{</span> <span class="nb">match</span><span class="p">(</span><span class="nv">$2</span><span class="p">,</span> <span class="sr">/</span><span class="se">$</span><span class="sr">.*</span><span class="se">$</span><span class="sr">/</span><span class="p">)</span> <span class="nx">table_name</span> <span class="o">=</span> <span class="nb">substr</span><span class="p">(</span><span class="nv">$2</span><span class="p">,</span> <span class="nx">RSTART</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">RLENGTH</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="nx">line</span><span class="p">[</span><span class="o">++</span><span class="nx">n</span><span class="p">]</span> <span class="o">=</span> <span class="nb">sprintf</span><span class="p">(</span><span class="s2">"SELECT '%s' as [Table Name], COUNT(*) as Count FROM [%s]"</span><span class="p">,</span> <span class="nx">table_name</span><span class="p">,</span> <span class="nx">table_name</span><span class="p">)</span> <span class="p">}</span> <span class="kr">END</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">n</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="k">print</span> <span class="nx">line</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="s2">" UNION"</span> <span class="p">}</span> <span class="k">print</span> <span class="nx">line</span><span class="p">[</span><span class="nx">n</span><span class="p">]</span> <span class="p">}</span> </code></pre></div></div> <p>Let me explain the important parts of this script.</p> <p><code class="language-plaintext highlighter-rouge">#!/usr/bin/awk -E</code>. This is called a <a href="https://en.wikipedia.org/wiki/Shebang_(Unix)">shebang</a>. It allows me to call the script without explicitly using <code class="language-plaintext highlighter-rouge">awk -f</code>.</p> <p>The generated script output has lines with our table names that look like:</p> <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="p">[</span><span class="n">dbo</span><span class="p">].[</span><span class="n">Ahus</span><span class="p">](</span> </code></pre></div></div> <p>I need the part <code class="language-plaintext highlighter-rouge">[Ahus]</code>. I don’t care about anything before the ‘<code class="language-plaintext highlighter-rouge">.</code>’ So I’m setting the field separator (<code class="language-plaintext highlighter-rouge">FS</code>) to be the period.</p> <p>I won’t explain everything about <code class="language-plaintext highlighter-rouge">awk</code>, but the lines</p> <div class="language-awk highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">/</span><span class="nx">CREATE</span> <span class="nx">TABLE</span><span class="o">/</span> <span class="p">{</span> <span class="nb">match</span><span class="p">(</span><span class="nv">$2</span><span class="p">,</span> <span class="sr">/</span><span class="se">$</span><span class="sr">.*</span><span class="se">$</span><span class="sr">/</span><span class="p">)</span> <span class="nx">table_name</span> <span class="o">=</span> <span class="nb">substr</span><span class="p">(</span><span class="nv">$2</span><span class="p">,</span> <span class="nx">RSTART</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">RLENGTH</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="nx">line</span><span class="p">[</span><span class="o">++</span><span class="nx">n</span><span class="p">]</span> <span class="o">=</span> <span class="nb">sprintf</span><span class="p">(</span><span class="s2">"SELECT '%s' as [Table Name], COUNT(*) as Count FROM [%s]"</span><span class="p">,</span> <span class="nx">table_name</span><span class="p">,</span> <span class="nx">table_name</span><span class="p">)</span> <span class="p">}</span> </code></pre></div></div> <p>will run the code in the braces on lines that have <code class="language-plaintext highlighter-rouge">CREATE TABLE</code> in them. It matches the second field for text in square brackets, extracts the name portion without the brackets using <code class="language-plaintext highlighter-rouge">substr</code>, and then stores the SQL query line that we want into an array called <code class="language-plaintext highlighter-rouge">line</code>.</p> <p>Then once we’ve gone through all the lines (the <code class="language-plaintext highlighter-rouge">END</code> pattern), we can print out each line with a <code class="language-plaintext highlighter-rouge">UNION</code> at the end, <em>except</em> the final line.</p> <p>Then we can run this query and get the output we wanted.</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">awk</span> <span class="nt">-f</span> table_record_count.awk ssms_output.sql </code></pre></div></div> <p>You can get a live copy of this script in my dotfiles repository <a href="https://github.com/mitchpaulus/dotfiles/blob/master/scripts/table_record_count.awk">here</a>.</p>Mitchell T. PaulusThere have been several occasions in which I’ve been given SQL Server database backups to explore from various building automation systems or other building related software. An important first step in understanding what the structure of the database is to have a count of the number of records in the various tables.Building LibreCAD on Manjaro/Arch Linux2020-04-04T00:00:00-05:002020-04-04T00:00:00-05:00https://mitchellt.com/2020/04/04/building-librecad-on-arch<p>tl;dr Maybe just try the package in the AUR?</p> <p>I’m working on building up a drawing set for my new home. I didn’t want to purchase any 2D CAD software for this, as my needs were quite minimal. Just needed the basics, shapes, text, layers, colors, etc.</p> <p>I came across <a href="https://www.librecad.org/">LibreCAD</a>, which was exactly what I was looking for. It’s free and open source so I ran with it.</p> <p>The computer I was looking to install it on runs Manjaro, which uses the Arch Linux package repository. I checked, and it wasn’t in the official repositories.</p> <p>So I decided I would go ahead and build from the source.</p> <p>LibreCAD has 3 main dependencies:</p> <ol> <li>Qt 5.2.1+ framework</li> <li>Boost</li> <li>muparser</li> </ol> <p><a href="https://www.qt.io/">Qt</a> is the main UI framework, and that package was available to install using:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>pacman <span class="nt">-S</span> qt5-base </code></pre></div></div> <p><a href="https://www.boost.org/">Boost</a> is a set of C++ libraries for many core programming tasks. This was also available in the Arch repositories. It happened to already be installed on my computer, likely because it is a common dependency for many programs. The packages that I would have likely installed were:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>pacman <span class="nt">-S</span> boost <span class="nb">sudo </span>pacman <span class="nt">-S</span> boost-libs </code></pre></div></div> <p>The final dependency was <a href="https://beltoforion.de/article.php?a=muparser">muparser</a>, a library for mathematical calculations. I did not find a package in the main repositories. So I went to build that from the source, available on <a href="https://github.com/beltoforion/muparser">GitHub</a>.</p> <p>To install, I didn’t actually clone the repository. You have to download one of the releases to make sure that it comes with the <code class="language-plaintext highlighter-rouge">configure</code> script for installation. If you do that, then you should be able to follow the directions from the <code class="language-plaintext highlighter-rouge">INSTALL.txt</code> file, which goes like:</p> <blockquote> <p>muParser can be installed just extracting the sources somewhere and then, from a terminal, typing:</p> </blockquote> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">cd</span> <span class="o">[</span>path to muParser] ./configure <span class="o">[</span><span class="nt">--enable-shared</span><span class="o">=</span><span class="nb">yes</span>/no] <span class="o">[</span><span class="nt">--enable-samples</span><span class="o">=</span><span class="nb">yes</span>/no] <span class="o">[</span><span class="nt">--enable-debug</span><span class="o">=</span><span class="nb">yes</span>/no] make <span class="o">[</span><span class="nb">sudo</span><span class="k">*</span><span class="o">]</span> make <span class="nb">install</span> <span class="o">[</span><span class="nb">sudo</span><span class="k">*</span><span class="o">]</span> ldconfig <span class="nb">cd </span>samples/example1 ./example1 </code></pre></div></div> <p>The last part with <code class="language-plaintext highlighter-rouge">example1</code> actually failed for me. But the <code class="language-plaintext highlighter-rouge">make</code>, <code class="language-plaintext highlighter-rouge">make install</code>, and <code class="language-plaintext highlighter-rouge">ldconfig</code> commands all succeeded.</p> <h2 id="building-librecad">Building LibreCAD</h2> <p>With those dependencies now installed, we can attempt to install LibreCAD.</p> <p>There are a couple of instructions in various places that are all slightly different. However, what worked for me was the two step process of:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qmake make <span class="nt">-j4</span> </code></pre></div></div> <p>There is going to be a <em>lot</em> of text printed to the console during the compilation process and it takes a few minutes. But it did successfully run. I then opened the program with:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./unix/librecad &amp; </code></pre></div></div> <p>and it worked.</p> <p>But then I realized I never checked the AUR to see if someone had already bundled all of this together in a package. Looks like someone did at: https://aur.archlinux.org/packages/librecad-git.</p> <p>Probably a much better idea 🙃</p>Mitchell T. Paulustl;dr Maybe just try the package in the AUR?Copying text to the clipboard: tmux, WSL, Windows Terminal2020-04-01T00:00:00-05:002020-04-01T00:00:00-05:00https://mitchellt.com/2020/04/01/copying-from-tmux-wsl-windows-terminal<p>tl;dr</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span><span class="nt">-shell</span> <span class="nt">-b</span> <span class="s1">'test -n "$WSLENV"'</span> <span class="s1">'bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel clip.exe'</span> <span class="k">if</span><span class="nt">-shell</span> <span class="nt">-b</span> <span class="s1">'test -n "$WSLENV"'</span> <span class="s1">'bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel clip.exe'</span> </code></pre></div></div> <p>I have been using the Windows Subsystem for Linux (WSL) for approximately 2 years and it has tremendously affected my daily workflow in the most positive of ways. Having access to a Unix shell and the Gnu core utilities opened up ways to process data and complete my engineering work that was not possible before.</p> <p>With that said, since it is not the common environment that many of the tools were developed in, certain tasks need some tweaking in order to get working correctly. One of those things is copying text to the clipboard from within <code class="language-plaintext highlighter-rouge">tmux</code>.</p> <p><code class="language-plaintext highlighter-rouge">tmux</code> has a built in ‘copy mode’. However, that support expects that an X server is running in the background, which is not the case for most of the distributions available through the WSL. Fortunately, newer versions of <code class="language-plaintext highlighter-rouge">tmux</code> have the ability to specify a command to pipe the data through.</p> <p>The command in <code class="language-plaintext highlighter-rouge">tmux</code> is <code class="language-plaintext highlighter-rouge">copy-pipe</code> (and variants) and has the following syntax, check the man page for exact commands in your version.</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>copy-pipe &lt;<span class="nb">command</span><span class="o">&gt;</span> <span class="o">[</span>&lt;prefix&gt;] copy-pipe-no-clear &lt;<span class="nb">command</span><span class="o">&gt;</span> <span class="o">[</span>&lt;prefix&gt;] copy-pipe-and-cancel &lt;<span class="nb">command</span><span class="o">&gt;</span> <span class="o">[</span>&lt;prefix&gt;] </code></pre></div></div> <p>If we were in a Linux distribution, we’d probably have <code class="language-plaintext highlighter-rouge">xclip</code> or <code class="language-plaintext highlighter-rouge">xsel</code> available for us. This is not the case in Windows. What we do have is <code class="language-plaintext highlighter-rouge">clip.exe</code>. This a Windows executable, but it does what we need: Take data piped into it and copy it to the clipboard.</p> <p>You can do this in <code class="language-plaintext highlighter-rouge">cmd.exe</code> like</p> <pre><code class="language-cmd">echo hello | clip.exe </code></pre> <p>You can run Windows executables in the WSL. So we’ll want to rebind the <kbd>Enter</kbd> key press when in copy mode (I use the vi copy mode bindings, if you are strange and want to use emacs mode, this would just be ‘copy-mode’ instead of ‘copy-mode-vi’). This looks like:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bind-key <span class="nt">-T</span> copy-mode-vi Enter send-keys <span class="nt">-X</span> copy-pipe-and-cancel clip.exe </code></pre></div></div> <p>That is a lot and it works, but there’s more to be done.</p> <p>My <code class="language-plaintext highlighter-rouge">tmux</code> configuration is shared between many computers that are running various OS’s. At the time of writing, we have two work computers running Windows, one Manjaro, and one Arch Linux. I don’t want to mess us the default settings for my Linux systems.</p> <p>Again, newer versions of <code class="language-plaintext highlighter-rouge">tmux</code> have a way to handle that. There is the <code class="language-plaintext highlighter-rouge">if-shell</code> command that allows you to execute commands based on the exit code of a shell command. So I wanted to test whether I was in a WSL environment before running the new <code class="language-plaintext highlighter-rouge">bind-key</code> command.</p> <p>There are different ways to do this, but I did a <code class="language-plaintext highlighter-rouge">printenv</code> to see if there were any environment variables that were related to the WSL. It turns out there was. There was an environment variable called <code class="language-plaintext highlighter-rouge">WSLENV</code>, and it doesn’t matter what it is or what it does, just that it was there.</p> <p>So we then use a <code class="language-plaintext highlighter-rouge">test</code> command (the command used to branch should be POSIX, so no Bash-isms) to check for this variable. The <code class="language-plaintext highlighter-rouge">-n</code> checks for a non-zero length string. Notice that you quote the entire commands.</p> <p>This ends up looking like</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span><span class="nt">-shell</span> <span class="nt">-b</span> <span class="s1">'test -n "$WSLENV"'</span> <span class="s1">'bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel clip.exe'</span> </code></pre></div></div> <p>On Windows, I have been using the <a href="https://github.com/Microsoft/Terminal">Windows Terminal</a>. I have liked it, and they are making great progress towards a 1.0 release.</p> <p>One of the features they just recently implemented was mouse support. So now, I can turn on the mouse functionality in <code class="language-plaintext highlighter-rouge">tmux</code> with</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">set</span> <span class="nt">-g</span> mouse on </code></pre></div></div> <p>and I can copy to clipboard using the mouse drag by just changing the ‘key’ from my previous <code class="language-plaintext highlighter-rouge">bind-key</code> command to <code class="language-plaintext highlighter-rouge">MouseDragEnd1Pane</code>.</p> <p>So in all, it looked like:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span><span class="nt">-shell</span> <span class="nt">-b</span> <span class="s1">'test -n "$WSLENV"'</span> <span class="s1">'bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel clip.exe'</span> <span class="k">if</span><span class="nt">-shell</span> <span class="nt">-b</span> <span class="s1">'test -n "$WSLENV"'</span> <span class="s1">'bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel clip.exe'</span> </code></pre></div></div>Mitchell T. Paulustl;dr