<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://shsjxzh.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://shsjxzh.github.io/" rel="alternate" type="text/html" /><updated>2025-09-28T16:12:41-04:00</updated><id>https://shsjxzh.github.io/feed.xml</id><title type="html">Zihao Xu (徐 子昊)</title><subtitle>PhD student at Rutgers</subtitle><author><name>Zihao Xu</name><email>zihao.xu@rutgers.edu</email></author><entry><title type="html">Introducing Nodariety.jl</title><link href="https://shsjxzh.github.io/posts/2022/08/nodariety" rel="alternate" type="text/html" title="Introducing Nodariety.jl" /><published>2021-08-18T00:00:00-04:00</published><updated>2021-08-18T00:00:00-04:00</updated><id>https://shsjxzh.github.io/posts/2022/08/nodariety</id><content type="html" xml:base="https://shsjxzh.github.io/posts/2022/08/nodariety"><![CDATA[<p>A few months ago, Jerry was idly musing and, much in the vein of summer-camp-type word games, asked me what the longest string of hyphenated scientific ideas I could come up with was, wherein the second name of one theory would be the first of the next, and so on. One example of such a chain: Euler-Cauchy, Cauchy-Riemann, Riemann-Roch.</p>

<p>He meant it as a fun idle topic of conversation, but somehow I couldn’t get this idea out of my head, and ended up running with it…and running…and running…and now I’ve built <a href="https://github.com/rkurchin/Nodariety.jl">Nodariety.jl</a>. It contains a <em>ton</em> of data I curated detailing a huge list of hyphenated theories (or theorems, or experiments, etc. etc.) and structuring this data into a <a href="https://en.wikipedia.org/wiki/Directed_graph">directed graph</a>.</p>

<p>In this post, I’ll give some details on what the package does, some results of my own playing around with this really fun hobby project, and also ways you can contribute if you’re so inclined! And for the answer to the original question of what the longest path was, read on… 😉</p>

<h3 id="what-do-you-get">What do you get?</h3>
<p>The package does a few things:</p>
<ol>
  <li>Defines a <code class="language-plaintext highlighter-rouge">HyphenGraph</code> type, which is <code class="language-plaintext highlighter-rouge">&lt;:LightGraphs.AbstractGraph</code>, stores the graph itself as a <code class="language-plaintext highlighter-rouge">MetaGraphs.MetaDiGraph</code> object, as well as the node and edge data as <code class="language-plaintext highlighter-rouge">DataFrame</code>s.</li>
  <li>Defines a default instance of this type, exported as the variable <code class="language-plaintext highlighter-rouge">hg</code>, which as of this writing, includes 446 nodes and 383 edges.</li>
  <li>Exports a function for visualizing the graph with nodes and edges colored and labeled according to various pieces of data (see some examples of this below).</li>
  <li>Exports some graph analysis and demographic plotting functions (again, examples below).</li>
</ol>

<p>The package is registered in the Julia General registry, so you can install it easily via <code class="language-plaintext highlighter-rouge">] add Nodariety</code> and have all of these things in your own REPL to play around with! 😀</p>

<h3 id="playing-around">Playing around!</h3>
<p>Here’s a first visualization of the graph, resulting from the command <code class="language-plaintext highlighter-rouge">plot_graph(node_color_prop="birth_year", edge_color_prop = "year")</code>. It uses one unified color scheme, ranging from red to green, to indicate the birth years of people at the nodes, and the year of reference for the theories on the edges:
<img src="/images/blog/2021-08-18-nodariety/big_ugly_graph.png" alt="" /></p>

<p>There are a lot of isolated clusters here, which make things hard to reason about. Let’s get just the largest connected cluster…</p>
<div class="language-julia highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">julia</span><span class="o">&gt;</span> <span class="n">cs</span> <span class="o">=</span> <span class="n">get_clusters</span><span class="x">()</span> <span class="c"># get all connected clusters</span>
 <span class="x">[</span><span class="mi">1</span><span class="x">,</span> <span class="mi">4</span><span class="x">,</span> <span class="mi">6</span><span class="x">,</span> <span class="mi">12</span><span class="x">,</span> <span class="mi">15</span><span class="x">,</span> <span class="mi">18</span><span class="x">,</span> <span class="mi">21</span><span class="x">,</span> <span class="mi">26</span><span class="x">,</span> <span class="mi">27</span><span class="x">,</span> <span class="mi">28</span>  <span class="n">…</span>  <span class="mi">419</span><span class="x">,</span> <span class="mi">427</span><span class="x">,</span> <span class="mi">428</span><span class="x">,</span> <span class="mi">430</span><span class="x">,</span> <span class="mi">435</span><span class="x">,</span> <span class="mi">437</span><span class="x">,</span> <span class="mi">441</span><span class="x">,</span> <span class="mi">443</span><span class="x">,</span> <span class="mi">444</span><span class="x">,</span> <span class="mi">446</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">13</span><span class="x">,</span> <span class="mi">57</span><span class="x">,</span> <span class="mi">80</span><span class="x">,</span> <span class="mi">106</span><span class="x">,</span> <span class="mi">113</span><span class="x">,</span> <span class="mi">121</span><span class="x">,</span> <span class="mi">124</span><span class="x">,</span> <span class="mi">165</span><span class="x">,</span> <span class="mi">200</span><span class="x">,</span> <span class="mi">239</span>  <span class="n">…</span>  <span class="mi">298</span><span class="x">,</span> <span class="mi">314</span><span class="x">,</span> <span class="mi">315</span><span class="x">,</span> <span class="mi">325</span><span class="x">,</span> <span class="mi">333</span><span class="x">,</span> <span class="mi">340</span><span class="x">,</span> <span class="mi">350</span><span class="x">,</span> <span class="mi">359</span><span class="x">,</span> <span class="mi">381</span><span class="x">,</span> <span class="mi">403</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">2</span><span class="x">,</span> <span class="mi">19</span><span class="x">,</span> <span class="mi">55</span><span class="x">,</span> <span class="mi">101</span><span class="x">,</span> <span class="mi">125</span><span class="x">,</span> <span class="mi">140</span><span class="x">,</span> <span class="mi">146</span><span class="x">,</span> <span class="mi">190</span><span class="x">,</span> <span class="mi">201</span><span class="x">,</span> <span class="mi">203</span><span class="x">,</span> <span class="mi">205</span><span class="x">,</span> <span class="mi">218</span><span class="x">,</span> <span class="mi">276</span><span class="x">,</span> <span class="mi">305</span><span class="x">,</span> <span class="mi">351</span><span class="x">,</span> <span class="mi">358</span><span class="x">,</span> <span class="mi">374</span><span class="x">,</span> <span class="mi">423</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">8</span><span class="x">,</span> <span class="mi">9</span><span class="x">,</span> <span class="mi">16</span><span class="x">,</span> <span class="mi">88</span><span class="x">,</span> <span class="mi">149</span><span class="x">,</span> <span class="mi">170</span><span class="x">,</span> <span class="mi">254</span><span class="x">,</span> <span class="mi">287</span><span class="x">,</span> <span class="mi">328</span><span class="x">,</span> <span class="mi">352</span><span class="x">,</span> <span class="mi">367</span><span class="x">,</span> <span class="mi">383</span><span class="x">,</span> <span class="mi">398</span><span class="x">,</span> <span class="mi">399</span><span class="x">,</span> <span class="mi">402</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">32</span><span class="x">,</span> <span class="mi">51</span><span class="x">,</span> <span class="mi">151</span><span class="x">,</span> <span class="mi">171</span><span class="x">,</span> <span class="mi">187</span><span class="x">,</span> <span class="mi">221</span><span class="x">,</span> <span class="mi">230</span><span class="x">,</span> <span class="mi">275</span><span class="x">,</span> <span class="mi">310</span><span class="x">,</span> <span class="mi">311</span><span class="x">,</span> <span class="mi">343</span><span class="x">,</span> <span class="mi">408</span><span class="x">,</span> <span class="mi">413</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">63</span><span class="x">,</span> <span class="mi">112</span><span class="x">,</span> <span class="mi">152</span><span class="x">,</span> <span class="mi">216</span><span class="x">,</span> <span class="mi">225</span><span class="x">,</span> <span class="mi">229</span><span class="x">,</span> <span class="mi">265</span><span class="x">,</span> <span class="mi">332</span><span class="x">,</span> <span class="mi">382</span><span class="x">,</span> <span class="mi">396</span><span class="x">,</span> <span class="mi">442</span><span class="x">,</span> <span class="mi">445</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">5</span><span class="x">,</span> <span class="mi">127</span><span class="x">,</span> <span class="mi">175</span><span class="x">,</span> <span class="mi">179</span><span class="x">,</span> <span class="mi">181</span><span class="x">,</span> <span class="mi">346</span><span class="x">,</span> <span class="mi">363</span><span class="x">,</span> <span class="mi">373</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">10</span><span class="x">,</span> <span class="mi">47</span><span class="x">,</span> <span class="mi">83</span><span class="x">,</span> <span class="mi">184</span><span class="x">,</span> <span class="mi">263</span><span class="x">,</span> <span class="mi">300</span><span class="x">,</span> <span class="mi">324</span><span class="x">,</span> <span class="mi">356</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">195</span><span class="x">,</span> <span class="mi">227</span><span class="x">,</span> <span class="mi">271</span><span class="x">,</span> <span class="mi">274</span><span class="x">,</span> <span class="mi">301</span><span class="x">,</span> <span class="mi">318</span><span class="x">,</span> <span class="mi">376</span><span class="x">,</span> <span class="mi">406</span><span class="x">]</span>
 <span class="n">⋮</span>
 <span class="x">[</span><span class="mi">294</span><span class="x">,</span> <span class="mi">429</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">307</span><span class="x">,</span> <span class="mi">432</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">308</span><span class="x">,</span> <span class="mi">394</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">322</span><span class="x">,</span> <span class="mi">345</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">327</span><span class="x">,</span> <span class="mi">371</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">349</span><span class="x">,</span> <span class="mi">405</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">375</span><span class="x">,</span> <span class="mi">440</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">393</span><span class="x">,</span> <span class="mi">438</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">395</span><span class="x">,</span> <span class="mi">424</span><span class="x">]</span>

