Jekyll2024-03-13T21:30:39-05:00https://mitchellt.com/feed.xmlMitchell PaulusHVAC ⋅ Building Commissioning ⋅ Software EngineeringMitchell T. PaulusViewing SharePoint Storage2024-03-13T00:00:00-05:002024-03-13T00:00:00-05:00https://mitchellt.com/viewing-sharepoint-storage<p>SharePoint storage is relatively expensive. As of 2024-03-05 at our company, it’s on the order of $20/100 Gb/month.
So it pays to keep an eye on the storage.</p>
<p>However, it’s not a guarantee that Microsoft makes investigating what document libraries and files are taking up the most space easy.</p>
<p>If you search for ways to investigate this storage, you’ll find UI screenshots of pages that no longer exist and random PowerShell scripts.</p>
<p>Here’s a trick that should continue to work.
There’s a URL for each SharePoint site that provides UI showing a file and folder size breakdown, including versioning.</p>
<p>Just append <code class="language-plaintext highlighter-rouge">_layouts/15/storman.aspx</code> to the base SharePoint site URL.</p>
<p>For example, if your site home page is,
<code class="language-plaintext highlighter-rouge">https://mybusiness.sharepoint.com/jobs</code>, go to the URL <code class="language-plaintext highlighter-rouge">https://mybusiness.sharepoint.com/jobs/_layouts/15/storman.aspx</code>.</p>
<p>You should see something like:</p>
<p><img src="/assets/img/SharePoint_Storage.png" alt="SharePoint storage breakdown" /></p>
<p>From there, you can drill down deeper into the directory tree.</p>Mitchell T. PaulusSharePoint storage is relatively expensive. As of 2024-03-05 at our company, it’s on the order of $20/100 Gb/month. So it pays to keep an eye on the storage.Derivation 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">></span>/dev/null 2>/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">></span>/dev/null 2>/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"><</span><span class="kt">string</span><span class="p">></span> <span class="n">values</span><span class="p">)</span> <span class="p">&&</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"><</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 &
</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?