<span class="n">julia</span><span class="o">&gt;</span> <span class="n">sg</span> <span class="o">=</span> <span class="n">hg</span><span class="x">[</span><span class="n">cs</span><span class="x">[</span><span class="mi">1</span><span class="x">]]</span> <span class="c"># could also do trim_graph(threshold=100)</span>
<span class="n">HyphenGraph</span> <span class="n">with</span> <span class="mi">248</span> <span class="n">people</span><span class="x">,</span> <span class="mi">251</span> <span class="n">hyphens</span>

<span class="n">julia</span><span class="o">&gt;</span> <span class="n">julia</span><span class="o">&gt;</span> <span class="n">plot_graph</span><span class="x">(</span><span class="n">sg</span><span class="x">,</span> <span class="n">node_color_prop</span><span class="o">=</span><span class="s">"birth_year"</span><span class="x">,</span> <span class="n">edge_color_prop</span><span class="o">=</span><span class="s">"year"</span><span class="x">)</span>
</code></pre></div></div>
<p><img src="/images/blog/2021-08-18-nodariety/big_cluster_years.png" alt="" /></p>

<p>The GLMakie visualization is interactive, so I did some manual rearranging of the node positions to make this more visually parse-able…see “how you can help” below).</p>

<p>We could also color nodes by other properties such as various demographic properties, but some of those are a bit easier to parse if we just look at some histograms. For that, there’s a <code class="language-plaintext highlighter-rouge">node_histogram</code> function.  For example, <code class="language-plaintext highlighter-rouge">node_histogram(“gender”)</code> yields:
<img src="/images/blog/2021-08-18-nodariety/hist_gender.png" alt="" /></p>

<p>Pretty depressing. Even more so if you then try <code class="language-plaintext highlighter-rouge">node_histogram(“given_name”)</code>, at which point you can find which three male names have more instances than the <em>total</em> number of women. Similar results from <code class="language-plaintext highlighter-rouge">node_histogram("birth_continent")</code>…
<img src="/images/blog/2021-08-18-nodariety/hist_continent.png" alt="" /></p>

<p>Obviously, a lot of this reflects historical norms and precedents, but it’s a pretty dramatic representation of how we can and need to do better going forward as a community. (It also, perhaps, suggests opportunities for targeted Wikipedia content-creation campaigns!)</p>

<p>But let’s try something else fun! Because <code class="language-plaintext highlighter-rouge">HyphenGraph&lt;:AbstractGraph</code>, lots of <code class="language-plaintext highlighter-rouge">LightGraphs</code> functionality “just works” here (in fact we’ve already used it to find clusters above), so we can do neat stuff like probe centrality measures. I’ve included some functions for this:</p>
<div class="language-julia highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">julia</span><span class="o">&gt;</span> <span class="n">most_central</span><span class="x">(</span><span class="n">betweenness_centrality</span><span class="x">)</span> <span class="c"># get details for one measure</span>
<span class="kt">Dict</span><span class="x">{</span><span class="kt">Symbol</span><span class="x">,</span> <span class="kt">Any</span><span class="x">}</span> <span class="n">with</span> <span class="mi">9</span> <span class="n">entries</span><span class="o">:</span>
  <span class="o">:</span><span class="n">birth_year</span>      <span class="o">=&gt;</span> <span class="mi">1879</span>
  <span class="o">:</span><span class="n">given_name</span>      <span class="o">=&gt;</span> <span class="s">"Albert"</span>
  <span class="o">:</span><span class="n">death_year</span>      <span class="o">=&gt;</span> <span class="mi">1955</span>
  <span class="o">:</span><span class="n">birth_country</span>   <span class="o">=&gt;</span> <span class="s">"Germany"</span>
  <span class="o">:</span><span class="n">race</span>            <span class="o">=&gt;</span> <span class="s">"white"</span>
  <span class="o">:</span><span class="n">reference</span>       <span class="o">=&gt;</span> <span class="s">"https://en.wikipedia.org/wiki/Albert_Einstein"</span>
  <span class="o">:</span><span class="n">birth_continent</span> <span class="o">=&gt;</span> <span class="s">"Europe"</span>
  <span class="o">:</span><span class="n">family_name</span>     <span class="o">=&gt;</span> <span class="s">"Einstein"</span>
  <span class="o">:</span><span class="n">gender</span>          <span class="o">=&gt;</span> <span class="s">"male"</span>

<span class="n">julia</span><span class="o">&gt;</span> <span class="n">all_centrals</span><span class="x">()</span> <span class="c"># or just do a bunch of them!</span>
<span class="n">betweenness_centrality</span><span class="o">:</span> <span class="n">Albert</span> <span class="n">Einstein</span>
<span class="n">closeness_centrality</span><span class="o">:</span> <span class="n">Leonhard</span> <span class="n">Euler</span>
<span class="n">degree_centrality</span><span class="o">:</span> <span class="n">Albert</span> <span class="n">Einstein</span>
<span class="n">eigenvector_centrality</span><span class="o">:</span> <span class="n">Satyendra</span> <span class="n">Bose</span>
<span class="n">katz_centrality</span><span class="o">:</span> <span class="n">David</span> <span class="n">Mumford</span>
<span class="n">pagerank</span><span class="o">:</span> <span class="n">Albert</span> <span class="n">Einstein</span>
<span class="n">stress_centrality</span><span class="o">:</span> <span class="n">Albert</span> <span class="n">Einstein</span>
<span class="n">radiality_centrality</span><span class="o">:</span> <span class="n">Niels</span> <span class="n">Abel</span>
</code></pre></div></div>

<p>Or, we can answer the question that started it all…</p>
<div class="language-julia highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">julia</span><span class="o">&gt;</span> <span class="n">paths</span> <span class="o">=</span> <span class="n">longest_path</span><span class="x">()</span> <span class="c"># get indices of paths</span>
<span class="mi">2</span><span class="o">-</span><span class="n">element</span> <span class="kt">Vector</span><span class="x">{</span><span class="kt">Vector</span><span class="x">{</span><span class="kt">Int64</span><span class="x">}}</span><span class="o">:</span>
 <span class="x">[</span><span class="mi">183</span><span class="x">,</span> <span class="mi">391</span><span class="x">,</span> <span class="mi">82</span><span class="x">,</span> <span class="mi">40</span><span class="x">,</span> <span class="mi">75</span><span class="x">,</span> <span class="mi">248</span><span class="x">,</span> <span class="mi">282</span><span class="x">,</span> <span class="mi">368</span><span class="x">]</span>
 <span class="x">[</span><span class="mi">285</span><span class="x">,</span> <span class="mi">391</span><span class="x">,</span> <span class="mi">82</span><span class="x">,</span> <span class="mi">40</span><span class="x">,</span> <span class="mi">75</span><span class="x">,</span> <span class="mi">248</span><span class="x">,</span> <span class="mi">282</span><span class="x">,</span> <span class="mi">368</span><span class="x">]</span>

<span class="n">julia</span><span class="o">&gt;</span> <span class="n">hg</span><span class="x">[</span><span class="n">paths</span><span class="x">[</span><span class="mi">1</span><span class="x">]]</span><span class="o">.</span><span class="n">node_info</span><span class="o">.</span><span class="n">family_name</span>
<span class="mi">8</span><span class="o">-</span><span class="n">element</span> <span class="kt">Vector</span><span class="x">{</span><span class="kt">String</span><span class="x">}</span><span class="o">:</span>
 <span class="s">"Kelvin"</span>
 <span class="s">"Stokes"</span>
 <span class="s">"Einstein"</span>
 <span class="s">"Cartan"</span>
 <span class="s">"Dieudonné"</span>
 <span class="s">"Manin"</span>
 <span class="s">"Mumford"</span>
 <span class="s">"Shah"</span>

<span class="n">julia</span><span class="o">&gt;</span> <span class="n">hg</span><span class="x">[</span><span class="n">paths</span><span class="x">[</span><span class="mi">2</span><span class="x">]]</span><span class="o">.</span><span class="n">node_info</span><span class="o">.</span><span class="n">family_name</span>
<span class="mi">8</span><span class="o">-</span><span class="n">element</span> <span class="kt">Vector</span><span class="x">{</span><span class="kt">String</span><span class="x">}</span><span class="o">:</span>
 <span class="s">"Navier"</span>
 <span class="s">"Stokes"</span>
 <span class="s">"Einstein"</span>
 <span class="s">"Cartan"</span>
 <span class="s">"Dieudonné"</span>
 <span class="s">"Manin"</span>
 <span class="s">"Mumford"</span>
 <span class="s">"Shah"</span>
</code></pre></div></div>

<h3 id="how-can-you-help">How can you help?</h3>

<p>In plenty of ways! Some ideas to start with:</p>

<ol>
  <li><strong>Add more data!</strong> The best way to do this is by GitHub pull request. While it’s not absolutely required, I would strongly request that if you do this, you follow these guidelines:
    <ol>
      <li>Chase tails thoroughly. e.g. if you want to add the A-B theorem, but A is also part of the A-C conjecture, the D-A algorithm, etc., make sure to add those (and so on down the branches).</li>
      <li>Add as much data as you can find (leave minimal missing entries in the CSV files); try to at least exhaust Wikipedia’s resources.</li>
      <li>Please include open-access references in those fields.</li>
    </ol>
  </li>
  <li><strong>Help with graph layout!</strong> You may notice that there are lots of edge crossings in the plots above. I’ve played around with a variety of layout algorithms from the NetworkLayout.jl package, but none of them are quite satisfactory. If you’re handy with that sort of thing, feel free to help!</li>
  <li><strong>Other visualization help!</strong> I have an alternative approach to graph visualization that you can see at <a href="https://rkurchin.github.io/nodarietyvis/">Nodariety</a> (GitHub repo <a href="https://github.com/rkurchin/nodarietyvis">here</a>) that has some nice things about it; in particular the little popups with other information. Would be cool to figure out a non-laggy way to do that within GraphMakie…</li>
  <li>Try out other fun ideas for graph analysis and make PR’s with those functions!</li>
</ol>

<p>I very much hope people might find this as rewarding of a hobby pursuit as I’ve found it to be, and would very much welcome any and all contributions! Please check out the <a href="https://github.com/rkurchin/Nodariety.jl/issues">issues</a> on the repository for some more specific suggestions, as well!</p>]]></content><author><name>Zihao Xu</name><email>zihao.xu@rutgers.edu</email></author><category term="coding_projects" /><category term="julia" /><category term="graphs" /><summary type="html"><![CDATA[HyphenGraphs are cool!]]></summary></entry><entry><title type="html">The Sirensong of Multitasking in the Remote Era</title><link href="https://shsjxzh.github.io/posts/2021/05/multitasking" rel="alternate" type="text/html" title="The Sirensong of Multitasking in the Remote Era" /><published>2021-05-26T00:00:00-04:00</published><updated>2021-05-26T00:00:00-04:00</updated><id>https://shsjxzh.github.io/posts/2021/05/multitasking</id><content type="html" xml:base="https://shsjxzh.github.io/posts/2021/05/multitasking"><![CDATA[<p>Likely we have all read articles about how “true” multitasking is impossible – all we really can do is switch rapidly between multiple tasks. Just in the past month, there have been <a href="https://www.discovermagazine.com/mind/why-multitasking-does-more-harm-than-good">several</a> <a href="https://www.fastcompany.com/90630548/the-myth-of-multitasking">articles</a> about how this is the case, how it lowers our overall work efficiency, and how <a href="https://www.wired.com/story/stop-looking-your-email-youre-video/">everyone is doing it anyway</a>.</p>

<p>(One <a href="https://www.bbc.com/worklife/article/20210416-how-multitasking-fuels-original-thinking">BBC article</a> puts forward the arguably contrarian take that it could be a good thing because it stimulates creativity or something, but let’s set that aside as an outlier and roll with the assertion that most of the time, multitasking is bad.)</p>

<p>Here’s the thing. As plenty of these articles point out, everything being on Zoom makes it <em>so</em>. <em>damn</em>. <em>tempting</em>. Especially if everyone’s cameras are off anyway, or it’s a webinar. Especially if the discussion is not particularly interesting or engaging. Especially if the to-do list is intimidatingly long. It’s absolutely tantalizing to just cruise through some emails or other minor chores. And probably sometimes that’s fine. (We all have things that we feel some obligation to be “present” at, but don’t get much from.)</p>

<p>But lately, I’ve realized that it’s actually been a long time since I gave my full attention to the entire duration of a webinar, conference talk, or even just large meeting that I wasn’t actively leading or participating in, and that strikes me as a problem. After all, in an in-person department seminar, I’m very rarely if ever on my laptop or otherwise trying to do anything other than listen to the speaker. And sometimes, it could be a talk that I would’ve been really interested in, but because I let myself be distracted at the beginning, I lost the thread.</p>

<p>So in the name of personal accountability, but also because I suspect I’m not the only one experiencing this, I’m going to posit some relatively specific measures that might help start to address this, mainly by reducing the temptation to turn my attention to other things. Some of them I already do somewhat regularly. Some are things I’d like to start doing, or start doing more of. Anyway, here goes.</p>

<ol>
  <li>
    <p><strong>Do things in fullscreen mode when possible.</strong> I already do this as a matter of course for tasks on which I can’t make any progress without undivided attention (coding, debugging, writing, etc.). I’d like to start doing it more for things like Zoom meetings, and maybe even temporarily turn off extra monitors that are only serving as distractions during those times.</p>
  </li>
  <li>
    <p><strong>Mitigate <em>others’</em> expectations.</strong> For example, set a Slack status as “away” during meetings/talks and maybe even mute notifications to reduce the temptation to respond to messages that come in.</p>
  </li>
  <li>
    <p><strong>Take actual breaks.</strong> This one I’m actually alright at. When I feel my focus waning, I step away from my desk and go for a walk, make some tea, do some yoga, work out, or otherwise disconnect briefly. I do often succumb to the temptation to continue answering Slack messages or emails that come in during breaks, so I could probably do to incorporate the wisdom of #2 above into my practice of this.</p>
  </li>
</ol>

<p>I genuinely believe that not only will these things make me a happier and healthier person, but through doing so, will increase me efficiency and productivity, because without the mental overhead of constantly switching my attention, I can put in more hours of <em>quality</em> work in any given day.</p>

<p>Maybe you want to join me?</p>

<p><em>(Full disclosure: This post was written while I was “at” a poster session for a conference. But nobody came to see the poster during that time…)</em></p>]]></content><author><name>Zihao Xu</name><email>zihao.xu@rutgers.edu</email></author><category term="musings" /><summary type="html"><![CDATA[Everybody's doing it...]]></summary></entry><entry><title type="html">Bike Power and Hills</title><link href="https://shsjxzh.github.io/posts/2020/05/ftp" rel="alternate" type="text/html" title="Bike Power and Hills" /><published>2020-05-25T00:00:00-04:00</published><updated>2020-05-25T00:00:00-04:00</updated><id>https://shsjxzh.github.io/posts/2020/05/ftp</id><content type="html" xml:base="https://shsjxzh.github.io/posts/2020/05/ftp"><![CDATA[<p>I’m a cyclist, and I’m also a nerd who loves playing with data, so unsurprisingly, I buy pretty much every kind of sensor I can afford to put on my bike and track various datastreams during my rides. Last year, I finally shelled out for a power meter, generally considered the gold standard type of training data since, unlike speed, power output should be comparable across all kinds of conditions (indoor/outdoor riding, wind speeds, etc.). Through that, I’ve been learning about different ways to use and target different power ranges in training. The standard parlance revolves around “functional threshold power,” or FTP, defined as the steady power output one could theoretically maintain for an hour. Then various effort levels for interval training are done at various multiples of FTP. For indoor training, I’ve actually been using <a href="https://thesufferfest.com">Sufferfest</a>, which does a fitness test designed to find your maximal power over several different durations, based on data suggesting that the maximum power someone can maintain for, say, five minutes, as a multiple of FTP, can vary greatly across individuals. See more <a href="https://thesufferfest.com/blogs/training-resources/is-your-training-app-accurate">here</a>, it’s pretty interesting stuff. Here are my most recent results:</p>

<p><img src="/images/blog/2020-05-25-ftp/4dp_April2020.png" alt="" /></p>

<p>Since I moved to Pittsburgh and have been cycling on some of the abundant hills in this area (I actually had to get some easier gears on my bike for this purpose), it’s been interesting to track all this data as I (oh so very gradually) get better at climbing! On my ride yesterday, I started getting curious about some of the inherent differences between riders with different FTP riding on the same terrain, and also the nonlinearities between power and speed, on those gradients or on a flat road. So I decided to do some physics to it!</p>

<h2 id="the-model">The model</h2>
<blockquote>
  <p>NOTE: Thanks a million to Chris White from Ride Far for <a href="https://ridefar.info/wp-content/uploads/pdf/method.pdf">this PDF</a> that informed my notation here, and more importantly, provided some characteristic values for several of the coefficients about which I had no idea.</p>
</blockquote>

<p>All the calculations I allude to below have been done in Python and the code is available on GitHub <a href="https://github.com/rkurchin/ftp_hills">here</a>, but I’ll include some of it inline in this post too. To start…</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">scipy.constants</span> <span class="kn">import</span> <span class="n">g</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">atm</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">seaborn</span> <span class="k">as</span> <span class="n">sns</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
</code></pre></div></div>

<p>Okay, so let’s lay out these equations. The basic premise of all of this (thanks, Newton) is that if you’re going at a steady speed, the power you are exerting should be equal to the power being dissipated by all the various resistive forces:</p>

\[P_{wheel} = P_{resist}\]

<p>So the crux of this is modeling either side of this. Let’s start with the more complicated one, the right side. For this, we need to enumerate every resistive force. First, air resistance:</p>

\[F_{air} = \frac12C_dA\rho v_a^2\]

<p>where $C_dA$ is the product of the drag coefficient and the area (characteristic values for this are tabulated <a href="https://ridefar.info/bike/cycling-speed/air-resistance-cyclist/">here</a>), $v_a$ is the air speed, and $\rho$ is the air density, which we can model as:</p>

\[\rho = \frac{p_0}{R_sT}\left(1-\frac{Lh}{T_0}\right)^{Mg/R_UL}\]

<p>where $p_0$ is a standard atmosphere (101,325 Pa), $R_s$ is the specific gas constant (287.058 J/[kg K]), $L$ is the approximate rate of decrease in temperature with elevation (0.0065 K/m), $h$ is the altitude in meters (about 500 for Pittsburgh), $M$ is the molar mass of dry air (0.02896 kg/mol), and $R_U$ is the universal gas constant (8.315 J/[mol K]).</p>

<p>Here’s the Python code for air resistance:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># air density in kg/m^3
</span><span class="k">def</span> <span class="nf">air_density</span><span class="p">(</span><span class="n">altitude</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">T</span><span class="o">=</span><span class="mi">293</span><span class="p">):</span>
    <span class="n">L</span> <span class="o">=</span> <span class="mf">0.0065</span>
    <span class="n">T0</span> <span class="o">=</span> <span class="mi">298</span>
    <span class="n">M</span> <span class="o">=</span> <span class="mf">0.02896</span>
    <span class="n">Rs</span> <span class="o">=</span> <span class="mf">287.058</span>
    <span class="n">p_exp</span> <span class="o">=</span> <span class="n">M</span><span class="o">*</span><span class="n">g</span><span class="o">/</span><span class="p">(</span><span class="n">R</span><span class="o">*</span><span class="n">L</span><span class="p">)</span>
    <span class="n">p</span> <span class="o">=</span> <span class="n">atm</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span><span class="o">-</span><span class="p">(</span><span class="n">L</span><span class="o">*</span><span class="n">altitude</span><span class="o">/</span><span class="n">T0</span><span class="p">))</span><span class="o">**</span><span class="n">p_exp</span>
    <span class="k">return</span> <span class="n">p</span><span class="o">/</span><span class="p">(</span><span class="n">Rs</span><span class="o">*</span><span class="n">T</span><span class="p">)</span>

<span class="c1"># resisting force from air
</span><span class="k">def</span> <span class="nf">F_air</span><span class="p">(</span><span class="n">mph</span><span class="p">,</span> <span class="n">CdA</span><span class="p">,</span> <span class="n">rho</span><span class="o">=</span><span class="n">air_density</span><span class="p">()):</span>
    <span class="n">speed</span> <span class="o">=</span> <span class="n">mph</span><span class="o">/</span><span class="mf">2.237</span>
    <span class="k">return</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">CdA</span> <span class="o">*</span> <span class="n">rho</span> <span class="o">*</span> <span class="n">speed</span><span class="o">**</span><span class="mi">2</span>
</code></pre></div></div>

<p>Next, rolling resistance! To compute the rolling friction, we need to know the force normal to the road, hence the triggy stuff.</p>

\[F_{roll} = C_{rr}\cos(\tan^{-1}G)mg\]

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">F_roll</span><span class="p">(</span><span class="n">grad</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">Crr</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">Crr</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">cos</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">arctan</span><span class="p">(</span><span class="n">grad</span><span class="p">))</span> <span class="o">*</span> <span class="n">m</span> <span class="o">*</span> <span class="n">g</span>
</code></pre></div></div>

<p>Here, $C_{rr}$ is the coefficient of rolling resistance (which I could look up for my tires <a href="https://www.bicyclerollingresistance.com/road-bike-reviews/mavic-yksion-elite-2018#rr">here</a>, it’s 0.00483), and $G$ is the gradient of the road, that is, the tangent of its angle of inclination.</p>

<p>And finally, gravity, for which we need the component along the level of the road:</p>

\[F_{grav} = \sin(\tan^{-1}G)mg\]

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">F_grav</span><span class="p">(</span><span class="n">grad</span><span class="p">,</span> <span class="n">m</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">sin</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">arctan</span><span class="p">(</span><span class="n">grad</span><span class="p">))</span> <span class="o">*</span> <span class="n">m</span> <span class="o">*</span> <span class="n">g</span>
</code></pre></div></div>

<p>We can sum these up and multiply by the ground speed to get the total resistive power:</p>

\[P_{resist} = F_{resist}v_g = (F_{air} + F_{roll} + F_{grav})v_g\]

<p>I’ll also assume for simplicity that $v_g=v_a\equiv v$, i.e. the ground speed is equivalent to the air speed (no wind). Then, the above equation becomes a cubic in the velocity:</p>

\[P_{resist} = \frac 12C_dA\rho v^3 + \left(C_rr\cos(\tan^{-1}G)mg + \sin(\tan^{-1}G)mg\right)v\]

<p>We’re almost there! We just need the other side of that original equation, the $P_{wheel}$ part. Unfortunately, all the power I can drive from my legs into my pedal-based power meter does not get to the wheel to drive the bike forward, because drivetrains are not 100% efficient. So we need to account for drivetrain losses $L_{dt}$:</p>

\[P_{wheel} = P_{legs}(1-L_{dt})\]

<p>Thanks to Chris White, I now know that a characteristic value for $L_{dt}$ is 0.051, so that’s what I’ve used here.</p>

<h2 id="the-results">The results</h2>
<p>First, I made a plot of power needed to achieve a given speed on various gradients (for reference, 0.0 is a flat road, 0.06 is usually the highest allowed on US highways, and the steepest hill in Pittsburgh (also the steepest public road in the US) is <a href="https://en.wikipedia.org/wiki/Canton_Avenue">Canton Avenue</a> which purportedly reaches a grade of 0.37):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">P_needed</span><span class="p">(</span><span class="n">mph</span><span class="p">,</span> <span class="n">grad</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">CdA</span><span class="p">,</span> <span class="n">Crr</span><span class="p">,</span> <span class="n">rho</span><span class="p">,</span> <span class="n">L_dt</span><span class="o">=</span><span class="mf">0.051</span><span class="p">):</span>
    <span class="n">speed</span> <span class="o">=</span> <span class="n">mph</span><span class="o">/</span><span class="mf">2.237</span> <span class="c1"># convert to m/s
</span>    <span class="n">F_resist</span> <span class="o">=</span> <span class="n">F_air</span><span class="p">(</span><span class="n">mph</span><span class="p">,</span> <span class="n">CdA</span><span class="p">,</span> <span class="n">rho</span><span class="p">)</span> <span class="o">+</span> <span class="n">F_roll</span><span class="p">(</span><span class="n">grad</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">Crr</span><span class="p">)</span> <span class="o">+</span> <span class="n">F_grav</span><span class="p">(</span><span class="n">grad</span><span class="p">,</span> <span class="n">m</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">F_resist</span> <span class="o">*</span> <span class="n">speed</span> <span class="o">/</span> <span class="p">(</span><span class="mf">1.0</span><span class="o">-</span><span class="n">L_dt</span><span class="p">)</span>

<span class="n">mph_vals</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="mf">12.1</span><span class="p">,</span><span class="mf">0.1</span><span class="p">)</span>
<span class="n">grad_vals</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.25</span><span class="p">,</span> <span class="mf">0.03</span><span class="p">)</span>
<span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">.</span><span class="n">from_records</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="n">mph_vals</span><span class="p">,</span> <span class="n">grad_vals</span><span class="p">)],</span> <span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s">"speed"</span><span class="p">,</span> <span class="s">"gradient"</span><span class="p">])</span>
<span class="c1"># calculate for me and my bike
</span><span class="n">df</span><span class="p">[</span><span class="s">'W/kg'</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">Wperkg</span><span class="p">(</span><span class="mf">63.5</span><span class="p">,</span> <span class="mf">8.0</span><span class="p">,</span> <span class="n">a</span><span class="p">.</span><span class="n">speed</span><span class="p">,</span> <span class="n">a</span><span class="p">.</span><span class="n">gradient</span><span class="p">)</span> <span class="k">for</span> <span class="n">a</span> <span class="ow">in</span> <span class="n">df</span><span class="p">.</span><span class="n">itertuples</span><span class="p">()]</span>
<span class="n">sns</span><span class="p">.</span><span class="n">lineplot</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="s">'speed'</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="s">'W/kg'</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">df</span><span class="p">,</span> <span class="n">hue</span><span class="o">=</span><span class="s">'gradient'</span><span class="p">,</span> <span class="n">palette</span><span class="o">=</span><span class="n">sns</span><span class="p">.</span><span class="n">cubehelix_palette</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">grad_vals</span><span class="p">),</span> <span class="n">start</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">rot</span><span class="o">=</span><span class="p">.</span><span class="mi">5</span><span class="p">))</span>
<span class="c1"># plus some plot formatting stuff
</span></code></pre></div></div>

<p><img src="/images/blog/2020-05-25-ftp/power_vs_speed.png" alt="" /></p>

<p>Note that the y-axis here is in watts per kilogram of body mass, so things will shift a bit if the ratio of body mass to bike (+gear) mass changes, for the calculations here it was about a factor of 9. I also put my FTP and my maximum 5-second power on as reference points. The choice of x-axis lower limit was also deliberate, as in my experience, 4 mph is about the slowest one can go uphill and remain upright.</p>

<p>So this plot certainly shows some of the things I’d been thinking about – for example, if we consider FTP to be some measure of “sustainable” power output, then we can get a sense of what grades folks with different FTP values can sustainbly chug their way up for quite awhile while staying above that 4mph threshold. For me at 2.24, it’s something like 9-10% or so. Whereas if you’re a much fitter person than I, you might be able to go up that 9% in excess of 10 mph at your FTP, or alternatively, at around the same pace on something like a 15% grade.</p>

<p>This is cool, but I realized I actually wanted a different chart, one where we could explicitly see the tradeoff between speed and gradient for a fixed power. To do that, we need to solve the cubic equation I outlined above. Fortunately, it always has exactly one real root, so that’s pretty easy, and we can make this plot (we’re essentially showing where different horizontal lines on the previous plot intersect with the curves for different gradients):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">calc_speed</span><span class="p">(</span><span class="n">watts_per_kg</span><span class="p">,</span> <span class="n">grad</span><span class="p">,</span> <span class="n">body_mass</span><span class="p">,</span> <span class="n">bike_mass</span><span class="p">,</span> <span class="n">CdA</span><span class="o">=</span><span class="mf">0.45</span><span class="p">,</span> <span class="n">Crr</span><span class="o">=</span><span class="mf">0.00483</span><span class="p">,</span> <span class="n">rho</span><span class="o">=</span><span class="n">air_density</span><span class="p">(),</span> <span class="n">L_dt</span><span class="o">=</span><span class="mf">0.051</span><span class="p">):</span>
    <span class="n">total_mass</span> <span class="o">=</span> <span class="n">body_mass</span> <span class="o">+</span> <span class="n">bike_mass</span>
    <span class="n">a3</span> <span class="o">=</span> <span class="mf">0.5</span><span class="o">*</span><span class="n">CdA</span><span class="o">*</span><span class="n">rho</span>
    <span class="n">a1</span> <span class="o">=</span> <span class="n">F_roll</span><span class="p">(</span><span class="n">grad</span><span class="p">,</span> <span class="n">total_mass</span><span class="p">,</span> <span class="n">Crr</span><span class="p">)</span> <span class="o">+</span> <span class="n">F_grav</span><span class="p">(</span><span class="n">grad</span><span class="p">,</span> <span class="n">total_mass</span><span class="p">)</span>
    <span class="n">a0</span> <span class="o">=</span> <span class="o">-</span><span class="mf">1.0</span> <span class="o">*</span> <span class="n">watts_per_kg</span><span class="o">*</span><span class="n">body_mass</span> <span class="o">*</span> <span class="p">(</span><span class="mf">1.0</span><span class="o">-</span><span class="n">L_dt</span><span class="p">)</span>
    <span class="n">rts</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">roots</span><span class="p">([</span><span class="n">a3</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">a1</span><span class="p">,</span> <span class="n">a0</span><span class="p">])</span>
    <span class="c1"># one should be real, return that one
</span>    <span class="k">return</span> <span class="p">[</span><span class="n">r</span><span class="p">.</span><span class="n">real</span><span class="o">*</span><span class="mf">2.237</span> <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">rts</span> <span class="k">if</span> <span class="n">r</span><span class="p">.</span><span class="n">imag</span><span class="o">==</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>

    <span class="n">P_vals</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span><span class="mf">22.1</span><span class="p">,</span><span class="mf">3.0</span><span class="p">)</span>
    <span class="n">grad_vals</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span><span class="mf">0.252</span><span class="p">,</span><span class="mf">0.002</span><span class="p">)</span>
    <span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">.</span><span class="n">from_records</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="n">P_vals</span><span class="p">,</span> <span class="n">grad_vals</span><span class="p">)],</span> <span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s">"W/kg"</span><span class="p">,</span> <span class="s">"gradient"</span><span class="p">])</span>
    <span class="n">df</span><span class="p">[</span><span class="s">'mph'</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">calc_speed</span><span class="p">(</span><span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">a</span><span class="p">.</span><span class="n">gradient</span><span class="p">,</span> <span class="mf">63.5</span><span class="p">,</span> <span class="mf">8.0</span><span class="p">)</span> <span class="k">for</span> <span class="n">a</span> <span class="ow">in</span> <span class="n">df</span><span class="p">.</span><span class="n">itertuples</span><span class="p">()]</span>

<span class="n">sns</span><span class="p">.</span><span class="n">lineplot</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="s">'gradient'</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="s">'mph'</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">df</span><span class="p">,</span> <span class="n">hue</span><span class="o">=</span><span class="s">'W/kg'</span><span class="p">,</span> <span class="n">palette</span><span class="o">=</span><span class="n">sns</span><span class="p">.</span><span class="n">cubehelix_palette</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">P_vals</span><span class="p">),</span> <span class="n">start</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">rot</span><span class="o">=</span><span class="mf">1.0</span><span class="p">))</span>
<span class="c1"># plus some more beautification
</span></code></pre></div></div>

<p><img src="/images/blog/2020-05-25-ftp/speed_vs_gradient.png" alt="" /></p>

<p>(Sidenote: <a href="https://seaborn.pydata.org/generated/seaborn.cubehelix_palette.html">Cubehelix</a> color palettes are so cool! As is <a href="https://seaborn.pydata.org">Seaborn</a> generally for dataviz.)</p>

<p>Here now I’ve overlaid my own FTP, maximum 1-minute, and maximum 5-second power values as additional iso-lines. Again, the minimum speed shown is 4mph. For some typical numbers for male and female cyclists of various calibers, check out the chart partway down <a href="https://cyclingtips.com/2017/06/just-how-good-are-male-pro-road-cyclists/">this page</a>.</p>

<p>In this plot, it’s even easier to read off the kind of things I was discussing above, and also to draw conclusions about what kind of gradients one could “power up” for really short periods of time. For example, if your absolute maximum power output were 4 W/kg, you’re not going to be able to get up anything steeper than around 19%, even instantaneously.</p>

<p>All this analysis is cool, and also serves as a good motivator to get out and improve my power numbers! Especially since the iso-power slopes at the bottom of this curve are such that a relatively small improvement in power capability leads to a comparatively large one in accessible gradients, and I’d love to take a shot at the <a href="https://en.wikipedia.org/wiki/Dirty_Dozen_(bicycle_competition)">Dirty Dozen</a> someday!</p>

<p>Reminder that the code is all <a href="https://github.com/rkurchin/ftp_hills">here</a> so you should definitely go play and put in your own numbers to see how this turns out for you!</p>

<h2 id="caveats">Caveats!</h2>
<p>In any simple model like this, there are a lot of assumptions! Here is a not-necessarily-exhaustive list</p>
<ul>
  <li>I ignored the effects of wind (as mentioned above, assumed ground speed equal to air speed)</li>
  <li>I assumed perfectly smooth power profiles, which really only happens if you have ideal pedaling technique – in reality, most of us fluctuate through the pedal stroke, though it’s debatable to what extent one’s power meter can accurately pick this up, and so the average numbers on both ends probably work out decently</li>
  <li>I used $C_dA=0.45$ everywhere since I usually ride in my tops up difficult hills (see graph <a href="https://ridefar.info/bike/cycling-speed/air-resistance-cyclist/">here</a>), but in reality, on less steep hills, I might be in the hoods or even down in my aerobars, so the speed curves would shift up a little from the reduced air resistance there</li>
</ul>]]></content><author><name>Zihao Xu</name><email>zihao.xu@rutgers.edu</email></author><category term="coding_projects" /><category term="python" /><category term="cycling" /><summary type="html"><![CDATA[Nerding out about bikes...likely not for the last time.]]></summary></entry><entry><title type="html">Beer and Quarantine</title><link href="https://shsjxzh.github.io/posts/2020/05/quarantine-project" rel="alternate" type="text/html" title="Beer and Quarantine" /><published>2020-05-23T00:00:00-04:00</published><updated>2020-05-23T00:00:00-04:00</updated><id>https://shsjxzh.github.io/posts/2020/05/quarantine-project</id><content type="html" xml:base="https://shsjxzh.github.io/posts/2020/05/quarantine-project"><![CDATA[<h2 id="the-premise">The premise</h2>
<p>About a week ago, I got an email from my uncle Dennis:</p>
<blockquote>
  <p>Hi,</p>

  <p>I seem to recall you doing some web scraping to do something useful. Do you have any advice as to how to do this? On this site I can see the beers in stock at my local store:
https://belmont.craftbeercellar.com</p>

  <p>Here, I can find ratings of beers:
https://www.beeradvocate.com</p>

  <p>I’d like to see the average Beer Advocate rating for beers available at Craft Beer Cellar. Even better if I can sort by style. Any suggestions on how to do that?</p>

  <p>Dennis</p>
</blockquote>

<p>This sounded like a fun project to me, so I decided to take it on and build him something.</p>

<h2 id="the-process">The process</h2>
<p>The first helpful thing I found was <a href="https://www.reddit.com/r/Python/comments/32b9w7/pybeer_a_solution_to_beeradvocatecoms_lack_of_any/">this reddit post</a> that linked to <a href="https://github.com/Jfach/beer-for-python">this repo</a>. Unfortunately, that code doesn’t work anymore because BeerAdvocate has changed their website formatting, so the patterns it was matching didn’t work. But it was a super useful starting point since I’d never <em>actually</em> done any web scraping of this kind before (I didn’t tell my uncle that).</p>

<p>It didn’t take too long to get a working version of the BeerAdvocate query, and then I had to build one to scrape from the Beer Cellar site. That wasn’t hard to work out with some trial and error and patterning off the BeerAdvocate one. Soon, I was ready to try to loop through all the results!</p>

<h2 id="the-pickle">The pickle</h2>
<p>(not the Python serialized kind)</p>

<p>After almost instantaneously getting banned from the Beer Cellar website, I quickly realized I needed to drop in some pauses to avoid triggering the websites’ protections against DDOS attacks and the like. This made the code take a bit longer to run, but solved the problem handily.</p>

<h2 id="the-product">The product</h2>
<p>In the end, I got something together that I’m quite happy with. Some features include:</p>

<ul>
  <li>In addition to collating the score and style (as well as ABV) from BeerAdvocate, one can customizably categorize the styles into larger clusters (e.g. English and American IPA both categorized as just “IPA”) for easier sorting</li>
  <li>If the search on BeerAdvocate yields multiple results, uses fuzzy string matching (via the Levenshtein distance as implemented in the <code class="language-plaintext highlighter-rouge">fuzzywuzzy</code> and <code class="language-plaintext highlighter-rouge">python-Levenshtein</code> packages) to choose the best match (if this happens, a remark will be put in the “note” column of output and you can check the included BeerAdvocate link to make sure it’s the right brew)</li>
  <li>Since running the script when ~800 beers are available takes ~40 minutes, optionally check previous output file and only query for beers that don’t have scores listed there, or only for beers that don’t have an entry there at all (this reduces runtime to ~10-15 minutes for a relatively recent output file).</li>
</ul>

<p>Here’s a selection of the output file content:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center">name</th>
      <th style="text-align: center">style</th>
      <th>score</th>
      <th style="text-align: center">brewer</th>
      <th>abv</th>
      <th style="text-align: center">link</th>
      <th style="text-align: center">note</th>
      <th style="text-align: center">category</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">Breakfast Brew</td>
      <td style="text-align: center">English Oatmeal Stout</td>
      <td>100.0</td>
      <td style="text-align: center">Canned Heat Craft Beer Company</td>
      <td>8.3</td>
      <td style="text-align: center"><a href="https://www.beeradvocate.com/beer/profile/1199/11757/">link</a></td>
      <td style="text-align: center">multiple results, guessed best match</td>
      <td style="text-align: center">Stout</td>
    </tr>
    <tr>
      <td style="text-align: center">Softly Spoken Magic Spells</td>
      <td style="text-align: center">New England IPA</td>
      <td>98.0</td>
      <td style="text-align: center">Singlecut Beersmiths</td>
      <td>8.6</td>
      <td style="text-align: center"><a href="http://www.beeradvocate.com/search/?q=Softly+Spoken+Magic+Spells&amp;qt=beer">link</a></td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">IPA</td>
    </tr>
    <tr>
      <td style="text-align: center">Samichlaus Helles</td>
      <td style="text-align: center">German Maibock</td>
      <td>86.0</td>
      <td style="text-align: center">Brauerei Schloss Eggenberg</td>
      <td>14.0</td>
      <td style="text-align: center"><a href="https://www.beeradvocate.com/beer/profile/285/39766/">link</a></td>
      <td style="text-align: center">multiple results, guessed best match</td>
      <td style="text-align: center">Other</td>
    </tr>
    <tr>
      <td style="text-align: center">Picnic Sunrise</td>
      <td style="text-align: center">Berliner Weisse</td>
      <td>NaN</td>
      <td style="text-align: center">Brekeriet Beer Ab</td>
      <td>2.7</td>
      <td style="text-align: center"><a href="http://www.beeradvocate.com/search/?q=Picnic+Sunrise&amp;qt=beer">link</a></td>
      <td style="text-align: center">No score yet</td>
      <td style="text-align: center">Sour</td>
    </tr>
    <tr>
      <td style="text-align: center">All My Best Friends Hop Heads</td>
      <td style="text-align: center">?</td>
      <td>NaN</td>
      <td style="text-align: center">Armada Brewing</td>
      <td>?</td>
      <td style="text-align: center">none</td>
      <td style="text-align: center">couldn’t find BeerAdvocate page</td>
      <td style="text-align: center">Other</td>
    </tr>
  </tbody>
</table>

<p>If you want to check this out for yourself, it’s all up on GitHub <a href="https://github.com/rkurchin/dennis_beer">here</a>, so feel free to play around!</p>

<p>Hope you’re staying healthy, safe, and sane in quarantine. Apparently my reaction to working from home on my computer all day is to take on evening projects to spend even <em>more</em> time at my computer! Don’t worry, I’m still getting outdoors too. Most days.</p>]]></content><author><name>Zihao Xu</name><email>zihao.xu@rutgers.edu</email></author><category term="coding_projects" /><category term="python" /><summary type="html"><![CDATA[Alcohol-motivated web scraping.]]></summary></entry><entry><title type="html">Don’t finish that!</title><link href="https://shsjxzh.github.io/posts/2020/02/do-not-finish" rel="alternate" type="text/html" title="Don’t finish that!" /><published>2020-02-07T00:00:00-05:00</published><updated>2020-02-07T00:00:00-05:00</updated><id>https://shsjxzh.github.io/posts/2020/02/do-not-finish</id><content type="html" xml:base="https://shsjxzh.github.io/posts/2020/02/do-not-finish"><![CDATA[<p>(Teaser photo from <a href="https://www.freepik.com/free-photo/jigsaw-puzzle-with-missing-piece-missing-puzzle-pieces_5469652.htm">here</a>.)</p>

<p>More philosophizing! This time about time management. The title is a bit misleading, but let me explain what I mean. I’ll give some context first.</p>

<p>As one progresses through various levels of studenthood, starting from kindergarten and going all the way through PhD, a monotonic trend is that a smaller and smaller fraction of your time is taken up with structured classroom activities, and a larger and larger fraction with ``homework’’ of some kind – the specific nature of it obviously changes over time, but the key feature is that there is not generally a specific time or sequence in which these tasks need to be completed. Said another way, the further you get, the more power you have to <em>manage your own time</em>.</p>

<p>As Uncle Ben said, with great power comes great responsibility. Personally, I adore the flexibility afforded by the lifestyle of a senior grad student (or now, postdoc, which isn’t <strong>that</strong> different). If it’s a completely gorgeous morning and I don’t have to be at a meeting, I can take off on a bike ride and nobody minds. But, like many others, I sometimes struggle with how to co-optimize my happiness with my productivity under this scheme, and occasionally struggle with feelings of guilt about whether I could be more productive without sacrificing happiness (the agonizing over which contributes to neither productivity nor happiness, as it turns out).</p>

<p>I’ve tried to internalize some general principles around this optimization, which include:</p>

<ol>
  <li>
    <p>Knowing what time you do your best thinking (for me, it’s the period starting 1-2 hours after I wake up and ending 4-5 hours after) and jealously guarding any uninterrupted stretches of time in that window for the really intensive intellectual tasks in my research.</p>
  </li>
  <li>
    <p>(corollary of 1) Saving tasks that don’t require as much mental bandwidth (e.g. responding to emails, filing for reimbursements, etc.) for other times, in particular those annoying short slots of a few tens of minutes that occasionally develop between other obligations.</p>
  </li>
  <li>
    <p>(corollary of 2) Stack meetings! Stack them into blocks as long as your attention span will allow. This helps to create more of the high-quality time mentioned in 1 and reduce the amount of low-quality time mentioned in 2 (there’s always too much of it anyway).</p>
  </li>
  <li>
    <p>Exercise 1-2 hours after lunch. I often experience a dip in energy or straight-up sleepiness during this time, and if my schedule allows, I’ve found it’s a great time to get my workout in. The gym is less crowded because the people who come during lunch have left, but the people who come after work haven’t arrived yet, and I return to my desk feeling mentally reinvigorated and, if I don’t have meetings, often get an extra 1-3 hours of “high-quality” thinking time as a result.</p>
  </li>
</ol>

<p>I’ve generally been pretty good at implementing these principles. The one I fail at most is being disciplined about #1. I’ll sit down at my desk first thing in the morning with every intention to make real headway in debugging some problem or implementing a new functionality in my code, but then I just want to make myself some tea and read a couple of the newest arXiv posts while I drink it, and then suddenly it’s two hours later and I’ve skimmed a few papers and responded to a bunch of emails but gotten no real work done in my highest mental-firepower time of the day!</p>

<p>So now we come to the title of the post, which alludes to something I’ve recently realized, which could potentially be referred to as a “life hack,” as trite and <a href="https://trends.google.com/trends/explore?date=all&amp;geo=US&amp;q=life%20hack">so 2013</a> as the term may be. This was fueled by the realization that the fundamental problem that leads to the wasting of the quality hours I just described is that in general, there’s a <em>much</em> lower activation energy to read a few emails than to dive into some big, potentially amorphous research task. And seeing the unread badge count of my email client decrease gives me a little endorphin boost as if I’m actually being productive! It’s so tempting!</p>

<p>Answer: <strong>Leave research tasks in an unfinished state.</strong></p>

<p>What exactly do I mean?</p>

<p>Often, I’ll stop working on something when it comes to a nice, sensible stopping point. This seems logical enough. I’ve finished some semi-self-contained task. But this is what creates that high activation energy to come back to the work! Usually, the logical stopping point is the one where I’ve finished every subtask that was an obvious next step, and figuring out what makes sense to do next will take some real mental heavy lifting. The prospect of which might be quite daunting!</p>

<p>What I’ve started doing recently is forcing myself to stop research tasks at a point <strong>where I know exactly what to do next.</strong> The exact form it takes depends on the particular nature of the task I’m working on, but as an example, recently I’ve been working on building an implementation of Crystal Graph Convolutional Neural Nets in Julia, so typical next steps might be implementing or testing the next feature. When I finish for the session/day, I make sure I know exactly what test I’m running next, or if the next thing is to write more code, I’ve written comments that create a detailed outline of how I’m going to write that particular function.</p>

<p>This has actually been minorly life-changing in terms of lowering that activation energy to jump right into research work in the morning or whenever I happen to return to my desk, so I felt the need to share it with whatever tiny audience I might have here.</p>

<p>Over and out for now!</p>]]></content><author><name>Zihao Xu</name><email>zihao.xu@rutgers.edu</email></author><category term="musings" /><category term="time_management" /><summary type="html"><![CDATA[More philosophizing! This time about time management.]]></summary></entry><entry><title type="html">Holiday project: CV revamp!</title><link href="https://shsjxzh.github.io/posts/2019/12/holiday-cv-revamp" rel="alternate" type="text/html" title="Holiday project: CV revamp!" /><published>2019-12-30T00:00:00-05:00</published><updated>2019-12-30T00:00:00-05:00</updated><id>https://shsjxzh.github.io/posts/2019/12/CV</id><content type="html" xml:base="https://shsjxzh.github.io/posts/2019/12/holiday-cv-revamp"><![CDATA[<p>I had a bunch of random to-do’s while I’ve been at my parents’ over the winter holidays and in possession of a high density of “free” time, one of which was to revamp my CV. Well, as sometimes happens with such things, I ended up going way down the rabbit hole, having a lot of fun, and learning a lot! The result was a new LaTeX <code class="language-plaintext highlighter-rouge">.cls</code> file and a bunch of supporting Python code for pulling from the same datafiles that maintain this very site so that I can have a <a href="/files/CVKurchin.pdf">nice pretty PDF</a> that’s relatively easy to keep in-sync with the information here.</p>

<p>So without further ado, let’s show it off! But before I go too deep, for a <strong>tl;dr</strong>, check out the Github repo <a href="https://github.com/rkurchin/Kurchin_CV">here</a> which has most of the basic info you’d need if you just really like it, want it for yourself, and don’t care too much about the nuts and bolts.</p>

<h2 id="my-goals">My goals</h2>
<p>People have different goals, preferences, and requirements when they make a CV. Here were my main ones:</p>

<ul>
  <li>Simple but elegant format that is pleasantly readable but capable of dense enough presentation to fit all my moderately-to-very important information within five pages</li>
  <li>Ability to maintain ONE set of files with all my information that can be used to generate this site as well as the CV (including the publication section!)</li>
  <li>Easy, consistent way to fudge vertical spacings to stop sections from running <em>slightly</em> across page breaks (in my old CV, I was constantly adding/changing <code class="language-plaintext highlighter-rouge">\vspace</code> commands all over the place to achieve this every time I had to add a new piece of information to the document and I was DONE with that)</li>
</ul>

<h2 id="the-starting-point">The starting point</h2>
<p>This is obviously not a new sort of task by any stretch, so I first did a bit of Googling as well as searching around on Github to see how other folks had handled it. I didn’t find any template that was off-the-shelf close enough to what I wanted, which actually surprised me a little bit. So I decided I would smoosh together two things and add some of my own special sauce:</p>

<ol>
  <li><a href="https://www.cmu.edu/cee/people/faculty/wang.html">Jerry</a>’s CV formatting (I don’t think his actual CV is posted online anywhere…)</li>
  <li><a href="https://www.overleaf.com/articles/rishi-shahs-resume/vgxvkmxktyxn">This template</a>  that I found on Overleaf but was originally by <a href="https://treyhunner.com">Trey Hunner</a> and posted on <a href="https://www.latextemplates.com">LaTeX Templates</a></li>
</ol>

<h2 id="how-does-it-work">How does it work?</h2>
<p>Probably half the work was just getting the aesthetics right, and it was pretty fun to actually learn how a LaTeX <code class="language-plaintext highlighter-rouge">.cls</code> file works as I’d never messed around much with defining my own environments and commands before, only super simple macros.</p>

<p>So what are the key components/structure of the template?</p>

<h3 id="the-top-section">The top section</h3>
<p>It looks like this:
<img src="/images/blog/2019-12-30-CV/AC7A7F93-F14B-455D-8D4B-9B7EF20A4A0B.png" alt="" /></p>

<p>All this stuff is easily changed with the top block of the <code class="language-plaintext highlighter-rouge">CV.tex</code> file:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% to populate the top areas
\name{Rachel C. Kurchin}
\uni{Carnegie Mellon University}
\dept{Mechanical Engineering \&amp; Materials Science and Engineering}
\addressone{3404 Wean Hall, Hamerschlag Drive}
\addresstwo{Pittsburgh, PA 15213}
\email{rkurchin@cmu.edu}
\website{rkurchin.github.io}
\scholar{https://scholar.google.com/citations?user=CSHe53oAAAAJ\&amp;hl=en\&amp;oi=ao}
</code></pre></div></div>

<p>These nice readable commands are implemented in the <code class="language-plaintext highlighter-rouge">cv.cls</code> file like so:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% some things for the main document to be more readable
\def \name#1{\def\@name{#1}}
\def \addressone#1{\def\@addressone{#1}}
\def \addresstwo#1{\def\@addresstwo{#1}}
\def \uni#1{\def\@uni{#1}}
\def \dept#1{\def\@dept{#1}}
\def \email#1{\def\@email{#1}}
\def \website#1{\def\@website{#1}}
\def \scholar#1{\def\@scholar{#1}} % Google Scholar link
\def \phone#1{\def\@phone{#1}}
</code></pre></div></div>

<h3 id="main-content-sections">Main content sections</h3>
<p>Each section is a <code class="language-plaintext highlighter-rouge">longtable</code>  (just a <code class="language-plaintext highlighter-rouge">tabular</code> environment that can cross page breaks if needed):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% Defines the cvSection environment for the large sections within the CV
% 3 arguments: (required) section name, (optionally) spacing fudge factor and first column width
\NewDocumentEnvironment{cvSection}{m O{-3pt} O{3.37cm}}
{
    \def\@fudgespace{#2}
    \sectionskip
    \MakeUppercase{\bf #1} % Section title
    \sectionlineskip
    {\color{CMUlightgrey}\hrule height 1.5pt}

    % use a table to get the column on the left
    \begin{longtable}{@{} p{#3} p{\textwidth-#3-0.4cm}}
}
{
    \end{longtable}
    \vspace{\@sectionfudge}
}
</code></pre></div></div>

<p>This is where the magic starts to happen. That <code class="language-plaintext highlighter-rouge">\NewDocumentEnvironment</code> command uses the <a href="https://ctan.org/pkg/xparse?lang=en"><code class="language-plaintext highlighter-rouge">xparse</code> package</a>, which allows much more flexible arguments in environment and function declarations, including multiple optional arguments, which is not possible in vanilla TeX. So in the case of the above, we have one required argument (the name of the section, signified by the <code class="language-plaintext highlighter-rouge">m</code> ) and two optional ones (signified by <code class="language-plaintext highlighter-rouge">O{defaultVal}</code>) for the spacing fudge factor (how far away should each entry be relative to one line width?) and the width of that first column.</p>

<p>You might also notice my custom <code class="language-plaintext highlighter-rouge">CMUlightgrey</code> color above, these are easy to define using the <a href="https://ctan.org/pkg/xcolor?lang=en"><code class="language-plaintext highlighter-rouge">xcolor</code></a> package, and in this case I just looked up the CMYK values for <a href="https://www.cmu.edu/brand/brand-guidelines/visual-identity/colors.html">CMU’s brand identity</a> (basically any university will publish these somewhere):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% for the colored lines
\usepackage[dvipsnames]{xcolor}
    \definecolor{CMUred}{cmyk}{0,1,.79,.2}
    \definecolor{CMUdarkgrey}{cmyk}{0,0,0,.7}
    \definecolor{CMUlightgrey}{cmyk}{0,0,0,.3}
</code></pre></div></div>

<h3 id="individual-entries">Individual entries</h3>
<p>Each entry corresponds to a couple of lines in that <code class="language-plaintext highlighter-rouge">longtable</code> environment. If you’ve ever messed with tables in TeX, you know they can be a bit clunky to handle (I highly recommend a table generator like <a href="https://www.tablesgenerator.com">this one</a>), but since in this case we’ll have many very similar entries, we can define commands for these items that makes everything much easier to read and write.</p>

<p>So for a “generic” item, we can just write something like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\cvItem{09/2019 -- present}
    {Carnegie Mellon University}
    {Depts. of Mechanical Engineering, Materials \newline Science and Engineering}
    {MFI Postdoctoral Fellow with Venkat Viswanathan and Jay Whitacre}
</code></pre></div></div>

<p>(Note that here I also manually added a line break because LaTeX was splitting up the word “Science” in a way I thought was ugly)…which gets turned into code for the table using this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% 4 arguments: year, institution, department/location, position
\newcommand{\cvItem}[4] {
    #1 &amp; \textbf{#2} \textit{#3}\\
     &amp; #4\\
     &amp;\\[\@fudgespace]
}
</code></pre></div></div>

<p>(Note how that “fudge space” comes back here)</p>

<p>Here’s an example of a section with less negative fudge space (-5.5pt):
<img src="/images/blog/2019-12-30-CV/E6EB8FA0-BFC4-46BA-8ECC-7ED19D95AD8D.png" alt="" /></p>

<p>And here’s one with more negative space (-9.5pt)
<img src="/images/blog/2019-12-30-CV/53EF1437-F18B-47D1-ADC3-C45D547BFB73.png" alt="" /></p>

<p>You can see that there’s less space between the entries in this case. But I only have to specify the amount of space once per section — huzzah!</p>

<p>I also have different types of items for publications and lower-detail things that only need to take up a single line.</p>

<h3 id="automatic-content-conversion">Automatic content conversion…</h3>
<p>I love the template I use for this website — check it out here: <a href="https://academicpages.github.io">academicpages</a>. As part of setting that up, I generated <code class="language-plaintext highlighter-rouge">.tsv</code> files for a bunch of different things like awards, service work, etc. Always willing to push the limits of <a href="https://xkcd.com/1205/">efficient automation</a>, I wanted to make Python code that could pull from those same files to generate those input files so I could avoid having to actually type <code class="language-plaintext highlighter-rouge">\cvItem</code> so many times…turns out it wasn’t that difficult! And because each section can be formulaically generated in a relatively similar way, it’s also easy to write a loop over the different sections. As a result, once data and functions have been imported, the loop that generates the input files for the awards, talks, posters, and service sections is only 25 lines of code (excluding comments)!</p>

<p>I actually put a decent amount of comments in all the Python, so I won’t bore you with the gory details here and rather refer you over to those files in the Github repo if you’re interested: <a href="https://github.com/rkurchin/Kurchin_CV/blob/master/cv_fcns.py"><code class="language-plaintext highlighter-rouge">cv_fcns.py</code> </a>, which defines the functions to write the different formats of the item listings, and <a href="https://github.com/rkurchin/Kurchin_CV/blob/master/cv.py"><code class="language-plaintext highlighter-rouge">cv.py</code></a>, which actually reads in the data and generates the content. It also takes care of only adding the year when it changes , as seen in the excerpt of the posters section above or the publications section below.</p>

<h3 id="even-for-publications">…even for publications!</h3>
<p>The Python code also includes some code (using the package <code class="language-plaintext highlighter-rouge">pybtex</code>, and borrowing some code from my friend <a href="https://github.com/romanodev">Giuseppe</a>) that reads in a <code class="language-plaintext highlighter-rouge">.bib</code> file with all my publications and generates a TeX file with a bunch of <code class="language-plaintext highlighter-rouge">\pubsItem</code> lines and also has my name bolded each time, resulting in a section that looks like this:</p>

<p><img src="/images/blog/2019-12-30-CV/1641375F-299C-451F-9701-C759FF24F95D.png" alt="" /></p>

<p>…and yes, those blue things are links to the papers. :D</p>

<h2 id="wanna-try-it">Wanna try it?</h2>
<p>Head on over to the <a href="https://github.com/rkurchin/Kurchin_CV">Github repo</a> and have a go! Let me know what you think!</p>

<p>…and now onto the next project: changing old passwords.</p>]]></content><author><name>Zihao Xu</name><email>zihao.xu@rutgers.edu</email></author><category term="coding_projects" /><category term="python" /><category term="LaTeX" /><summary type="html"><![CDATA[Revamping my CV with some cool Python and LaTeX code.]]></summary></entry><entry><title type="html">Impostor syndrome and mental models</title><link href="https://shsjxzh.github.io/posts/2019/09/impostor-syndrome-mental-models" rel="alternate" type="text/html" title="Impostor syndrome and mental models" /><published>2019-10-21T00:00:00-04:00</published><updated>2019-10-21T00:00:00-04:00</updated><id>https://shsjxzh.github.io/posts/2019/09/impostor-syndrome-mental-models</id><content type="html" xml:base="https://shsjxzh.github.io/posts/2019/09/impostor-syndrome-mental-models"><![CDATA[<p>Jerry and I both love teaching and so (especially now that he’s a professor with students of his own!) we frequently have conversations about pedagogical approaches, and these frequently come back to the idea of mental models. That is, when a student is having trouble understanding something, often it’s because their internal picture of how that system works, or how a new concept connects to old ones, is flawed or incomplete in some way. This could be due to any number of reasons, including (but not limited to):</p>

<ul>
  <li>poor prior teaching,</li>
  <li>an overreliance on rote memorization over deep understanding, or</li>
  <li>an unwillingness (or just lack of sufficient time to) to think hard about and engage with the material for enough time for the new ideas to sink in.</li>
</ul>

<p>As an example, I remember the first time I was introduced to the concept of logarithms. I think I was about 11 or 12, and they seemed utterly mysterious. The initial idea (<em>“What power should I raise a number to to get this number?”</em>) seemed clear enough, but then I started learning all these rules about sums and differences of logs, or changing base, and I felt my eyes glaze over as I reverted to simply memorizing the rules to finish my homework assignments. I probably had to be taught the material two or three more times before it really became intuitive.</p>

<p>(For what it’s worth, I had the same experience with trigonometry as I did with logarithms. And I probably <strong>still</strong> need to learn second quantization once or twice more to really get it…)</p>

<p>And to some extent, I think that’s unavoidable. Some ideas are so different from those you’ve been exposed to before that you really need to chew on them before you can internalize and use them fluently. But as I’ve started my postdoc and am working to learn new ideas about electrochemistry and how batteries work, I find myself really wanting to “short-circuit” that time. I don’t mean I want to cheat out of the real work of learning something, so much as force myself to do all that deep engagement and mental wrestling the first time I’m exposed to something rather than the second, or third, or…</p>

<p>Anyway, what does all this have to do with impostor syndrome? Well, as tends to happen whenever one (who is prone to this sort of thing) makes a “jump” up the hierarchy, I’ve been wrestling with it again lately. As is the common manifestation for me, it’s not so much the <em>“Am I good enough?”</em> kind of insecurity (I actually do believe I’m smart enough to be doing what I’m doing) but rather the <em>“Do I work hard enough?”</em> kind. In the back of my mind, I’m always asking myself, <em>“Can I really kick it in academia if I need to sleep 8.5 hours per night? If I’m committed to exercising six days per week? If I don’t want to do (much) work on weekends?”</em></p>

<p>Graduate school at MIT is in some sense a trial by fire in dealing with this type of thing because you’re inevitably surrounded by people who work longer hours than you, who publish more papers, who get more awards, etc. etc. etc. And so while I still deal with impostor syndrome, I’ve developed some coping mechanisms. For example, I’ve realized that most (possibly all) of those people who are in the office/lab for 14 hours a day are not actually working during all of that time!</p>

<p>And this is where it connects back to the idea of a mental model. My mental model for the behaviors and thoughts of the people around me was wrong! That seeming superhuman who is at her desk from her breakfast of a quart of coffee until well past her dinner of two pieces of toast? She spent an hour of that time reading the Guardian, half an hour shooting the breeze with labmates, two hours in group meeting, another hour napping…maybe it should have been obvious, but sitting in front of your computer doesn’t mean you’re working! But once your model is proven wrong once, it’s on you to dismantle the other assumptions that faulty model led to in your mind!</p>

<p>Another conversation Jerry and I come back to again and again is the question of how many hours of real work one can get done in a good day. Our consensus answer is about five hours. Even on the most mystically productive day, you’ve got (maybe!) five hours of real, focused brain time – probably less if you’re underslept, or sick, or just stressed. Everything else is meetings, or emails, or twiddling your thumbs and reading Twitter (<a href="https://twitter.com/intent/tweet?text=%40rachel_kurchin%20&amp;original_referer=https://clicktotweet.com&amp;related=clicktotweet">@ me</a> if you think I’m wrong about this. I’m genuinely curious for others’ honest assessment of this number). Of course, all (well, some) of those things are also necessary for professional success! Personally, as a morning person, I try as best as I can to shunt as many of those things as possible to the after-lunch hours when I tend not to be at my mental peak anyway. But time management strategies could be a whole other post.</p>

<p>Anyway, I’ve rambled a bit, but my point is that something I actively try to do now is question my assumptions about what other people are doing with their time or (perhaps even more importantly) what they’re thinking about how I spend my time. I think it’s worth the effort to actively engage with these thoughts in order to refine my own mental model of the people around me, and maybe, if my career doesn’t crash and burn, I’ll eventually prove to myself (and to everyone else!) that I can do this, in my own way and in my own time. And also learn electrochemistry.</p>]]></content><author><name>Zihao Xu</name><email>zihao.xu@rutgers.edu</email></author><category term="impostor_syndrome" /><category term="pedagogy" /><category term="musings" /><summary type="html"><![CDATA[Cross-pollination of ideas about STEM pedagogy and minority representation.]]></summary></entry><entry><title type="html">Why Hello There…</title><link href="https://shsjxzh.github.io/posts/2019/09/blog-post-1/" rel="alternate" type="text/html" title="Why Hello There…" /><published>2019-09-26T00:00:00-04:00</published><updated>2019-09-26T00:00:00-04:00</updated><id>https://shsjxzh.github.io/posts/2019/09/first</id><content type="html" xml:base="https://shsjxzh.github.io/posts/2019/09/blog-post-1/"><![CDATA[<p>Hi there! I thought I’d start using the blog feature to share my thoughts and/or cool little side projects I take on for fun. This post is really just a placeholder commemorating the fact that I figured out how to turn the blog option back on for the site template. Look forward to some actual content hitting this space soon!</p>]]></content><author><name>Zihao Xu</name><email>zihao.xu@rutgers.edu</email></author><category term="misc" /><summary type="html"><![CDATA[Hi there! I thought I’d start using the blog feature to share my thoughts and/or cool little side projects I take on for fun. This post is really just a placeholder commemorating the fact that I figured out how to turn the blog option back on for the site template. Look forward to some actual content hitting this space soon!]]></summary></entry></feed>