<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Breaking Bytes</title><link href="https://breakingbytes.github.io/" rel="alternate"/><link href="https://breakingbytes.github.io/feeds/all.atom.xml" rel="self"/><id>https://breakingbytes.github.io/</id><updated>2025-02-17T00:00:00-08:00</updated><subtitle>My personal blog about &lt;a rel="me" href="https://fosstodon.org/@mikofski"&gt;me&lt;/a&gt;</subtitle><entry><title>Climbing Life Lessons: Failure</title><link href="https://breakingbytes.github.io/climbing-life-lessons-failure.html" rel="alternate"/><published>2025-02-17T00:00:00-08:00</published><updated>2025-02-17T00:00:00-08:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2025-02-17:/climbing-life-lessons-failure.html</id><summary type="html">&lt;p&gt;Failure is inevitable and also a learning opportunity, so how you deal with it matters.&lt;/p&gt;</summary><content type="html">&lt;h1 id="life-lessons-from-failure"&gt;Life Lessons from Failure&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;Failure is inevitable, learn if you can&lt;/li&gt;
&lt;li&gt;Prepare &amp;amp; know your competencies&lt;/li&gt;
&lt;li&gt;Value the experience of others&lt;/li&gt;
&lt;li&gt;Show the proper humility &amp;amp; respect&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="failure-is-inevitable"&gt;Failure is inevitable&lt;/h1&gt;
&lt;p&gt;It's important to accept that mistakes happen to everyone. If we're lucky, and the consequences aren't terrible, we have a chance to learn something. Then our mistakes aren't what define us, it's how we handle them that matters. Thinking back I've had lots of chances to learn from my mistakes on the rock, in the backcountry, and even in the gym, so I guess I've been pretty lucky. I think my climbing failures come in four flavors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unknown risk&lt;/li&gt;
&lt;li&gt;inexperience &amp;amp; insufficient skill&lt;/li&gt;
&lt;li&gt;carelessness&lt;/li&gt;
&lt;li&gt;hubris&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="matthes-crest"&gt;Matthes Crest&lt;/h2&gt;
&lt;p&gt;One of my earliest climbing experiences was on Matthes Crest in the spectacular high country between Tuolumne Meadows and above Yosemite Valley.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Matthes crest Yosemite backcountry view from west side" src="images/Matthes_crest_yosemite_backcountry_view_from_west_side.jpg"&gt;
&lt;em&gt;Image credit: &lt;a href="https://www.yosemiteclimbing.org/"&gt;Yosemite Climbing Association&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Unfortunately, our traverse of this magnificent route was cut short when I fell a small distance landing on my back on some rocks. It knocked the wind out of me, and I bruised my ribs bad enough that it was hard to breathe. I was so embarrassed that I couldn't stop laughing which was also painful. What happened? Why did I fall on such an easy route? I think I can chalk this up to a combination of unknown risk, inexperience, carelessness, and hubris. I had never climbed outdoors before, and falling wasn't my only mistake on this route. I also lost my belay device which fell off my locking carabiner that either wasn't locked or somehow unscrewed itself. There wasn't a lot of exposure where I fell, but I failed to evaluate the risk and decided on a risky boulder move, without considering the consequences. I was clearly careless, and I think I was overconfident. The rock is merciless and quick to deal out stern lessons whether you have the aptitude to learn from them or not. &lt;/p&gt;
&lt;p&gt;Another climber also injured on Matthes Crest offered this piece of wisdom in an &lt;a href="https://publications.americanalpineclub.org/articles/13200505200/Fall-on-RockJumped-Instead-of-Down-Climbing-California-Yosemite-National-Park-Tuolumne-Meadows-Matthes-Crest"&gt;AAC incident report&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One lesson is: no matter how easy the climb is you have to pretend it’s the hardest thing you ever did.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="sons-of-yesterday-serenity-crack"&gt;Sons of Yesterday + Serenity Crack&lt;/h2&gt;
&lt;p&gt;The most terrifying fall I ever had was on the rappel between Sons of Yesterday and Serenity Crack. A similar incident was reported in this article in &lt;a href="https://www.climbing.com/skills/analysis-five-yosemite-climbing-accidents/"&gt;Climbing Magazine: "Five Preventable Yosemite Climbing Accidents"&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Multiple such incidences have occurred at this location, sometimes injuring the party.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In my story however the climber ahead of me also spun out of control, so I had warning that I would be working against gravity to make it to the anchors, but I didn't have the skill or experience to avoid spinning out of control and over the edge. Luckily, the first climber, had already tied the rope ends to the anchor, and my helmet kept me safe, but I still bashed my hand and fingers and ripped open a large gash on the back of my shoulder. I still consider how lucky I am to be alive, and that I had a more experienced climber with me.&lt;/p&gt;
&lt;h2 id="the-warm-up-wall-owens-river-gorge"&gt;The Warm Up Wall, Owens River Gorge&lt;/h2&gt;
&lt;p&gt;The most painful and embarrassing fall was near Bishop at a popular climbing area that I have been to many, many times. I was careless and foolish, being silly, talking too much, and not paying close attention while on lead, and before I knew it I'd slipped, fell a short distance, and hit a ledge twisting my ankle and nearly flipping before being caught by my last piece of protection. I felt like the climbing gods had struck me down for disrespecting them, and I deserved it.&lt;/p&gt;
&lt;h2 id="the-gym"&gt;The Gym&lt;/h2&gt;
&lt;p&gt;My most recent injury happened bouldering at the gym. Having been stymied all night because I am so horribly out of shape, I insisted on topping out on a high ball after already downclimbing because I didn't feel safe. I wish I'd followed that first instinct, because on my second attempt, I was too tired to downclimb and dropped several feet to the mat, losing my balance and landing badly on my arm which twisted at my elbow. Later while icing my elbow, I saw someone else top out, and realized there were rungs set next to the route to easily downclimb to a safe height before dropping. I really wish I had scoped out that landing better or talked to some other climbers before attempting it the second time. Many months later, my arm still hurts, and I had several visits to doctors and x-rays to make sure nothing was broken. This lesson was a bitter pill to swallow, after climbing for so many years, that I still had so much more to learn.&lt;/p&gt;
&lt;h2 id="lessons-learned"&gt;Lessons Learned&lt;/h2&gt;
&lt;p&gt;Be prepared for the risk. Learn what skills are required and know your competencies. Go with more experienced climbers, listen to them, and follow what they do. Pay attention, be serious, and don't fool around. Don't be afraid to back off of a climb for safety, even at the gym. Scope out your approach, your route, your landing, and your descent carefully before committing. Talk with others and get the beta. And when accidents happen, take the time to do the hard work to learn from them, bounce back, and then keep climbing!&lt;/p&gt;</content><category term="Climbing Life Lessons"/><category term="project-management"/><category term="project-planning"/><category term="research"/><category term="rant"/><category term="time-management"/><category term="risk-management"/></entry><entry><title>PV Electric Mismatch in Silicon-Cell PV - Part 2</title><link href="https://breakingbytes.github.io/pv-electric-mismatch-in-silicon-cell-pv-part-2.html" rel="alternate"/><published>2024-08-23T01:10:00-07:00</published><updated>2024-08-23T01:10:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2024-08-23:/pv-electric-mismatch-in-silicon-cell-pv-part-2.html</id><summary type="html">&lt;p&gt;What happens when shade cuts across PV strings?&lt;/p&gt;</summary><content type="html">&lt;blockquote&gt;
&lt;p&gt;Note: This post is part of a joint blog with my colleague Kurt Rhee:
&lt;a href="https://kurt-rhee.github.io/2024/08/26/a-primer-on-electrical-mismatch"&gt;Primer on Electrical Mismatch&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="the-many-shades-of-pv-electrical-mismatch"&gt;The many shades of PV electrical mismatch&lt;/h1&gt;
&lt;p&gt;Effects from shade are complicated, but can be summarized in two orthogonal categories:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="#shade-parallel-to-strings"&gt;shade parallel to strings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#shade-perpendicular-to-strings"&gt;shade perpendicular to strings&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These categories were defined in the Fast Shade Model [&lt;a href="#references"&gt;1, 2&lt;/a&gt;]
developed by Dr. Bennet Meyers after simulating hundreds of different shade
patterns and grouping them by their electrical mismatch. &lt;/p&gt;
&lt;h2 id="shade-parallel-to-strings"&gt;shade parallel to strings&lt;/h2&gt;
&lt;p&gt;One example of shade across all modules that is parallel to strings, is row-to-row
shade in fixed-tilt systems, typically in winter. When I originally wrote
about &lt;a href="https://breakingbytes.github.io/electric-mismatch-in-silicon-cell-pv-is-not-intuitive.html"&gt;PV electrical mismatch&lt;/a&gt;, I analyzed
this type of shade using &lt;a href="https://sunpower.github.io/PVMismatch/"&gt;PVMismatch&lt;/a&gt;
to simulate shade across the bottom row of a single string of 10 modules in a
10 string system. The conclusion of that post was that the string performed as
well as the most shaded cell, so even though only the bottom cells were shaded,
the modules in the string lost most of their power. I shaded the bottom cells
80% to simulate only diffuse light, and the string lost roughly 80% of output.
The other 9 strings operated at full capacity, so the system only lost 8%. The
&lt;a href="https://www.nist.gov/el/energy-and-environment-division-73200/heat-transfer-alternative-energy-systems/photovoltaic-1"&gt;NIST ground mount array&lt;/a&gt;
is an example of a system that will have row-to-row shade in winter that will
cause most of the strings to lose almost all of their output even when only
the bottom row of cells is shaded.&lt;/p&gt;
&lt;p&gt;&lt;img alt="NIST Google" src="./images/nist-ground-array.png"&gt;&lt;/p&gt;
&lt;h2 id="shade-not-parallel-to-strings"&gt;shade not parallel to strings&lt;/h2&gt;
&lt;p&gt;However, that post also contained a picture of a rooftop with non-uniform shade
that was not consistent across each module of the string. The shade cast from
the roofline cut diagonally across the modules in the string, which was wrapped
in two rows to fit.&lt;/p&gt;
&lt;p&gt;&lt;img alt="non uniform shade on a roof" src="./images/20150923_170418.jpg"&gt;&lt;/p&gt;
&lt;p&gt;I didn't analyze the shade from this system in that post, so it raises the
question whether the rule of thumb I recommended would still apply?&lt;/p&gt;
&lt;h2 id="shade-perpendicular-to-strings"&gt;shade perpendicular to strings&lt;/h2&gt;
&lt;p&gt;To simplify the question, the rest of this post analyzes a PV system with a
shade obstacle like a wind turbine, a telephone pole, or a chimney, that casts shade
perpendicular to the strings. My analysis is in this Jupyter notebook on Google Colaboratory:
&lt;a href="https://colab.research.google.com/drive/1b2Ll7G-4WBKPl57m-FPBhU8MLjLOTfIb"&gt;&lt;code&gt;mismatch_vs_strings.ipynb&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TL;DR: When shade cuts perpendicular to strings, cells go into reverse bias,
bypass diodes activate in the shaded submodules, and the other modules operate
at higher voltage to match the voltage of unshaded parallel strings.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I simulated perpendicular shade on the first half of the first module in the
string, while the rest of the strings were unshaded. For example, this shade could be caused by a
chimney. Then I increased the number of unshaded strings to see if it changed
the effect. The effect of a shadow perpendicular to the string caused bypass
diodes to trigger. Adding more strings did not stop the bypass diodes from triggering, even after 20 parallel
strings. The IV curve of the system had a kink until 4 unshaded parallel strings were added, but after 9 unshaded parallel strings were added, the IV curve
appeared unaffected. After 19 unshaded parallel strings were added, the total power loss was only 0.85% for the system
compared to unshaded. However, the power loss in the shaded string was about 15%.&lt;/p&gt;
&lt;p&gt;Here is the IV curve of the 20 string PV system with perpendicular shade on the
1st module of the 1st string from the Jupyter notebook. It looks unaffected!&lt;/p&gt;
&lt;p&gt;&lt;img alt="20 string PV system with cross-string shade" src="./images/cross-string-mismatch/pvsystem-20strings.png"&gt;&lt;/p&gt;
&lt;p&gt;Now check out the IV curve of the string with the shaded module. It should
be generating about 3200[W], but even though it's lost about 500[W], it
still operates at 5[A], nearly the same current as the other strings. It
still has to operate at the same voltage as the other strings, 538.7[V] in
this example, so how does it do it with 2 bypass diodes activated?&lt;/p&gt;
&lt;p&gt;&lt;img alt="20 string PV system with cross-string shade" src="./images/cross-string-mismatch/pvstring-20strings.png"&gt;&lt;/p&gt;
&lt;p&gt;A look at the module IV curves tells the rest of the story. The shaded module
still has to carry the 5[A] of the string, but 2 bypass diodes are triggered
so the voltage is down 75%. Note: these are SunPower/Maxeon 320[W] modules,
that have 96-cells in 8-columns with 3 bypass diodes in a 24-48-24 cell arrangement.&lt;/p&gt;
&lt;p&gt;&lt;img alt="20 string PV system with cross-string shade" src="./images/cross-string-mismatch/pvmod0-20strings.png"&gt;&lt;/p&gt;
&lt;p&gt;However, the unshaded modules make up for the lost voltage in the shaded
module by operating just above the max power point. This is why the string
is operating at 5[A], to increase the voltage in the unshaded modules.
Very clever! Go team! Luckily for the shaded module, that current is
also very close to its max power point, which is only down 75% thanks
to the activated bypass diodes. Recall in the parallel shade scenario,
the entire string was down.&lt;/p&gt;
&lt;p&gt;&lt;img alt="20 string PV system with cross-string shade" src="./images/cross-string-mismatch/pvmod1-20strings.png"&gt;&lt;/p&gt;
&lt;p&gt;Please check out &lt;a href="https://colab.research.google.com/drive/1b2Ll7G-4WBKPl57m-FPBhU8MLjLOTfIb"&gt;&lt;code&gt;mismatch_vs_strings.ipynb&lt;/code&gt;&lt;/a&gt;
because the 1-string example isn't limited by the voltage of parallel strings, so it's free to operate at the max power
point, and the losses are lower. Recall in the 20-string example, the shaded string lost about 15%, but in the 1-string
example, the loss was only about 8%. Another variation is allowing the shade to cross two or more strings. I covered
a scattershot of scenarios and found that parallel string voltage began to dominate sowmhere between 5 to 10 strings.
Of course, that only applies in this contrived example, but it was interesting nonetheless.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I wish I could say, that's all there is to it, but as I said in my first blog post,
electrical mismatch in crystalline silicon is very counter-intuitive.
That's why I created PVMismatch to begin with. I was tired of guessing and
being wrong. So don't guess. Simulate with confidence, try PVMismatch, and
let me know what you learn!&lt;/p&gt;
&lt;h2 id="epilogue"&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;So back to that rooftop with the diagonal shade line. It's a bit of both
categories right? How do you think it will perform? Will it lose nearly
all of its power or will bypass diodes active and save the day? Or maybe
something in between or completely different. Try to analyze it using
PVMismatch. If you need help I analyzed it in this Google Colab notebook:
&lt;a href="https://colab.research.google.com/drive/1wOSF9aNvxUc2t1iduNKN1Dn-vBW_j92w"&gt;&lt;code&gt;nonuniform-rooftop-shade.ipynb&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Meyers, B., Mikofski, M. A., &amp;amp; Anderson, M. (2016). A Fast Parameterized Model for Predicting PV System Performance under Partial Shade Conditions. In IEEE (Ed.), 2016 IEEE 43rd Photovoltaic Specialists Conference (PVSC) (pp. 3173–3178). IEEE. &lt;a href="https://doi.org/10.1109/PVSC.2016.7750251"&gt;https://doi.org/10.1109/PVSC.2016.7750251&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Meyers, B., &amp;amp; Mikofski, M. A. (2017). Accurate Modeling of Partially Shaded PV Arrays. In IEEE (Ed.), 2017 IEEE 44th Photovoltaic Specialist Conference (PVSC) (Vols. 2017-June, pp. 3354–3359). IEEE. &lt;a href="https://doi.org/10.1109/PVSC.2017.8521559"&gt;https://doi.org/10.1109/PVSC.2017.8521559&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content><category term="Solar"/><category term="solar"/><category term="modeling"/><category term="code"/></entry><entry><title>Climbing Life Lessons: Reassessing Changing Risk</title><link href="https://breakingbytes.github.io/climbing-life-lessons-reassessing-changing-risk.html" rel="alternate"/><published>2023-07-14T00:00:00-07:00</published><updated>2023-07-14T00:00:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2023-07-14:/climbing-life-lessons-reassessing-changing-risk.html</id><summary type="html">&lt;p&gt;Reassessing risk as conditions change is critical in the backcountry.&lt;/p&gt;</summary><content type="html">&lt;h1 id="reassessing-risk-in-mountains"&gt;Reassessing Risk in Mountains&lt;/h1&gt;
&lt;p&gt;&lt;a href="http://www.supertopo.com/rock-climbing/Yosemite-Valley-Half-Dome-Snake-Dike"&gt;Snake Dike&lt;/a&gt;
is on the southwest shoulder of Half Dome, perhaps the most recognizable rock face in
Yosemite Valley. Although considered a moderate climb, rated 5.7 R, it's sometimes called
"Snake Hike" because it's a serious commitment with huge exposure.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike Approach" src="images/P2010025.JPG"&gt;&lt;/p&gt;
&lt;p&gt;Understanding risk on Snake Dike is important. This climb requires planning, good timing,
a bit of skill, and lots of luck. More importantly though, you need to be constantly
aware of changing conditions, how they can affect your risk, and whether bailing is still
an easy option. Evaluating risk is one of climbing's life lessons.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike Rainout" src="images/P2010019.JPG"&gt;&lt;/p&gt;
&lt;p&gt;Snake Dike is an all day climb. The approach is a 6 mile, 3-hour hike up 2500-ft that starts
at Happy Isles behind Curry Village and follows the Mist Trail, one of my favorites.
Unfortunately, our 4am start was quickly foiled by rain, but our good timing gave us some
flexibility, so we decided to wait it out for at least an hour and then reassess. Lucky for us
our patience was rewarded, and within the hour we were back on the trail. We decided to pick up
the pace a bit to make up for the lost time. Getting caught in a thunderstorm can have deadly
consequences for climbers, so even though there was no rain in the forecast that day, we didn't
know if the weather would turn bad again later that afternoon. Same as for climbers, descending
the cables from Half Dome in a thunderstorm is a death sentence. We also didn't want to hike out
in the dark, although we had brought our torches and some emergency overnight gear just in case.
There's always trade offs to consider in the backcountry, so we tried to pack as lightly as we
could to move fast yet still be safe. There's a saying in the mountains:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Speed is safety.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It means don't waste time, so we didn't. Ok, well maybe we stopped to take a few photos of the
breathtaking views near Lost Lake.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike Photo Opp" src="images/P2010026.jpg"&gt;&lt;/p&gt;
&lt;p&gt;I had climbed Snake Dike once before with a large group of experienced climbers, and I'd been on the
Mist Trail numerous times, so I had good intuition for finding the right path to turn off the trail,
and miraculously we nailed the approach. We ate a quick snack, tied on our climbing shoes, strapped
on our harnesses, clipped on a light rack (mostly slings &amp;amp; quickdraws), and tackled the first pitch
which is actually one of the 5.7 cruxes of the climb. The "R" stands for "runout" which means there's
no protection. That pretty much describes most of Snake Dike, but the first pitch is especially gnarly.
It starts on typical Yosemite glacial polish with tiny microedges that you can friction climb up to a
roof where you can finally place your first solid piece and exhale a huge sigh of relief. If you slip on
this slab, you'll get some serious scrapes as you tumble back to the base of the climb. In researching
for this blog, I read an
&lt;a href="https://www.nps.gov/yose/blogs/rescue-from-near-snake-dike-half-dome.htm"&gt;incident report from YOSAR&lt;/a&gt;
about a couple that were off route but slipped on similar frictiony slab, tumbled to the base, and
dragged the belayer, who was not clipped to anything, off her ledge and into a talus field. She had a
punctured lung, a fractured pelvis, and broke two vertebra. Climbing can be especially dangerous if
you're unlucky, don't  take precautions, and fail to assess your risk carefully. I'm not trying to
scare you, lots of things are dangerous, that's why it's important to know your risk and make conscious
decisions. Anyway, I pulled the roof, shimmied up to the ledge and made the only anchor with pro I'd
need during the entire climb. From there on out is was bolt city.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike First Anchor" src="images/P2010027.JPG"&gt;&lt;/p&gt;
&lt;p&gt;That's the SERENE anchor, a cordelet with 3 equalized cams, &lt;em&gt;a la&lt;/em&gt;
&lt;a href="http://falcon.com/books/9780762782079"&gt;climbing anchors by John Long&lt;/a&gt;. Pay attention
to that black webbing because it's got a bit part in this story later on. I swear
my second has a backup clove hitch on a 4th piece out of the frame you can't see,
so he can escape the belay.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike Crux Pitch" src="images/P2010028.JPG"&gt;&lt;/p&gt;
&lt;p&gt;There's one more sketchy traverse on frictiony slab protected by a single bolt,
and then the rest of Snake Dike is all jugs and 5 easy. You should download the
&lt;a href="http://www.supertopo.com/topos/yosemite/snakedike.pdf"&gt;free supertopo&lt;/a&gt; if you're
serious about climbing Snake Dike. I had tried to memorize it, because there are so
many bolts on this climb that it's easy to either stop early or skip an anchor.
People talk about girth hitching chicken heads but the dike doesn't really take
protection. It's just a long juggy extrusion that you follow like a ladder.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike The Dike" src="images/P2010029.JPG"&gt;&lt;/p&gt;
&lt;p&gt;All the rest of the pitches are juggy and positive but super runout with just a single
bolt every 60 feet or so. In fact, some of the pitches don't have &lt;em&gt;any&lt;/em&gt; bolts!
Another interesting twist is that every so often you come across a pair of bolts that
looks like an anchor, but is for shorter ropes. Knowing where to belay is part of route
finding that can speed up your climb and remember, "speed is safety!" Clip the
wrong bolts though, and you might find yourself and your partner simul-climbing.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike Runout Bolts" src="images/P2010030.JPG"&gt;&lt;/p&gt;
&lt;p&gt;The climbing was smooth, and the views were spectacular. Take a moment to
appreciate this sweeping vista starting to your left past Glacier Point right
to the valley floor. Is that El Cap off in the distance? &lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike View L" src="images/P2010034.JPG"&gt;
&lt;img alt="Snake Dike View M" src="images/P2010033.JPG"&gt;
&lt;img alt="Snake Dike View R" src="images/P2010032.JPG"&gt;&lt;/p&gt;
&lt;p&gt;Look just a little further to the left though, and you can see what I was seeing.
Dark clouds gathering around Liberty Cap and Mt. Broderick were moving our way. We
needed to pick up the pace, or we would end up with only bad options.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike Dark Clouds" src="images/P2010036.JPG"&gt;
&lt;img alt="Snake Dike Liberty Cap &amp;amp; Mt. Broderick" src="images/P2010035.JPG"&gt;&lt;/p&gt;
&lt;p&gt;Is there anything that is purely deterministic? I believe almost every event has
a distribution of possible outcomes, some good, some great, some bad, and some
very bad. Lucky or not, those possibilities aren't usually random, some outcomes
are more likely than others. So now I had to weigh the odds that we could summit
and descend before it started raining. The main danger for climbers in the rain is
lightning. For a quick primer check out this Climbing Magazine article,
&lt;a href="https://www.climbing.com/skills/learn-this-laws-of-lightning/"&gt;Don't Get Struck By Lightning&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In my haste, I made a tactical error. After clipping a pair of anchor bolts,
I decided to pass them thinking they were for shorter ropes, but before I knew
it I was literally at the end of my rope. I had clipped the bolts between me and
my second, and I knew there was another pair of anchors just up ahead. I felt confident
so I decided to start simul-climbing, and I yelled down to my partner, "START CLIMBING!"
A 60-meter rope is almost 200-ft long, and in the vastness, sounds get blown away, so I
just started pulling on the rope forcing him to follow me upward. This worked for a few
feet but then I was yanked to a stop. I could see the next pair of anchors, but they were
just out of reach. I turned around to start screaming frantic, feeling the buzz
of adrenaline shoot into my veins, when I saw my partner take off his backpack. I was
completely confused. Dread crept into my bones as I saw something flutter off into
the breeze, but I was quickly filled with relief as my partner slung his backpack
and lept upward. I flew to the anchors, clipped them like a baby hugging his mother
and hauled up my second. "What the heck was that?" I asked as he got closer, and he
explained that he had climbed to the end of that black piece of webbing, but it was
too tight, and he couldn't reach down to unclip the locking carabiner I'd lent him,
so he cut the sling and left the biner. It was a small price I was happy to pay.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike Scramble" src="images/P2010037.JPG"&gt;&lt;/p&gt;
&lt;p&gt;Mini epic almost over, the rest of the climb was uneventful. We reached the third class
scramble while there were still patches of blue in the cloudy skies, but once on the
summit, while taking victory photos and scarfing food, it started snowing and we
knew we had to sprint down the cables. &lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike Snow" src="images/P2010039.jpg"&gt;&lt;/p&gt;
&lt;p&gt;We reached the bottom and started planning our celebration with cocktails at the
Mountain Bar at the Lodge. There we reflected on the choices we made during the
day as the conditions changed, and we reassessed our risk. Our planning didn't
consider the surprise mini-epic, but a little skill and a lot of luck kept us
safe. I don't think either of us were particularly risky, even before we had kids.
Sure, there's a spectrum, and each person has to make a conscious choice of the
risks they're willing to take, but to do that effectively requires understanding
and evaluating the risk especially as conditions change. &lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake Dike Me on the Diving Board" src="images/P2010041.JPG"&gt;&lt;/p&gt;
&lt;p&gt;Here's me 20 years ago, after that Snake Hike, on the infamous diving board,
overlooking the valley. I reflect back on my climbing experiences, and I realize
that of the many valuable lessons I learned in the mountains, assessing risk
has been one of the most useful. Understanding uncertainty has helped me navigate
an often uncertain world, and to realize that as conditions change, I can re-evaluate
my risk and make different choices. You can too. Thanks for reading!&lt;/p&gt;
&lt;p&gt;If you like this Climbing Life Lesson, check out this one on
&lt;a href="https://breakingbytes.github.io/climbing-life-lessons-offwidths.html"&gt;Offwidths&lt;/a&gt; or why it's better to stick
with the plan rather than making last minute changes.&lt;/p&gt;
&lt;h2 id="my-notes"&gt;My Notes:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Any event has a distribution of possible outcomes, some that are more favorable than others and some that are definitely undesirable.&lt;/li&gt;
&lt;li&gt;The distribution might not be random, in other words some outcomes might be more likely than others.&lt;/li&gt;
&lt;li&gt;Risk assessment is a measure of the possibility of favorable and undesirable outcomes.&lt;/li&gt;
&lt;li&gt;Each person's or party's tolerance for risk is a choice that depends on trade offs between the chances of favorable vs. undesired outcomes.&lt;/li&gt;
&lt;li&gt;changing conditions force you to reevaluate the risk and make new choices.&lt;/li&gt;
&lt;li&gt;Sometimes conditions and risk can change quickly, leaving you exposed to greater risk.&lt;/li&gt;
&lt;li&gt;Exposure in theoretical terms is a measure of the level risk you've accepted but in practical terms it's measured in how far or long it will take to escape danger and get to help.&lt;/li&gt;
&lt;li&gt;climbing in the mountains automatically increases your exposure because it can be difficult to escape the rock safely.&lt;/li&gt;
&lt;li&gt;climbing in the backcountry can increase exposure significantly because in addition to a safe retreat you may also need to hike and drive significant distances before you can get help.&lt;/li&gt;
&lt;li&gt;Dangers in the mountains include route finding, getting caught in bad weather, darkness, lightning strikes, equipment failure, missing or losing equipment, falls, injuries, death&lt;/li&gt;
&lt;li&gt;learning to evaluate risk in changing conditions is an important skill that can have practical advantages in all aspects of your life.&lt;/li&gt;
&lt;li&gt;it can help you tolerate uncertainty, because reality is both full of possibilities and uncertainty&lt;/li&gt;
&lt;li&gt;it can help you rule out unlikely or scenarios with no serious consequences&lt;/li&gt;
&lt;/ul&gt;</content><category term="Climbing Life Lessons"/><category term="project-management"/><category term="project-planning"/><category term="research"/><category term="rant"/><category term="time-management"/><category term="risk-management"/></entry><entry><title>OSS Organization as Leadership</title><link href="https://breakingbytes.github.io/oss-organization-as-leadership.html" rel="alternate"/><published>2022-05-30T00:00:00-07:00</published><updated>2022-05-30T00:00:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2022-05-30:/oss-organization-as-leadership.html</id><summary type="html">&lt;p&gt;The best model of leadership is open source.&lt;/p&gt;</summary><content type="html">&lt;h1 id="open-source-organization-as-leadership"&gt;Open Source Organization as Leadership&lt;/h1&gt;
&lt;p&gt;It starts with the premise that everyone is curious and compassionate. Maybe this is what &lt;a href="https://youtu.be/u6XAPnuFjJc"&gt;Dan Pink meant in his RSA Animate talk&lt;/a&gt;. He asked, "What motivates us?" His findings were autonomy, mastery, and purpose. Not money. Not leisure. Not luxury. Humans are neither lazy, stupid, nor greedy. In fact we're the opposite. This is why &lt;a href="https://poquitopicante.blogspot.com/2011/07/second-law-of-infodynamics.html"&gt;the second law of infodynamics&lt;/a&gt; is true: we steadily and continuously, against all odds and the sublime nature of the universe, continue to learn, improve, and thrive together as a species.&lt;/p&gt;
&lt;p&gt;Okay. Again assuming this premise, so then good leadership takes advantage of these axioms by providing a framework for their organization that fosters autonomy, allows individuals to seek mastery, and creates a strong sense of common purpose. In practice this means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Collaboration&lt;/li&gt;
&lt;li&gt;Communication&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Constructivism_(philosophy_of_education)"&gt;Constructivism&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Leadership that models and follows these three C's will be stronger, faster, and smarter than its opposites. An organization that is punitive, divisive, and siloed will be trapped, wither, and die.&lt;/p&gt;
&lt;p&gt;In practice this type of organization has the following characteristics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flat structure&lt;/li&gt;
&lt;li&gt;Rotating leadership&lt;/li&gt;
&lt;li&gt;Frequent informal meetings&lt;/li&gt;
&lt;li&gt;Free time&lt;/li&gt;
&lt;li&gt;Opportunities to change job description&lt;/li&gt;
&lt;li&gt;Strong mission, with loose long term planning, but detailed short term planning&lt;/li&gt;
&lt;li&gt;Detailed diagnostics&lt;/li&gt;
&lt;li&gt;Ambitious goals&lt;/li&gt;
&lt;li&gt;An acceptance of failure&lt;/li&gt;
&lt;li&gt;Celebrates individuality&lt;/li&gt;
&lt;li&gt;Culture of pluralism&lt;/li&gt;
&lt;li&gt;An appreciation of small things&lt;/li&gt;
&lt;li&gt;Groom leaders and empower career growth&lt;/li&gt;
&lt;li&gt;Give positive feedback&lt;/li&gt;
&lt;li&gt;Share business constraints at all levels&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="flat-structure"&gt;Flat structure&lt;/h2&gt;
&lt;p&gt;In a 3C organization, everyone has an opportunity to be heard and be visible. This means there are less levels of leadership, more autonomy, but also more communication and collaboration.&lt;/p&gt;
&lt;p&gt;Think about a free open source software project like &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt;. There might be be an owner or two, a rotating group of maintainers, several contributors, and many users. This is the most successful business model. A product that is completely free, thrives for decades, continuously becomes better, grows, and fuels numerous new projects.&lt;/p&gt;
&lt;h2 id="rotating-leadership"&gt;Rotating leadership&lt;/h2&gt;
&lt;p&gt;The founder of an organization may be a visionary or a glorified figure head. She initially provided direction for the organization, but eventually, a group of leaders were chosen from the community to handle day to day functions. Some of these tasks are mundane yet critical, others creative and potentially ground breaking. If the organization draws strength from its members then many will be able to lead in different capacities. By rotating leadership responsibilities, the organization leverages every member's unique potential.&lt;/p&gt;
&lt;h2 id="frequent-informal-meetings"&gt;Frequent informal meetings&lt;/h2&gt;
&lt;p&gt;No one likes pointless meetings, so instead of waiting, in the age of &lt;a href="https://slack.com/"&gt;Slack&lt;/a&gt; and other instant messaging, communicate immediately when required. In fact all the time. Communication is the key that makes an organization truly agile and responsive. As soon as you know a products timeline shifts, the sooner you can plan contingencies.&lt;/p&gt;
&lt;h2 id="free-time"&gt;Free time&lt;/h2&gt;
&lt;p&gt;Without free time, ideas have no fertile soil to grow. Organizations that are overworked become dysfunctional and implode.&lt;/p&gt;
&lt;h2 id="opportunities-to-change-job-descriptions"&gt;Opportunities to change job descriptions&lt;/h2&gt;
&lt;p&gt;In an organization that cherishes individuals, members are encouraged to seek their true calling. That may mean they move on to new challenges, or that they create new roles for themselves within the organization. Flexibility to try on different roles empowers members to seek mastery in something they value.&lt;/p&gt;
&lt;h2 id="strong-mission-with-loose-long-term-planning-but-detailed-short-term-planning"&gt;Strong mission, with loose long term planning, but detailed short term planning&lt;/h2&gt;
&lt;p&gt;See &lt;a href="https://breakingbytes.github.io/iterative-project-planning-in-research-and-development.html#iterative-project-planning-in-research-and-development"&gt;iterative project planning&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="detailed-diagnostics"&gt;Detailed diagnostics&lt;/h2&gt;
&lt;p&gt;Measure what you treasure. Intuition and gut instincts will only get you so far, but data is gold. If you want your team to succeed, you need to have the facts. Data tells you where you can celebrate victories, and where you can trim the fat. Share the data with your team and make swift decisions.&lt;/p&gt;
&lt;h2 id="ambitious-goals"&gt;Ambitious goals&lt;/h2&gt;
&lt;p&gt;Go after what you really want. What makes your team come to work everyday at the crack of dawn? Small victories are important, but only if they are working towards a larger goal.&lt;/p&gt;
&lt;h2 id="an-acceptance-of-failure"&gt;An Acceptance of failure&lt;/h2&gt;
&lt;p&gt;Failure is the very nature of experimentation. You will fail. It's not a matter of if, only when. Model and demonstrate that failure is always an option, that we can learn valuable information, and that there's no judgement when we all put in our best efforts. Be supportive and always constructive.&lt;/p&gt;
&lt;h2 id="celebrate-individuality"&gt;Celebrate individuality&lt;/h2&gt;
&lt;p&gt;Seek different. Do not collect a team of clones. Encourage diversity and nurture it. Don't dismiss ideas that seem foreign to you. Instead embrace these as gems to be treasured and cherish the uniqueness of everyone on your team.&lt;/p&gt;
&lt;h2 id="culture-of-pluralism"&gt;Culture of pluralism&lt;/h2&gt;
&lt;p&gt;Include your team in decisions and strive for consensus. Share information and collaborate. We are stronger as a team. There are no heroes who have all the answers. When celebrating victories, recognize everyone. Work hard to reap the benefits of your team's rich &amp;amp; diverse culture.&lt;/p&gt;
&lt;h2 id="an-appreciation-of-small-things"&gt;An appreciation of small things&lt;/h2&gt;
&lt;p&gt;Celebrate little victories. Break work down into short tasks that can be more easily grokked. &lt;/p&gt;
&lt;h2 id="groom-leaders-and-empower-career-growth"&gt;Groom leaders and empower career growth&lt;/h2&gt;
&lt;p&gt;Always be working to replace yourself. Put others on a path to leadership. Learn what teammates crave and give it to them.&lt;/p&gt;
&lt;h2 id="give-positive-feedback"&gt;Give positive feedback&lt;/h2&gt;
&lt;p&gt;Positive feedback is not the same as praise. Practice sharing detailed examples of work that you value in private with your team. Always sandwich constructive feedback. A good relationship should always have a larger ratio of positive feedback.&lt;/p&gt;
&lt;h2 id="share-business-constraints-at-all-levels"&gt;Share business constraints at all levels&lt;/h2&gt;
&lt;p&gt;Your team needs to know what their motivation is, so give them the details. Don't hold back. If there's only $10 left in the bank, they won't make big plans at a time when earning cash is the priority.&lt;/p&gt;</content><category term="Leadership"/><category term="equity"/><category term="diversity"/><category term="inclusion"/><category term="rant"/></entry><entry><title>Identity at the crossroads</title><link href="https://breakingbytes.github.io/identity-at-the-crossroads.html" rel="alternate"/><published>2021-07-16T00:00:00-07:00</published><updated>2021-07-16T00:00:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2021-07-16:/identity-at-the-crossroads.html</id><summary type="html">&lt;p&gt;I was no one until I began to acknowledge my intersectionality.&lt;/p&gt;</summary><content type="html">&lt;h1 id="who-am-i"&gt;Who am I?&lt;/h1&gt;
&lt;p&gt;Even today, I'm still trying to figure out who I am. Several years ago I made a &lt;a href="https://poquitopicante.blogspot.com/p/about-me.html"&gt;bulleted list&lt;/a&gt;. I thought I'd covered everything, but identity can't be packaged up so neatly in a box with only six facets. It's more like last year's Christmas lights all tangled in knots. You pull one side and three other strands come out.&lt;/p&gt;
&lt;h1 id="ethnicity"&gt;Ethnicity&lt;/h1&gt;
&lt;p&gt;I am Cuban-American on my mother's side and Polish-Jewish-American from my father. Although, to be honest, my mother and father were separated before I was born, and while growing up I had never met my father or anyone in his family, so according to custom, I shouldn't be Jewish. However, my single mother wanted to keep all options open to me, so she raised me as a Jew, even though she herself had been raised Catholic. Therefore, I went to Hebrew school from the time I was 4 years old until 3rd grade, and I never learned Spanish, even though my grandmother who immigrated from Cuba in the 30's and spoke almost no English, frequently lived with us. You can imagine how confusing this was for me. Am I an Eastern European Jew or Latinx from the Caribbean? Even today, I still haven't figured out what my ethnicity is. I don't have a simple answer. I'm both, neither, and something else.&lt;/p&gt;
&lt;h1 id="immigration"&gt;Immigration&lt;/h1&gt;
&lt;p&gt;When my grandmother left Cuba, she was a teenager. She never had more than an elementary school education, and she came to New York City with the family whom she served. She married one of the family sons, my grandfather, had two daughters, and was suddenly widowed. Then, my grandmother, my mother, and my aunt were deported back to Cuba where they lived for almost four years. They were finally allowed to return to the U.S., where my grandmother earned her citizenship and raised her two children by herself, working as a seamstress. So am I first generation or second? My mother immigrated to this country, even though she was born here.&lt;/p&gt;
&lt;h1 id="sex-family"&gt;Sex &amp;amp; Family&lt;/h1&gt;
&lt;p&gt;Three women, worked hard, raising children, without the partners that begot them. My grandmother, in a foreign country, with little formal education, only broken English, and a skill set only a servant could acquire, with a will stronger than tool steel, forged ahead, alone, making sure both her children received the education she never had. The nuns were stern at their Catholic schools, striking my left-handed aunt so she would use her right, but my mother and her sister persevered and made it through high school.&lt;/p&gt;
&lt;p&gt;My mother went straight to community college to get her teaching credential and started working. Oh to be young and employed in New York City in 1969! How enchanting! The music, the poetry! Although it didn't work for my mother and father, she chose to keep me anyway, and like her mother made the sacrifices necessary to make sure I was healthy and happy and had more opportunities than she did as a child. We moved to California when I was three to be closer to my aunt who was also a single mother.&lt;/p&gt;
&lt;p&gt;That was my unconventional family: my mom, my aunt, my cousin, my grandmother, and me. There were never any men in our lives other than a few of our parents' coworkers  and other friends' fathers. I saw traditional families when I went to friends' houses. Lots of kids asked me about my father, if I knew him, or if I even had a father. Yep, it was weird, but to be honest, I always loved my family, and I couldn't even imagine it any other way. We always had love, and that's what matters.&lt;/p&gt;
&lt;p&gt;So I try to be a feminist, not just because I'm grateful of the women in my family, but because it's the right thing to be. Mothers shouldn't have to make sacrifices to raise their children, no one should. All humanity should raise up and respect parents who bring forth life, nourishing it, and model the goodness we wish to see in the world.&lt;/p&gt;
&lt;h1 id="gender"&gt;Gender&lt;/h1&gt;
&lt;h1 id="neurodiversity"&gt;Neurodiversity&lt;/h1&gt;
&lt;h1 id="sexuality"&gt;Sexuality&lt;/h1&gt;
&lt;h1 id="race-color"&gt;Race &amp;amp; Color&lt;/h1&gt;
&lt;h1 id="nationality"&gt;Nationality&lt;/h1&gt;
&lt;h1 id="intersectionality"&gt;Intersectionality&lt;/h1&gt;</content><category term="Diversity"/><category term="justice"/><category term="equity"/><category term="diversity"/><category term="inclusion"/><category term="rant"/></entry><entry><title>Case for Experts in Management</title><link href="https://breakingbytes.github.io/case-for-experts-in-management.html" rel="alternate"/><published>2021-05-22T00:00:00-07:00</published><updated>2021-05-22T00:00:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2021-05-22:/case-for-experts-in-management.html</id><summary type="html">&lt;p&gt;Empathy &amp;amp; a growth mindset are equally important to both managers &amp;amp; individual contributors.&lt;/p&gt;</summary><content type="html">&lt;h1 id="a-case-for-more-experts-in-management"&gt;A Case for More Experts in Management&lt;/h1&gt;
&lt;h2 id="intro"&gt;Intro&lt;/h2&gt;
&lt;p&gt;First off, let me say I don't think you need to be an expert to be a good manager. In fact, I don't even think you need to be an expert to be a good individual contributor. Sure it helps, but I believe there are other traits that are more important for making good technical contributions and better management decisions.&lt;/p&gt;
&lt;p&gt;A mentor once told me, when hiring, look for intelligence versus skillset, because a fast &amp;amp; flexible learner continues to be useful when a project ends, but a finite skillset will eventually be obsolete. My definition of intelligence has evolved, and now I think he was really talking about mindsets. I believe having empathy &amp;amp; a growth mindset might be even more important than expertise or leadership.&lt;/p&gt;
&lt;h2 id="maximize-diversity"&gt;Maximize Diversity&lt;/h2&gt;
&lt;p&gt;In my opinion, there's a common misconception that experts lack leadership skills, or that their unique skills are better utilized as individual contributors. However, this artificial perception blocks potential contributions from others with unique perspectives, and it creates a void of subject matter expertise &amp;amp; experience in leadership. Instead it reinforces the stereotype of the alpha-type boss and the nerdy, absent minded professor.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Morty the mad scientist stereotype" src="images/morty-back-to-future.jpeg"&gt;&lt;/p&gt;
&lt;p&gt;I strongly feel this lack of diversity in leadership &amp;amp; individual contributions is a real missed opportunity to maximize collaboration with unique voices &amp;amp; special experiences across an organization. This opportunity cost limits growth and sustainability, and I would bet these types of businesses die from starvation &amp;amp; cannibalism as experts give up, frustrated from a lack of voice, and leaders, failing to listen to these valuable perspectives, opt for short term gains and cash out before they crash &amp;amp; burn.&lt;/p&gt;
&lt;h2 id="a-new-model"&gt;A New Model&lt;/h2&gt;
&lt;p&gt;Instead of perpetuating this rigid 20th century hierarchy, I propose a new model where empathy &amp;amp; open mindedness are championed for all, and opportunities are encouraged for all maximizing diversity. If a contributor excels at research or analysis, that doesn't mean they can't learn to be an empathic leader. If their work is a safe &amp;amp; supportive environment that encourages stretch goals and embraces failure instead of punishing it, they can grow to accomplish anything, enhancing their perspectives and adding value to the organization.&lt;/p&gt;
&lt;p&gt;Conversely, why are non-technical workers pushed into management, limiting their opportunities to learn new skills and make unique contributions. I don't believe workers are just one thing and only have one value. They are constantly growing and bringing new values to the organization. &lt;/p&gt;
&lt;h2 id="t-shapes-growth-mindsets"&gt;T-Shapes &amp;amp; Growth Mindsets&lt;/h2&gt;
&lt;p&gt;Several reports have expressed the importance of lateral moves and T-shaped workers [&lt;a href="#references"&gt;1-4&lt;/a&gt;]. When a manager takes an assignment as an individual contributor they learn new skills and gain new perspectives that gives them both depth &amp;amp; breadth in their field. This allows them to synthesize new ideas and innovations they wouldn't have had if they'd remained siloed. Similarly when experts take on the responsibility of management, they are forced to delegate work, give up control, &amp;amp; learn the business constraints that limit what the organization can do. These perspectives are critical. Again, as with the manager, the expert now synthesizes these new ideas and innovates in ways they wouldn't have if they'd stayed in the lab. Both of these coworkers are now T-shaped. Instead of staying in fixed roles, by embracing a growth mindset, they have created synergy.&lt;/p&gt;
&lt;p&gt;This is what &lt;a href="https://profiles.stanford.edu/carol-dweck"&gt;Carol Dweck&lt;/a&gt; was encouraging when she wrote about motivation in her 2006 book &lt;em&gt;Mindset: The New Psychology of Success&lt;/em&gt; [&lt;a href="#references"&gt;5&lt;/a&gt;].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"Fixed-mindset individuals dread failure because it is a negative statement on their basic abilities, while growth mindset individuals don't mind or fear failure as much because they realize their performance can be improved and learning comes from failure." &lt;a href="https://en.m.wikipedia.org/wiki/Carol_Dweck#Mindset_work"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="empathy"&gt;Empathy&lt;/h2&gt;
&lt;p&gt;Empathy is always in short supply,  although we all know it to be the most valuable of human abilities. Would anyone of us be alive if it weren't for the caring love of our family &amp;amp; friends. At work we draw boundaries, as we should, but that should not exclude empathy from the workplace. In fact, I argue it's even more important. You want people to give their best, then they have to have a safe &amp;amp; supportive environment where they feel trusted and respected. No punitive action is ever useful at work. Positive &amp;amp; constructive feedback is based on the assumption of good faith. That everyone is pulling together and giving their best for mutual success. Where failure is not criticized, but a learning opportunity for all.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So in summary, don't box yourself in or your coworkers. Strive to create a culture where anything is possible, learning is continuous, and failure is always an option, and that's good. Encourage lateral moves and grow a T-shaped workforce. Listen to experts and encourage them step outside of their comfort zone to take a turn at the wheel, while leaders experiment and analyze data. Don't be afraid to mix it up. The result might be a more sustainable and profitable business. &lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://hbr.org/2001/03/introducing-t-shaped-managers-knowledge-managements-next-generation"&gt;&lt;strong&gt;Introducing T-Shaped Managers: Knowledge Management’s Next Generation&lt;/strong&gt; -- Morten T. Hansen &amp;amp; Bolko von Oetinger, Harvard Business Review, 2001&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hbr.org/podcast/2018/05/lateral-moves"&gt;&lt;strong&gt;Lateral Moves&lt;/strong&gt; -- Dear HBR Podcast, Harvard Business Review, 2018&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/pulse/value-t-shaped-people-mitchell-platt"&gt;&lt;strong&gt;The Value of T-Shaped People&lt;/strong&gt; -- Mitchell Pratt, LinkedIn, 2019&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/pulse/say-yes-lateral-move-you-marissa-lee"&gt;&lt;strong&gt;To Say Yes or No: Is a Lateral Move for You?&lt;/strong&gt; -- Marissa Lee, LinkedIn, 2020&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.penguinrandomhouse.com/books/44330/mindset-by-carol-s-dweck-phd/"&gt;&lt;em&gt;Mindset: The New Psychology of Success&lt;/em&gt;, Carol Dweck, Penguin Random House, 2007&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hbr.org/2016/01/what-having-a-growth-mindset-actually-means"&gt;&lt;strong&gt;What Having a “Growth Mindset” Actually Means&lt;/strong&gt; -- Carol Dweck, Harvard Business Review, 2016&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.theatlantic.com/education/archive/2016/12/how-praise-became-a-consolation-prize/510845/"&gt;&lt;strong&gt;How Praise Became a Consolation Prize:
Helping children confront challenges requires a more nuanced understanding of the “growth mindset.”&lt;/strong&gt; -- Christine Gross-Loh
, The Atlantic, 2016&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content><category term="Productivity"/><category term="project-management"/><category term="rant"/></entry><entry><title>Climbing Life Lessons: Offwidths</title><link href="https://breakingbytes.github.io/climbing-life-lessons-offwidths.html" rel="alternate"/><published>2021-05-01T00:00:00-07:00</published><updated>2021-05-01T00:00:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2021-05-01:/climbing-life-lessons-offwidths.html</id><summary type="html">&lt;p&gt;An offwidth is a difficult climb, too big for hand jams, but too small to chimney.&lt;/p&gt;</summary><content type="html">&lt;h1 id="life-lessons-from-an-offwidth"&gt;Life Lessons from an Offwidth&lt;/h1&gt;
&lt;p&gt;&lt;img alt="East Buttress" src="images/eastbutress-of-elcap-route.JPG"&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://www.supertopo.com/rock-climbing/Yosemite-Valley-El-Capitan-East-Buttress"&gt;East Buttress of El Capitan&lt;/a&gt; is one of the best but hardest climbs I've ever been on. It's long &amp;amp; sustained, with huge exposure &amp;amp; beautiful, sweeping views of the valley &amp;amp; Middle Cathedral. In typical Yosemite fashion, it challenges you right from the get go with a steep chimney overlooking a 1,000-foot drop. Then on the 9th pitch, after you've been climbing for a few hours, over 2,000 feet above the valley floor, there's a classic Yosemite offwidth. Like glacial polish, Yosemite offwidths are a ritual you can't avoid. You just have to stash your ego, open your heart, and humbly listen to what the great mother is trying to teach you.&lt;/p&gt;
&lt;p&gt;&lt;img alt="pitch 9 offwidth" src="images/9-ow-pitcheastbut2.JPG"&gt;&lt;/p&gt;
&lt;p&gt;That's me, 20 years ago, on that incredible, unforgettable flaring 9th pitch. An offwidth is a difficult climb. It's too big for hand jams, but too small to chimney. Everybody has advice for offwidths: armbars, heel-toe jams, chicken wings, kneebars, shoulder jams, handstacks, liebacks, you name it. About the only advice worth following comes from climbing and squirrel suit legend &lt;a href="https://stephdavis.co/blog/how-to-climb-offwidths-101/"&gt;Steph Davis&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I think the biggest thing with offwidths is to remain calm and be patient.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;She makes it sound so easy! However, there's another dimension that exponentially increases the challenge. Placing protection on offwidths is no joke. First, it's no fun lugging a giant cam 1,000 vertical feet just to use it once. Oh, did I mention the 9th pitch is one of the longest? So make that two huge 5-inch cams. So far we have challenging, sustained, strenuous technique combined with sketchy, runout pro, fatigue, and huge exposure. Oh, but did I mention the breathtaking, awe-inspiring views? Look right across the yawning expanse of the valley is the mighty Middle Cathedral rock! And behold Half Dome!&lt;/p&gt;
&lt;p&gt;&lt;img alt="Middle Cathedral" src="images/mighty-cathedral-in-the-morn.JPG"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Half Dome" src="images/halfdomefromelcap.JPG"&gt;&lt;/p&gt;
&lt;p&gt;Feeling good now? So what exactly is the great mother trying to teach us? Trust. Relax. Move intentionally, methodically, and don't hesitate. Above all, resist the urge to bump or reposition your pro. What's this? Remember we only have one gigantic cam with us on this slog, we're scared and tired, so it might seem logical to reach down and pull that cam up, or maybe you spy the perfect placement you couldn't find when you shoved it in desperately thinking you might slip out any second. Sometimes if you're lucky on a nice evenly spaced marathon, maybe you can bump the cam over your head a few times as you move up. However, these dream parallel chasms only exist in Canyonland sandstone. Out on the granite cliffs of the valley, unfortunately you're more likely to accidentally lose your only last good pro as the cam tips out into a sudden widening or jam in the back never to be retrieved again. Your second will curse you, other parties will praise you as they clip this fixed pro, and later a seasoned YOSAR regular will pull it out as the rock changes, reading it like an ancient magic rune. A &lt;a href="https://www.climbing.com/skills/learn-this-offwidth-protection-primer/"&gt;Climbing magazine&lt;/a&gt; primer summarizes the cons well:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the cam tips out or falls down the rope, you could end up way above your last piece. Worse, it could knock out that last piece—and possibly your belayer. Be careful not to push your cam into a spot that’s too tight, too tipped out to hold a fall, or out of your reach.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Of course use long slings so you don't inadvertently walk your cam out of its spot, and suffer the same fate as if you bumped it yourself. Argh! There are so many things to consider, and you're in a really tight spot (pun intended). So don't try any 11th hour dramatics. Leave the gear where it is, keep moving, find another placement deeper in the crack, maybe back it up. Climbers have an old saying that is part cheek and part truth.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When in doubt run it out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It means keep climbing. Unless you're going to downclimb or retreat, it's better to keep making forward progress, than to burn out in a precarious situation. Chances are you will power through the moves, and more often than not the rock offers you a neat solution that saves your ass in the nick of time. But you won't have the juice to make those moves, or the brains to read what the rock is telling you, if you spin out trying to fix a piece of pro that's already your best placement.&lt;/p&gt;
&lt;p&gt;So what's the lesson? How does this apply to me, in my office, clicking a keyboard? Have you ever had a project or a product that's about to ship? And your boss calls you at 11pm, and says, "Hey change this thing." Or maybe you're the boss, and you call your team with a desperate hail Mary pitch at the last minute. &lt;strong&gt;Don't. Just. Don't!&lt;/strong&gt; Calm down. Relax. Trust your team. Stick to the plan. The most important part is sending the project. Whatever your idea is, it's just not worth it. Or maybe it is? But you better be damned sure, because you had a product ready to ship. Are you certain you really want to fuck it up on a Friday night? Why not sleep on it? Maybe in the morning, when you start getting feedback, some bad, some good, on the product that you &lt;em&gt;actually&lt;/em&gt; delivered, you'll be glad that you didn't almost bomb everything.&lt;/p&gt;
&lt;p&gt;This is the lesson. After the 9th pitch, the climbing on the East Buttress was still hard, not technically, but we had reached the end of our rope (no pun this time), and we were fried. There was some mundane 5-easy, exposed traversing with awful rope drag, but we made it to the top. And what a top out it was. Half Dome ablaze in the most glorious sunset of my life.&lt;/p&gt;
&lt;p&gt;&lt;img alt="sunset over half dome" src="images/redhalfdome.JPG"&gt;&lt;/p&gt;
&lt;p&gt;The descent is actually the most dangerous part of the climb. Something like 70% of climbing accidents actually happen when parties rap off the ends of their ropes, because they're too tired to pay attention to tiny but crucial details. We descended the famed East Ledges which true El Cap warriors rap from when they finish big wall test pieces like the Nose. There were fixed ropes that looked good enough, and we stuck the finish, with an elated sense of the sublime. The great mother had graced us with her wisdom, and we had tried our best to suss it out.&lt;/p&gt;
&lt;p&gt;So maybe that's the lesson? To keep it all in perspective? We had planned this trip, read the guides, studied the topo maps, racked our gear with intent, rose early and given it our best. We were lucky that we didn't have to improvise. We were given a cake walk, and we ate it up.&lt;/p&gt;
&lt;p&gt;Thanks for listening. Here's me right before the crux 10b move after the chimney. Not a care in the world.&lt;/p&gt;
&lt;p&gt;&lt;img alt="at the 10b crux after the chimney" src="images/marcoatchimneypitch.JPG"&gt;&lt;/p&gt;</content><category term="Climbing Life Lessons"/><category term="agile"/><category term="project-management"/><category term="project-planning"/><category term="research"/><category term="rant"/><category term="time-management"/><category term="risk-management"/></entry><entry><title>Future of Science</title><link href="https://breakingbytes.github.io/future-of-science.html" rel="alternate"/><published>2020-03-08T00:00:00-08:00</published><updated>2020-03-08T00:00:00-08:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2020-03-08:/future-of-science.html</id><summary type="html">&lt;p&gt;The future of science is OSS, and the language is code.&lt;/p&gt;</summary><content type="html">&lt;h1 id="the-future-of-science-is-oss-and-the-language-is-code"&gt;The future of science is OSS, and the language is code.&lt;/h1&gt;
&lt;p&gt;In the future money is obsolete, currency is your reputation, and you earn it
but contributing to open source data, science, and software. What is open
source? Software seems to dominate open source, but open science, open data,
and open art are also important. Content is not only digital, but also
material. An open science experiment or open educational lesson plan. It's the
ultimate socialist sharing economy. So how does it thrive?&lt;/p&gt;</content><category term="Modeling"/><category term="OSS"/><category term="science"/><category term="code"/><category term="research"/><category term="rant"/></entry><entry><title>Electric Mismatch in Silicon-Cell PV is Not Intuitive</title><link href="https://breakingbytes.github.io/electric-mismatch-in-silicon-cell-pv-is-not-intuitive.html" rel="alternate"/><published>2019-10-18T15:23:00-07:00</published><updated>2019-10-18T15:23:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2019-10-18:/electric-mismatch-in-silicon-cell-pv-is-not-intuitive.html</id><summary type="html">&lt;p&gt;Why does shade just across the bottom row of cells cause 90% power loss?&lt;/p&gt;</summary><content type="html">&lt;h1 id="uniform-versus-non-uniform-shade"&gt;Uniform Versus Non-Uniform Shade&lt;/h1&gt;
&lt;p&gt;The counter-intuitive effect of shade on photovoltaic (PV) solar energy panels
surprises most people.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Shade across a relatively small portion of the panel can cause a
disproportionately large power loss.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For example, shade across just the bottom row of cells, only 6 cells out of 60,
merely 10% of the PV panel, will cause 90% power loss! Why does this happen?
Did you think it would cause only 10% power loss? Why isn't power loss &lt;em&gt;linear&lt;/em&gt;
with shade?&lt;/p&gt;
&lt;p&gt;OK, to be fair, if the entire array is uniformly shaded or merely illuminated
with a less intense light, then the decrease in power &lt;em&gt;would&lt;/em&gt; be proportional
to the decrease in light intensity. For example if a giant rain cloud passed
over the sun, or the sun slowly sank behind a mountain, so that the same light
was cast everywhere, but just lower intensity, say 50%, then the power would be
50% lower as well.&lt;/p&gt;
&lt;p&gt;So just to be &lt;em&gt;completely clear&lt;/em&gt;, in this post we are &lt;strong&gt;not&lt;/strong&gt; going to talk
about the effects of changes in light intensity that are totally uniform
everywhere, but instead we're going to focus on what happens when there are
non-uniform differences in light intensity across the PV system. Here's an
example of a PV system with non-uniform shade.&lt;/p&gt;
&lt;p&gt;&lt;img alt="non uniform shade on a roof" src="./images/20150923_170418.jpg"&gt;&lt;/p&gt;
&lt;h1 id="pv-primer"&gt;PV Primer&lt;/h1&gt;
&lt;p&gt;In PV lingo, a panel is called a &lt;strong&gt;module&lt;/strong&gt; which, in most silicon PV modules,
is made up of smaller solar &lt;strong&gt;cells&lt;/strong&gt; connected in series. Modules are most
often connected in series to form &lt;strong&gt;strings&lt;/strong&gt; of modules. Strings are connected
in parallel to an &lt;strong&gt;inverter&lt;/strong&gt; which converts DC from the panels to AC which is
sent to the grid.&lt;/p&gt;
&lt;p&gt;Here's a typical PV system at the National Institute of Standards and
Technology (NIST) in Gaithersburg, MD, which has a single 260-kW inverter
connected to 96 strings, each with 12
&lt;a href="https://pvfree.herokuapp.com/cec_modules/14738/"&gt;Sharp NU-U235F2&lt;/a&gt; 235-W
modules. The first four rows are 5 modules deep, but the last row is only 4
modules deep. The modules are all in landscape and have straight stringing:&lt;/p&gt;
&lt;p&gt;&lt;img alt="NIST Google" src="./images/nist-ground-array.png"&gt;
&lt;img alt="NIST ground mount racks" src="./images/ground-mount-racks.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Here's a simulation from
&lt;a href="https://www.dnvgl.com/services/solar-pv-plant-design-software-solarfarmer-140689"&gt;SolarFarmer&lt;/a&gt;
showing the shadows cast by the eastern trees.&lt;/p&gt;
&lt;p&gt;&lt;img alt="NIST Google" src="./images/NIST_SolarFarmer_trees.png"&gt;
&lt;img alt="NIST Google" src="./images/NIST_Google.png"&gt;&lt;/p&gt;
&lt;h2 id="pv-cells-and-reverse-bias-breakdown"&gt;PV Cells and Reverse Bias Breakdown&lt;/h2&gt;
&lt;p&gt;The cells in a PV module can be considered roughly as a current source in
parallel with a diode and some resistive elements. Diodes are semiconductors.
In other words, they only conduct current in one direction, called the forward
bias. When a negative voltage, or a reverse bias, is applied to the cell, the
semiconductor won't conduct a current. However, if enough reverse bias is
applied, all semiconductors will eventually breakdown, and carry a current.
This phenomema is called &lt;strong&gt;reverse bias breakdown&lt;/strong&gt;, and the &lt;strong&gt;breakdown
voltage&lt;/strong&gt; varies between cell technology. A typical front contact p-type
silicon solar cell may breakdown at around -20 volts, while a back-contact
n-type silicon solar cell may breakdown at -5 volts. There are many factors,
beyond the scope of this primer, that affect reverse bias breakdown, such as
purity of the substrate as well as type and concentration of dopant. The most
important thing to understand about reverse bias breakdown is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When a cell is in reverse breakdown, it can carry nearly any current, but
because the voltage is negative, then the cell will dissipate energy and will
get hot as it exchanges heat with the environment around it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="the-single-diode-model"&gt;The Single Diode Model&lt;/h3&gt;
&lt;p&gt;The electric circuit analog used to describe a solar cell is called the single
diode model. Here's a schematic:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Single Diode Model" src="https://www.sandia.gov/app/uploads/sites/243/2022/11/Single-Diode-EC2.png"&gt;&lt;/p&gt;
&lt;p&gt;Image credit:
&lt;a href="https://pvpmc.sandia.gov/modeling-steps/2-dc-module-iv/diode-equivalent-circuit-models/"&gt;Sandia PV Performance Modeling Collaborative&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The solar cell current-voltage relation, or IV curve, shown below demonstrates
the forward and reverse bias characteristics of a solar cell. The single diode
model matches this response close enough to be used for predictions.&lt;/p&gt;
&lt;p&gt;&lt;img alt="3-quadrant IV curve" src="https://www.pveducation.org/sites/default/files/PVCDROM/Modules/Images/NONIDEAL.gif"&gt;&lt;/p&gt;
&lt;p&gt;Image credit:
&lt;a href="https://www.pveducation.org/pvcdrom/modules-and-arrays/mismatch-effects"&gt;PV Education, UNSW, ASU, &lt;em&gt;et al.&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="pv-modules-and-bypass-diodes"&gt;PV Modules and Bypass Diodes&lt;/h2&gt;
&lt;p&gt;PV modules are usually designed with bypass diodes to avoid energy loss and hot
spots due to cells in reverse bias breakdown by allowing current to bypass the
cells in reverse bias breakdown. The figure below shows a 72-cell module with
3 bypass diodes, each in parallel with a 24-cell submodule (&lt;em&gt;aka&lt;/em&gt;: substring).&lt;/p&gt;
&lt;p&gt;&lt;img alt="Circuit diagram of the solar module with 72 cells" src="./images/Circuit-diagram-of-the-solar-module-with-72-cells_W640.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Credit: &lt;em&gt;Analysis of Power Loss for Crystalline Silicon Solar Module during the
Course of Encapsulation&lt;/em&gt; by Hong Yang &lt;em&gt;et al.&lt;/em&gt;, April 2015, International
Journal of Photoenergy 2015:1-5 &lt;a href="https://doi.org/10.1155/2015/251615"&gt;DOI: 10.1155/2015/251615&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When the voltage in the submodule exceeds a small trigger voltage in the bypass
diode, due to a cell or cells in reverse bias breakdown, then current will flow
through the bypass diode, bypassing the entire submodule. For example, if one
cell in the submodule is in reverse bias breakdown at -20 volts, and the other
23 cells are all normal at 0.6 volts, then the total voltage of the submodule
is -6.2 volts.&lt;/p&gt;
&lt;div class="math"&gt;$$\begin{align}V_{sub} &amp;amp;= -20[V] + 23\left(0.6[V]\right) \\
&amp;amp;= -20[V] + 13.8[V] = -6.2[V]\end{align}$$&lt;/div&gt;
&lt;p&gt;So if the trigger voltage of the bypass diode is -0.5 volts, then the current
will pass through the bypass diode. If the module current is 6 amps, then the
bypass diode dissipates only 3 watts and avoids losing 36 watts from the bad
submodule. What's more important than saving energy though, is that the bypass
diode is also a safety device, because the single cell in reverse bias
breakdown would have dissipated 120 watts, which could potentially cause a
fire, and most likely would cause permanent damage to the cell and the module
encapsulant or backsheet. &lt;/p&gt;
&lt;h1 id="electric-circuit-theory"&gt;Electric Circuit Theory&lt;/h1&gt;
&lt;p&gt;So why does non-uniform shade cause this non-linear effect? If we consider the
PV system as an electric circuit, then it must obey the following two laws:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Ohm%27s_law"&gt;Ohm's Law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Kirchhoff%27s_circuit_laws"&gt;Kirchhoff's Law&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ohms-law"&gt;Ohms Law&lt;/h2&gt;
&lt;p&gt;According to Ohm's law, because the cells and modules in a PV system are all in
series, then they must all carry the same current, &lt;span class="math"&gt;\(I\)&lt;/span&gt;, the total voltage of
each module, &lt;span class="math"&gt;\(V_{mod}\)&lt;/span&gt;, is the sum of the cell voltages in that module, and the
total string voltage, &lt;span class="math"&gt;\(V_{str}\)&lt;/span&gt;, is the sum of the module voltages in the string.
In the equation below, &lt;span class="math"&gt;\(Z\)&lt;/span&gt; represents the impedance of each cell in a module or
each module in a string.&lt;/p&gt;
&lt;div class="math"&gt;$$V = \sum V = I \sum Z$$&lt;/div&gt;
&lt;p&gt;The actual relation in a cell is really a bit more complicated than &lt;span class="math"&gt;\(V=IZ\)&lt;/span&gt;,
but hopefully this will do to explain electrical mismatch for now. The relation
between the flux of charge and solar cell voltage is described by the
&lt;a href="https://en.wikipedia.org/wiki/Poisson%E2%80%93Boltzmann_equation"&gt;Poisson Energy Transport Equation&lt;/a&gt;
but in practice &lt;a href="#the-single-diode-model"&gt;the single diode model&lt;/a&gt; circuit
discussed above, is a useful analog. For more detail on the single diode model,
see this post on
&lt;a href="./examples-of-implicit-versus-explicit-solutions-for-photovoltaic-solar-energy-systems.html"&gt;examples of implicit vs. explicit solutions for PV systems&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="kirchhoffs-law"&gt;Kirchhoff's Law&lt;/h2&gt;
&lt;p&gt;Kirchhoff's law requires the voltage across all of the parallel strings to be
the same as the voltage across the inverter, and the total current at the
inverter nodes to be the sum of the currents from all parallel strings
connected to it.&lt;/p&gt;
&lt;div class="math"&gt;$$I = \sum I = V\sum{\frac{1}{Z}}$$&lt;/div&gt;
&lt;h1 id="electrical-mismatch"&gt;Electrical Mismatch&lt;/h1&gt;
&lt;p&gt;The combined effect of enforcing Ohm's and Kirchhoff's laws on a PV system that
is partially shaded can cause what's called &lt;em&gt;electrical mismatch&lt;/em&gt;. In other
words, the shaded cells can't &lt;em&gt;match&lt;/em&gt; the current being carried by the rest of
the module, or the string with shaded modules can't carry the same voltage as
the PV system, and therefore the entire system has to find a new operating
condition to satisfy Ohm's and Kirchhoff's laws.&lt;/p&gt;
&lt;h2 id="a-rule-of-thumb"&gt;A Rule of Thumb&lt;/h2&gt;
&lt;p&gt;The counter intuitive behavior of a PV array with partial shade can be
understood by remembering the following rule of thumb:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A submodule will perform as well as its most shaded cell.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Imagine a submodule has the bottom cells completely shaded, so they are only
getting diffuse light, say 10% of the total plane of array irradiance that's
incident on the other cells in the module. The rest of the PV array is
operating normally, so the current and voltage of each string might be 7.8 amps
and 360 volts, assuming the NIST site shown in the &lt;a href="#pv-primer"&gt;PV Primer&lt;/a&gt;
above. So, as described in the section on
&lt;a href="#pv-cells-and-reverse-bias-breakdown"&gt;PV Cells and Reverse Bias Breakdown&lt;/a&gt;,
the two shaded cells can only carry this current in reverse bias breakdown,
which would trigger the bypass diode to activate, and cause the string to lose
one-third of a module's voltage. However, the strings all have to have the same
voltage, and the maximum power point of the system is not going to be at lower
voltage, so the bad submodule can't activate it's bypass diode, and the string
will have to operate at a lower current - the same current as the bad cell.&lt;/p&gt;
&lt;h2 id="pvmismatch"&gt;PVMismatch&lt;/h2&gt;
&lt;p&gt;We can analyze this using &lt;a href="https://sunpower.github.io/PVMismatch/"&gt;PVMismatch&lt;/a&gt;
Python package by SunPower.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: A PV system with just 10% of cells shaded in only 1 string, loses
nearly all of the power in that string.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="script"&gt;Script&lt;/h3&gt;
&lt;p&gt;The following scrpt uses PVMismatch to model a PV system at STC with 10 strings
of 10 &lt;a href="https://pvfree.herokuapp.com/pvmodules/1517/"&gt;SunPower SPR-315E-WHT&lt;/a&gt;
96-cell modules per string. Then analyzes the same system, but with the bottom
row of cells shaded 80%, &lt;em&gt;ie&lt;/em&gt;: only diffuse irradiance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;analyze and plot a 10x10 system with bottom cells of one row shaded&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# import the pvmismatch library&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pvmismatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# import the matplotlib plotting library&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;matplotlib&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;

&lt;span class="c1"&gt;# make a 10x10 system of SPR-E20-315 96-cell panels at stc&lt;/span&gt;
&lt;span class="n"&gt;pvsys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pvsystem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PVsystem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# plot the system&lt;/span&gt;
&lt;span class="n"&gt;pvsys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plotSys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tight_layout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# shade the bottom cell of all modules in the 1st string by 80%&lt;/span&gt;
&lt;span class="n"&gt;pvsys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setSuns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;cells&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;84&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;Ee&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)}})&lt;/span&gt;

&lt;span class="c1"&gt;# plot the btm row shaded system&lt;/span&gt;
&lt;span class="n"&gt;pvsys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plotSys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tight_layout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# btm row shaded system max power&lt;/span&gt;
&lt;span class="n"&gt;pvsys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pmp&lt;/span&gt;
&lt;span class="c1"&gt;# 29607.549997102204&lt;/span&gt;

&lt;span class="c1"&gt;# make a new stc system for comparison&lt;/span&gt;
&lt;span class="n"&gt;pvsys_stc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pvsystem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PVsystem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# stc system max power&lt;/span&gt;
&lt;span class="n"&gt;pvsys_stc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pmp&lt;/span&gt;
&lt;span class="c1"&gt;# 32119.317380452303&lt;/span&gt;

&lt;span class="c1"&gt;# calculate loss&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pvsys_stc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pmp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pvsys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pmp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;pvsys_stc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pmp&lt;/span&gt;
&lt;span class="c1"&gt;# 0.07820114461332704&lt;/span&gt;

&lt;span class="c1"&gt;# btm row shaded string max power&lt;/span&gt;
&lt;span class="n"&gt;pvsys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pvstrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pstring&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# 720.6607971888608&lt;/span&gt;

&lt;span class="c1"&gt;# stc string max power&lt;/span&gt;
&lt;span class="n"&gt;pvsys_stc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pvstrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pstring&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# 3212.7324983909575&lt;/span&gt;

&lt;span class="c1"&gt;# calculate loss&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pvsys_stc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pvstrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pstring&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pvsys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pvstrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pstring&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;pvsys_stc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pvstrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pstring&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# 0.7756860250426105&lt;/span&gt;

&lt;span class="c1"&gt;# plot stc string&lt;/span&gt;
&lt;span class="n"&gt;pvsys_stc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pvstrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plotStr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gcf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;axes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_ylim&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tight_layout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# plot btm row shaded string&lt;/span&gt;
&lt;span class="n"&gt;pvsys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pvstrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plotStr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gcf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;axes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_ylim&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;axes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_ylim&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tight_layout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="PV system at STC" src="./images/pvsys_stc.png"&gt; &lt;/p&gt;
&lt;p&gt;The maximum power at STC is 32,119 [W].&lt;/p&gt;
&lt;p&gt;&lt;img alt="PV system with bottom row shaded" src="./images/pvsys_bot_row.png"&gt;&lt;/p&gt;
&lt;p&gt;The maximum power with bottom row shaded) is 29,607 [W]. The calculated the
relative loss, &lt;code&gt;(pvsys_stc – pvys_btm_row) / pvsys_stc&lt;/code&gt;, is 7.8%.&lt;/p&gt;
&lt;p&gt;&lt;img alt="PV string at STC" src="./images/pvstr_stc.png"&gt;&lt;/p&gt;
&lt;p&gt;The string power at STC is 3213 [W].&lt;/p&gt;
&lt;p&gt;&lt;img alt="PV string with bottom row shaded" src="./images/pvstr_bot_row.png"&gt;&lt;/p&gt;
&lt;p&gt;The string power with the bottom row shaded is 721 [W], and the calculated
relative loss, &lt;code&gt;(pvstr_stc – pvstr_btm_row) / pvstr_stc&lt;/code&gt;, is 78%.&lt;/p&gt;
&lt;p&gt;(Sorry the resolution is a bit poor around the knee, this was a quick
simulation using only 100 points.)&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Solar"/><category term="solar"/><category term="modeling"/><category term="code"/></entry><entry><title>OAuth 2.0 and OpenID Connect Primer</title><link href="https://breakingbytes.github.io/oauth-20-and-openid-connect-primer.html" rel="alternate"/><published>2019-10-17T17:30:00-07:00</published><updated>2019-10-17T17:30:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2019-10-17:/oauth-20-and-openid-connect-primer.html</id><summary type="html">&lt;p&gt;Authentication and authorization with OAuth 2.0 and OpenID Connect.&lt;/p&gt;</summary><content type="html">&lt;h1 id="authentication-and-authorization"&gt;Authentication and Authorization&lt;/h1&gt;
&lt;p&gt;Web applications may contain both public and private data. Private data may
be further restricted to only certain authorized users. Therefore the web
application must first authenticate a user, and then determine what private
data they are authorized to access. The OAuth 2.0 specification implements an
alternative workflow which alows users to authorize another web application to
access resources on their behalf. The specification states that
&lt;a href="https://oauth.net/articles/authentication/"&gt;OAuth 2.0 is not an authentication protocol&lt;/a&gt;.
However, &lt;a href="https://openid.net/connect/"&gt;OpenID Connect&lt;/a&gt; does use OAuth 2.0 to
implement a standard for authentication. GitHub is an example of the OpenID 
Connect standard, see &lt;a href="https://developer.github.com/v3/guides/basics-of-authentication/"&gt;Basics of Authentication&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="terms"&gt;Terms&lt;/h2&gt;
&lt;p&gt;The following definitions are from from the linked reference by Alex Bilbie.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Resource (API) server&lt;/strong&gt;: The server hosting the protected resources,
capable of accepting and responding to protected resource requests using access
tokens.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization server&lt;/strong&gt;: The server issuing access tokens to the client
after successfully authenticating the resource owner and obtaining
authorization.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource owner (the User)&lt;/strong&gt;: An entity capable of granting access to a
protected resource. When the resource owner is a person, it is referred to as
an end-user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client&lt;/strong&gt;: An application making protected resource requests on behalf of
the resource owner and with its authorization. The term client does not imply
any particular implementation characteristics (e.g. whether the application
executes on a server, a desktop, or other devices).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="authorizing-users-clients"&gt;Authorizing Users &amp;amp; Clients&lt;/h2&gt;
&lt;p&gt;There are four workflows supported by OAuth 2.0 that allow users to authorize
an application to obtain a &lt;code&gt;Bearer&lt;/code&gt; token to gain access to resources in lieu
of user credentials or client tokens.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="#authorization-code"&gt;Authorization Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#resource-owner-password-credentials"&gt;Resource Owner Password Credentials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#client-credentials"&gt;Client Credentials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Implicit (not covered in this post)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="authorization-code"&gt;Authorization Code&lt;/h3&gt;
&lt;p&gt;This is the most secure workflow and is the preferred workflow for applications
running on remote web servers that can store credentials confidentially. The
process is documented in the &lt;a href="https://oauth.net/2/"&gt;OAuth 2.0 specification&lt;/a&gt;
and in the &lt;a href="#references"&gt;References&lt;/a&gt;. The authorization code workflow
consists of the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ask an administrator to create a client application with the desired grant
type. You should be given a &lt;code&gt;client_id&lt;/code&gt; and a &lt;code&gt;client_secret&lt;/code&gt;. These must be
treated as secrets, &lt;em&gt;e.g.&lt;/em&gt; never transmit them over HTTP, only HTTPS, and don't
commit them to repositories. The client type for the authorization code
workflow should always be &lt;strong&gt;Confidential&lt;/strong&gt;. A &lt;em&gt;confidential&lt;/em&gt; client can keep a
secret, while a &lt;em&gt;public&lt;/em&gt; client can't. An example of a confidential client
would be a server, while a public client would be single-page JavaScript
application, because anyone could view the page source. Authorization code
should only be used with confidential clients that can keep secrets. The client
registration must include a list of redirect URI to consume the authorization
code from the authorization service.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then a registered client can send a &lt;code&gt;GET&lt;/code&gt; request to the authorization URL
to redirect users to login with the identity provider and get an authorization
code which can be exchanged by the client for an access token. For this example,
we'll use the following fictitious authorization URL:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://identity-provider/authorize/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The authorization URL is very picky. It must include a the following query
string parameters:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="s"&gt;&amp;quot;client_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;response_type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;code&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;redirect_uri&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;scope&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;read write email name&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The authorization can also include the desired scopes including any
additional claims about the user such as: &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;given_name&lt;/code&gt;,
&lt;code&gt;family_name&lt;/code&gt;, &lt;code&gt;openid&lt;/code&gt;, &lt;em&gt;etc.&lt;/em&gt; that can be retrieved in the ID token from
the &lt;code&gt;/identity-provider/userinfo/&lt;/code&gt; endpoint.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Optionally, the authorization request should also contain a unique &lt;code&gt;state&lt;/code&gt;
code, a string of any length, used to prevent cross site forgery request.
It's up to the client to save the state sent in the request and validate it
with the state returned in the response from the authorization server.&lt;/li&gt;
&lt;li&gt;Optionally there may be an &lt;code&gt;approval_prompt&lt;/code&gt; parameter that can be provided
and set to either &lt;code&gt;force&lt;/code&gt; or &lt;code&gt;auto&lt;/code&gt;. If not set, and there is a "skip
authorization" checkbox in the app registration form which is disabled, then
the default may be &lt;code&gt;force&lt;/code&gt; which may ask users to authenticate everytime. To
only ask users to authenticate the first time, try &lt;code&gt;auto&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The identity provider will ask the user for their credentials if not
already cached in the user's browser.&lt;/li&gt;
&lt;li&gt;Depending on the &lt;code&gt;approval_prompt&lt;/code&gt; parameter or the "skip authorization"
checkbox, the user may be prompted to authorize the token, with a list of the
claims and scopes that the token is requesting.&lt;/li&gt;
&lt;li&gt;The authorization service returns a code and the state submitted by the
client. The code can then be exchanged for an access token.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The redirect URI on the client side has 10 minutes, and one attempt to
exchange the authorization code for an access token. This request is sent as a
&lt;code&gt;POST&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://identity-provider/token/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The token URL is very picky. It must include the client's id and secret
either as a basic authentication header or as payload data. Additionally the
following data must also be in the payload of the &lt;code&gt;POST&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="s"&gt;&amp;quot;grant_type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;authorization_code&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;code&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;redirect_uri&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The response is a JSON string with the &lt;code&gt;access_token&lt;/code&gt;, &lt;code&gt;expires_in&lt;/code&gt;,
&lt;code&gt;token_type&lt;/code&gt; (always &lt;code&gt;Bearer&lt;/code&gt;), &lt;code&gt;scopes&lt;/code&gt; and a &lt;code&gt;refresh_token&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="resource-owner-password-credentials"&gt;Resource Owner Password Credentials&lt;/h3&gt;
&lt;p&gt;This can be used by trusted clients by passing users credentials directly to
the authorization server, in return for a token. See the
&lt;a href="#references"&gt;References&lt;/a&gt; for more info and examples.&lt;/p&gt;
&lt;h3 id="client-credentials"&gt;Client Credentials&lt;/h3&gt;
&lt;p&gt;This can be used by trusted clients to use the API directly on their own behalf
with any user. See the &lt;a href="#references"&gt;References&lt;/a&gt; for more info and examples.&lt;/p&gt;
&lt;h3 id="refresh-token"&gt;Refresh Token&lt;/h3&gt;
&lt;p&gt;This can be used by any client to extend a token before it expires by
exchanging a &lt;em&gt;refresh token&lt;/em&gt; for a new token without requiring the user to
reenter their credentials. See the &lt;a href="#references"&gt;References&lt;/a&gt; for more info and
examples.&lt;/p&gt;
&lt;h2 id="getting-user-info"&gt;Getting User Info&lt;/h2&gt;
&lt;p&gt;This part of the OpenID Connect specification allows a client to use the access
token to obtain the token owners identity and other claims such as &lt;code&gt;email&lt;/code&gt;,
&lt;code&gt;name&lt;/code&gt;, &lt;code&gt;given_name&lt;/code&gt; and &lt;code&gt;family_name&lt;/code&gt;. These claims must be submitted as
scopes when the authorization code is requested. Then the client can send a
&lt;code&gt;GET&lt;/code&gt; request:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://identity-provider/userinfo/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;GET&lt;/code&gt; must have an authorization header containing the bearer token:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Authorization: &amp;quot;Bearer &amp;lt;token&amp;gt;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The content of the response is JSON web token (JWT), a base-64 encoded string
signed by hashing the JWT using the client secret as the key. See
&lt;a href="https://jwt.io/"&gt;jwt.io&lt;/a&gt; for more information and available bindings.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;The response status code should be &lt;code&gt;200 OK&lt;/code&gt; and the content type should be
&lt;code&gt;application/jwt&lt;/code&gt;. The status code will be &lt;code&gt;405 METHOD NOT ALLOWED&lt;/code&gt; if the
request does not use &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;403 FORBIDDEN&lt;/code&gt; if the bearer token is missing
or invalid.&lt;/li&gt;
&lt;li&gt;The JWT contains the user name as the subject claim, &lt;code&gt;sub&lt;/code&gt;, as well as any
additional claims requested as scopes.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;groups&lt;/code&gt; scope to get access to groups a user belongs to on the
identity provider such as the &lt;em&gt;administrator&lt;/em&gt; group.&lt;/li&gt;
&lt;li&gt;It is up to the client to validate that the JWT is signed correctly. The hash
algorithm is in the JWT as &lt;code&gt;alg&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The JWT also has an issuer claim, &lt;code&gt;iss&lt;/code&gt;, that should be set to the name of the
identity provider.&lt;/li&gt;
&lt;li&gt;The JWT audience claim, &lt;code&gt;aud&lt;/code&gt;, is the the client id.&lt;/li&gt;
&lt;li&gt;It's up to the client to check the time frame of the JWT using issued at
&lt;code&gt;iat&lt;/code&gt;, expires in &lt;code&gt;exp&lt;/code&gt;, and not before &lt;code&gt;nbf&lt;/code&gt; claims.&lt;/li&gt;
&lt;li&gt;The client can also confirm that the correct token was used by examining the
access token hash claim, &lt;code&gt;at_hash&lt;/code&gt;. Please see the the
&lt;a href="http://openid.net/specs/openid-connect-core-1_0.html"&gt;OpenID Connect Core&lt;/a&gt;
specification for more info.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="permissions"&gt;Permissions&lt;/h2&gt;
&lt;p&gt;Access to resources can be limited in two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;permissions (not covered in this post)&lt;/li&gt;
&lt;li&gt;token scopes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="token-scopes"&gt;Token Scopes&lt;/h3&gt;
&lt;p&gt;If a user or an application is authenticated using an OAuth 2.0 token, then
that token must have the scopes required for the desired action, &lt;em&gt;regardless&lt;/em&gt;
of the owner's permissions or group membership! For example if an application
has been authorized by a member of the &lt;em&gt;administrator&lt;/em&gt; group, then that token
can only be used to edit data if it has the &lt;code&gt;write&lt;/code&gt; scope.&lt;/p&gt;
&lt;h3 id="scopes-and-claims"&gt;Scopes and Claims&lt;/h3&gt;
&lt;p&gt;Scopes can also used as claims to identify a token owner. The following scopes
and claims may be available.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;read&lt;/td&gt;
&lt;td&gt;Read scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write&lt;/td&gt;
&lt;td&gt;Write scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;groups&lt;/td&gt;
&lt;td&gt;Access to your groups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;name&lt;/td&gt;
&lt;td&gt;JWT claim for user full name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;email&lt;/td&gt;
&lt;td&gt;JWT claim for user email&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;given_name&lt;/td&gt;
&lt;td&gt;JWT claim for first name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;family_name&lt;/td&gt;
&lt;td&gt;JWT claim for last name&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;groups&lt;/code&gt; scope can also be used as a claim to get the groups that the token
owner belongs to from the identity provider.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://alexbilbie.com/guide-to-oauth-2-grants/"&gt;A Guide To Oauth 2.0 Grants by Alex Bilbie&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2"&gt;An Introduction to OAuth 2 by Digital Ocean&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aaronparecki.com/oauth-2-simplified/"&gt;OAuth 2 Simplified by Aaron Parecki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="Authentication and Authorization"/><category term="code"/><category term="web"/><category term="auth"/><category term="oauth"/><category term="openid"/></entry><entry><title>Python Concurrency Primer</title><link href="https://breakingbytes.github.io/python-concurrency-primer.html" rel="alternate"/><published>2019-10-07T10:33:00-07:00</published><updated>2019-10-07T10:33:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2019-10-07:/python-concurrency-primer.html</id><summary type="html">&lt;p&gt;Asynchronous Python workflows for IO and CPU processes on a single machine versus a cluster.&lt;/p&gt;</summary><content type="html">&lt;h1 id="python-threading-versus-multiprocessing"&gt;Python Threading versus Multiprocessing&lt;/h1&gt;
&lt;p&gt;Threading works really well with IO, but not CPU bound processes, hence why there's an &lt;a href="https://docs.python.org/3/library/asyncio.html"&gt;asyncio lib&lt;/a&gt; for IO and a &lt;a href="https://docs.python.org/3/library/multiprocessing.html"&gt;MP lib&lt;/a&gt;. Before Py3, you could only manually create threads using the &lt;a href="https://docs.python.org/3/library/threading.html"&gt;threading lib&lt;/a&gt; and some people who don't like asyncio still prefer to use the manual threading lib, an external lib like &lt;a href="https://trio.readthedocs.io/en/stable/"&gt;trio&lt;/a&gt;, or older packages like &lt;a href="http://www.gevent.org/"&gt;gevents&lt;/a&gt;, etc.&lt;/p&gt;
&lt;p&gt;For an example of using threading for an IO bound process see the &lt;a href="https://github.com/pvlib/pvlib-python/blob/master/pvlib/iotools/ecmwf_macc.py#L168"&gt;&lt;code&gt;pvlib.iotools.get_ecmwf_macc()&lt;/code&gt; method&lt;/a&gt; which returns the thread that calls the ECMWF api client because it is soooooo slow!&lt;/p&gt;
&lt;h1 id="concurrency-in-the-cloud"&gt;Concurrency in the Cloud&lt;/h1&gt;
&lt;p&gt;Curve ball (⚾) - threading and MP work great for a single computer, but when you are on a cluster, such as web applications, then you have more options!  &lt;/p&gt;
&lt;p&gt;In a web app, you usually have a load balancer in front of several workers who answer get requests.&lt;/p&gt;
&lt;p&gt;Therefore you can deligate the work for both IO and CPU bound processes differently than by using either threading, MP, asyncio, trio, gevent, etc.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.celeryproject.org/"&gt;Celery&lt;/a&gt; is probably the most popular task queue for distributed resources especially for web applications.&lt;/p&gt;
&lt;p&gt;I would guess there are task managers other than Celery, but would have to Google around to find a curated list - and many examples recommend Celery, eg: &lt;a href="https://devcenter.heroku.com/articles/celery-heroku"&gt;Heroku&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="inter-process-communication"&gt;Inter-Process Communication&lt;/h1&gt;
&lt;p&gt;Note a task queue, threading, and MP all require a way for threads and processes to communicate with each other. Threads on a single machine can more easily share memory which makes them more efficient than MP. For Python threads, I typically use the standard Python &lt;a href="https://docs.python.org/3/library/queue.html"&gt;Queue lib&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Multiprocessing requires some form of &lt;a href="https://en.wikipedia.org/wiki/Inter-process_communication"&gt;inter-process communication or IPC&lt;/a&gt;, which attempts to serialize and deserialize objects between processes. Python provides &lt;a href="https://docs.python.org/3/library/multiprocessing.html?highlight=process#pipes-and-queues"&gt;MP Queues and Pipes&lt;/a&gt; to &lt;a href="https://docs.python.org/3/library/multiprocessing.html?highlight=process#exchanging-objects-between-processes"&gt;exchange objects between processes&lt;/a&gt;. I prefer the MP Queue, because it nearly mimics the API of the standard Python Queue lib, but because it is IPC, I have to be careful to only pass in objects that can be easily serialized and deserialized like numbers, strings, sequences, and dictionaries.&lt;/p&gt;
&lt;p&gt;For distributed computing, the recommendation is to use a message queue like &lt;a href="https://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt; or a shared memory server like &lt;a href="https://memcached.org/"&gt;memcached&lt;/a&gt; or &lt;a href="https://redis.io/"&gt;reddis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, I don't have the expertise to say when to use an MQ verses memcache, but there are lots of articles on the internet discussing the pros/cons of each. I've used memcache and it works, but I believe for Celery they preffer MQ.&lt;/p&gt;
&lt;h2 id="queues"&gt;Queues&lt;/h2&gt;
&lt;p&gt;For both threads and processes, IMHO it's a good idea to keep the communication simple. I recommend only pushing the primitive types that will be needed by the target function handled by the thread and then reconstructing more complicated objects in the main branch of execution as needed. I believe this will keep the communication overhead low as well as make it easier to serialize and deserialize target arguments and avoid race conditions.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: Python also uses the GIL to avoid race conditions between threads. See below.&lt;/p&gt;
&lt;h3 id="thread-safety-and-shared-memory"&gt;Thread Safety and Shared Memory&lt;/h3&gt;
&lt;p&gt;This is at the edge of my experience, but Python does provide tools to lock and synchronize communication between &lt;a href="https://docs.python.org/3/library/threading.html#lock-objects"&gt;threads&lt;/a&gt; and &lt;a href="https://docs.python.org/3/library/multiprocessing.html#synchronization-between-processes"&gt;processes&lt;/a&gt; as well as a &lt;a href="https://docs.python.org/3/library/multiprocessing.html?highlight=process#sharing-state-between-processes"&gt;shared memory map for multiprocessing&lt;/a&gt;. Explore on your own.&lt;/p&gt;
&lt;h1 id="the-difference-between-io-and-cpu-bound-process"&gt;The Difference Between IO and CPU Bound Process&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;An IO bound process is like getting data from a URL, or writing a file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A CPU bound process is like a big calculation like matrix inversion, a search routine, or optimization/minimzation problem&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;IO bound processes use Threads because threads do not block IO bound processes - IE: you can start an IO process in a thread and execution will continue to the next line unless you tell Python to wait. AsyncIO and Trio probably use threading in the background, IDK for sure.&lt;/p&gt;
&lt;p&gt;CPU bound processes use MP, because they need their own core to do their stuff. But unfortunately there is a lot of overhead with MP, and so the calculation needs to be really expensive to make it worthwhile.&lt;/p&gt;
&lt;p&gt;There's no limit to the number of threads you can create, but the number of MP processes is limited to the number of cores.&lt;/p&gt;
&lt;p&gt;These rules don't apply to distributed computing, and they may not apply in other computing languages either. IE: you can distribute both IO and CPU tasks to a task queue across a cluster. However, there may be task managers that are better suited for different types of tasks. EG: &lt;a href="https://dask.org/"&gt;Dask&lt;/a&gt; is probably better for distributed computing of CPU intense processes. But honestly, IDK, I'm at the edge of my personal research and experience here, and so I would probably recommend a little more exploration ...&lt;/p&gt;
&lt;h1 id="python-global-interpreter-lock"&gt;Python Global Interpreter Lock&lt;/h1&gt;
&lt;p&gt;The reason there's this difference between IO/CPU and threads/processes in Python specifically, and perhaps not other languages, is because Python is single threaded by default due to the &lt;a href="https://wiki.python.org/moin/GlobalInterpreterLock"&gt;Global Interpreter Lock or GIL&lt;/a&gt;. Other languages may or may not have similar design, IDK, however Python C-API does let you explicitly &lt;a href="https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock"&gt;release the GIL&lt;/a&gt; and manage threads manually. This is what packages like &lt;a href="https://scipy-cookbook.readthedocs.io/items/ParallelProgramming.html"&gt;NumPy/SciPy&lt;/a&gt; and &lt;a href="https://cython.readthedocs.io/en/latest/src/userguide/parallelism.html"&gt;CPython&lt;/a&gt; do.&lt;/p&gt;</content><category term="Python"/><category term="code"/><category term="web"/></entry><entry><title>Examples of implicit versus explicit solutions for photovoltaic solar energy systems</title><link href="https://breakingbytes.github.io/examples-of-implicit-versus-explicit-solutions-for-photovoltaic-solar-energy-systems.html" rel="alternate"/><published>2018-06-22T20:55:00-07:00</published><updated>2018-06-22T20:55:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2018-06-22:/examples-of-implicit-versus-explicit-solutions-for-photovoltaic-solar-energy-systems.html</id><summary type="html">&lt;p&gt;Using a numerical solver to model solar power.&lt;/p&gt;</summary><content type="html">&lt;h1 id="full-scale-pv-modeling"&gt;Full Scale PV Modeling&lt;/h1&gt;
&lt;p&gt;I want to explore different ways to find the operating conditions for max power
of a PV system. I'm going to assume that we're using a single diode model (SDM)
which is an analog curcuit of a solar cell composed from a current source, a
diode, and a resistance in parallel, in series with another resistance.&lt;/p&gt;
&lt;p&gt;&lt;img alt="single diode model" src="https://www.sandia.gov/app/uploads/sites/243/2022/11/Single-Diode-EC2.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;source: &lt;a href="https://pvpmc.sandia.gov/"&gt;Sandia National Labs: PV Performance Modeling Collaborative&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The cell current in the SDM can't be solved explicitly as a function of cell
voltage without using the Lambert-W function. It can be solved implicitly by
using an iterative solver such as a gradient descent method. And there is an
explicit method that can be used to solve for cell current as a function of an
intermediate parameter, the diode voltage, which can then be used to calculate
the cell current. To get a specific voltage using this explicit parameterized
approach, requires solving for several diode voltages and interpolating.&lt;/p&gt;
&lt;p&gt;A common use of the SDM is to solve for the ideal operating condition of an
entire PV system by assuming all of the cells are identical. Another proposed
usage of the SDM is to model all of the cells in an entire system to account
for their differences either due to variance in manufacturing, ambient
conditions, or degradation. This second problem poses some challenges
computationally for very large systems.&lt;/p&gt;
&lt;p&gt;What is the best method to do full scale PV system modeling? I'm going to skip
the Lambert-W function and focus on implict and explicit methods.&lt;/p&gt;
&lt;h1 id="conclusions"&gt;Conclusions&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The implicit approach is efficient at finding a single operating condition,
even or especially when most of the cells are different, but it's slow at
tracing the entire IV curve.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The implicit approach is also efficient at determining the max power point.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Nesting search methods is less efficient than a single closed solution.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The explicit method is most efficient at tracing the entire IV curve, even
when every cell is different.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="gist"&gt;Gist&lt;/h1&gt;
&lt;p&gt;The following Gist contains examples for a full scale implicit approach and the
explicit parameterized approach described. The Gist are best viewed on
&lt;a href="http://nbviewer.jupyter.org/gist/mikofski/df318d1f892767ac7c762e732fecaa7f"&gt;nbviewer&lt;/a&gt;.&lt;/p&gt;
&lt;script src="https://gist.github.com/mikofski/df318d1f892767ac7c762e732fecaa7f.js"&gt;&lt;/script&gt;</content><category term="Solar"/><category term="code"/><category term="solar"/><category term="modeling"/></entry><entry><title>SPACES: Mnemonic for core values of good modeling software</title><link href="https://breakingbytes.github.io/spaces-mnemonic-for-core-values-of-good-modeling-software.html" rel="alternate"/><published>2018-05-10T00:14:00-07:00</published><updated>2018-05-10T00:14:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2018-05-10:/spaces-mnemonic-for-core-values-of-good-modeling-software.html</id><summary type="html">&lt;p&gt;What makes good modeling software? Repeat this three times out loud.&lt;/p&gt;</summary><content type="html">&lt;h1 id="modeling"&gt;Modeling&lt;/h1&gt;
&lt;p&gt;Mathematical models of scientific phenomena are essential in engineering. We
use them to design experiments and select instruments based on estimates of the
sensitivities of relationships between factors. We derive models from the
experimental results and use them to develop more predictive models. Sometimes
we can use models to reorganize experimental results to fit into the context of
our understanding of the physical world, and sometimes this leads to the
discovery of new factors and new relationships. When we are confident in our
models ability to explain physical phenomena and describe relationships, we use
them to predict. We can use predictive models to find solutions by optimizing
our objectives. Good models are powerful, and they are the very essense of
science.&lt;/p&gt;
&lt;h1 id="software"&gt;Software&lt;/h1&gt;
&lt;p&gt;Computers have revolutionalized the derivation and use of mathematical models
to understand, describe, and predict our universe. We use code to interpret
mathematics and science into a language that computers can use to perform
calculations that would have taken us years to do by hand. But just like pencil
and paper work there are ways to organize code best to accomplish your objectives. Good
modeling software is based on a set core values.&lt;/p&gt;
&lt;h1 id="spaces"&gt;SPACES&lt;/h1&gt;
&lt;p&gt;SPACES is a mnemonic for the core values of good modeling software.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;S&lt;/strong&gt;imple&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;P&lt;/strong&gt;erfomant&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A&lt;/strong&gt;ccurate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;ollaborative&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;E&lt;/strong&gt;xtensible&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;S&lt;/strong&gt;table&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="simplicity"&gt;Simplicity&lt;/h2&gt;
&lt;p&gt;Good modeling software is simple. It doesn't do more or less than what it needs
to do. It doesn't create more problems than it solves. It isn't convoluted or
difficult to explain. It may contain complex algorithms, but the art and beauty
of good code is that it makes the complex elegant. Document your code well. Use
autodocumentation so that there isn't double documentation. Keep as much of the
documenation in the source code as possible. Make comments that explain the
intent of your code and read like a human being wrote them. Write comments to
yourself. Use &lt;code&gt;TODO&lt;/code&gt; and &lt;code&gt;FIXME&lt;/code&gt; frequently in your code as reminders of
changes you still need or want. Break up your documentation into sections based
on the target audience.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Always include a &lt;strong&gt;Quickstart Tutorial&lt;/strong&gt; that is one page and has simple but
realistic examples!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Include a detailed but simple user guide, a separate, more detailed theory
guide, a comprehensive API, and a developer guide that explains things like how
to build the documentation, how the CI is set up, how the server is configured,
how to deploy the software, and what conventions were used. Be detailed.
Consider adding a cookbook of examples, a gallery, tutorials, or HOWTO
sections. Your API should be 100% covered.&lt;/p&gt;
&lt;h2 id="performance"&gt;Performance&lt;/h2&gt;
&lt;p&gt;It just has to be fast enough. Usually there is a trade off between speed and
accuracy, so finding the balance is tricky. But in order to be useful, modeling
software must not be slow. Efficiency where possible should always be a
priority. Maybe not in the first draft, but in revisions inefficiency should be
removed wherever it's found. Don't repeat yourself. However, don't create overly complicated code. Efficient code should still be simple. Strive for elegance.&lt;/p&gt;
&lt;h2 id="accuracy"&gt;Accuracy&lt;/h2&gt;
&lt;p&gt;It just has to be accurate enough. Understand your uncertainties. Regardless,
modeling software that is inaccurate is useless. Make sure it works the way
it's expected. Validate with publicly available data and publish
your results in a notebook, like &lt;a href="https://jupyter.org"&gt;Jupyter&lt;/a&gt;,
with the analysis code so others can run your calculations too.
Check the bias:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;plot the distribution of the bias&lt;/li&gt;
&lt;li&gt;auto correlate against the output&lt;/li&gt;
&lt;li&gt;look for correlation versus time and location, by month and hour
   of the day, and by vertical and horizontal position&lt;/li&gt;
&lt;li&gt;cross correlate against the inputs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Calculate the mean bias difference (MBD) and root mean square difference (RMSD). All correlations should be random and bias should be uniformly distributed.
Don't validate with your training data. Save a third of the data for validation.&lt;/p&gt;
&lt;p&gt;Include high precision test data and use it in your unit tests.&lt;/p&gt;
&lt;h2 id="collaboration"&gt;Collaboration&lt;/h2&gt;
&lt;p&gt;Work together. Science is consensus. Others have already solved your problems.
Others can find new problems. Use what is out there already. Don't reinvent the
wheel. Ask for help. Get opinions. Listen. Consider alternatives. Collaborate.&lt;/p&gt;
&lt;h2 id="extensibility"&gt;Extensibility&lt;/h2&gt;
&lt;p&gt;Things change. Our understanding of the physical world changes. New
technologies evolve. Build in the capacity for change in your modeling
software. Anticipate that your code will need to adapt. Make it easy to
maintain and update. Make it extensible to customization. Modularize. Break
down your model into units that can be reused, so that intermediate values
can be interrogated easily, and so contributions can be made at different entry
points. Consider making your application a stack with a common object that is
passed from unit to unit, and can be serialized and deserialized when needed.
This will make it easier to save and load state, and to scale and parallelize.
It will also make it easier to test.&lt;/p&gt;
&lt;h2 id="stability"&gt;Stability&lt;/h2&gt;
&lt;p&gt;Good modeling software isn't fragile. It should be bomb proof. Test it. Use a
unitest framework as well as some end-to-end tests. Strive for 100% coverage.
Set up a CI server to test on every push to the master branch and on pulls.
Don't use polynomial fits over 3rd order unless they are physical. Set limits
on arguments, and handle exceptions gracefully. Make sure that equations don't
blow up unexpectedly and return &lt;code&gt;NaN&lt;/code&gt; or &lt;code&gt;Inf&lt;/code&gt; everywhere. Make sure that units
are explained in the documentation or user interface. Scale big numbers and
watch out for overflows. Watch for interger division. It's okay to follow the
practice of "easier to ask for forgiveness than permission" but errors have to
be handled eventually, and users deserve meaningful feedback. Just make sure it
always works as expected.&lt;/p&gt;
&lt;h2 id="spaces_1"&gt;Spaces&lt;/h2&gt;
&lt;p&gt;Put spaces in your code. Don't be terse. Follow the coding guidelines common
for the codebase.&lt;/p&gt;</content><category term="Modeling"/><category term="rant"/><category term="code"/><category term="modeling"/></entry><entry><title>Iterative Project Planning in Research and Development</title><link href="https://breakingbytes.github.io/iterative-project-planning-in-research-and-development.html" rel="alternate"/><published>2017-06-16T13:04:39-07:00</published><updated>2017-06-16T13:04:39-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2017-06-16:/iterative-project-planning-in-research-and-development.html</id><summary type="html">&lt;p&gt;Iterative planning is a a successful strategy for research and development.&lt;/p&gt;</summary><content type="html">&lt;h1 id="objectives-and-scope"&gt;Objectives and Scope&lt;/h1&gt;
&lt;p&gt;The purpose of this document is to outline a successful strategy for the optimal
preparation, execution and measurement of plans for open-ended research and
development projects. In particular this document demonstrates the effective use
of short-term iteration cycle planning commonly known as Agile, Scrum or Kanban
project management. It is important to note that the methods described in this
document are not applicable to projects which have well-known execution times
such as engineering, procurement and construction (EPC). Also while short-term
iteration cycle planning may work well for open-ended research questions, they
may be combined with traditional waterfall techniques in time-limited projects.&lt;/p&gt;
&lt;h1 id="methods"&gt;Methods&lt;/h1&gt;
&lt;p&gt;In a nutshell, an iterative project plan has these major features:&lt;/p&gt;
&lt;h2 id="mission"&gt;Mission&lt;/h2&gt;
&lt;p&gt;All projects should have a big giant overriding mission statement. This is what
you look at when you're really lost and trying to re-inspire your team. In other
words, this is what it's all about. For example a school projects mission might
be the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To empower students to become agents of their own learning&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="goals-or-objectives"&gt;Goals or Objectives&lt;/h2&gt;
&lt;p&gt;In order to accomplish the mission, you need a few long-term, broad goals or
objectives that point out what direction you think you need to follow. Don't be
too specific here. Continuing the school environment example, some goals might
be the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Engage students in STEAM&lt;/li&gt;
&lt;li&gt;Reinforce literacy in every interaction&lt;/li&gt;
&lt;li&gt;Create a strong sense of community&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="release-cycle"&gt;Release Cycle&lt;/h2&gt;
&lt;p&gt;Pick a long-term time frame to work in, during which you hope to make progress
in the same direction as these goals. This is your "release cycle". For example
in a school setting the release cycle might be every three months.&lt;/p&gt;
&lt;h2 id="milestones-or-epics"&gt;Milestones or Epics&lt;/h2&gt;
&lt;p&gt;Narrow down milestones for each of the broad goals that you think can be
accomplished during each release cycle. These milestones are sometimes called
"epics" or "projects"&lt;/p&gt;
&lt;h2 id="iteration-cycle"&gt;Iteration Cycle&lt;/h2&gt;
&lt;p&gt;Pick a short-term iteration cycle during which you can meet with your team and
measure progress on your "epics". Iteration cycles vary anywhere from weekly to
monthly, but a good compromise is every two weeks. This time-frame is your
"iteration" or "scrum" because you will often meet your group for a quick tally
kind of like a rugby scrum.&lt;/p&gt;
&lt;h2 id="tasks"&gt;Tasks&lt;/h2&gt;
&lt;p&gt;Brainstorm the detailed, small, atomic tasks that you think would be required to
complete the epics and put them on cards in a list called your backlog. There
are many online tools like &lt;a href="https://trello.com/"&gt;Trello&lt;/a&gt; that are designed to
make this easy, but a large cork board works just fine. An atomic task is nearly
the smallest discreet unit of work you can think of that would need to be done
in order to accomplish the milestone. Tasks are easily measured, and it should
also be easier to predict the time necessary to complete them.&lt;/p&gt;
&lt;h2 id="effort"&gt;Effort&lt;/h2&gt;
&lt;p&gt;Assign each card or task an effort score. Keep the scale small; think of it as a
rubric, not a linear scale, but it could loosely be proportional to time. A
common effort scale might be the following:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Effort&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Approximate time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;chore&lt;/td&gt;
&lt;td&gt;any known length of time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;easy&lt;/td&gt;
&lt;td&gt;&amp;lt; 1 day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;hard&lt;/td&gt;
&lt;td&gt;&amp;lt; 1 week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;mini-epic&lt;/td&gt;
&lt;td&gt;&amp;gt; 1 week&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Usually a chore is a well-defined task that is done all the time so you know
exactly how long it takes. Just mark chores on your schedule and do them. A
mini-epic probably means you should break it into smaller tasks, but you may try
to do it as a single task anyway. Effort is used to measure progress; the data
is used to normalize and prioritize future iteration cycles. Since any data is
useful as long as it is carefully measured and calibrated, you may adjust your
effort scale to fit your needs after several iterations and releases.&lt;/p&gt;
&lt;h3 id="kanban"&gt;Kanban&lt;/h3&gt;
&lt;p&gt;Kanban, Scrum and Agile have many similarities. See this
&lt;a href="https://www.smartsheet.com/agile-vs-scrum-vs-waterfall-vs-kanban"&gt;Smartsheet article for a side-by-side comparison&lt;/a&gt;.
In a nutshell, Agile is a philosophy, while Scrum and Kanban are
implementations of Agile. Scrum introduces the concept of a sprint or interval
while Kanban introduces puts all tasks into lists or swimlanes on a board that
everyone can see and manipulate. IMO forget the names and concentrate of what
works best and is easiest to use. The goal is to be efficient and productive.&lt;/p&gt;
&lt;p&gt;The simplest Kanban board can be divided into 3 swimlanes, which is the default
workflow for a new Trello board:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To do (&lt;em&gt;aka&lt;/em&gt; backlog)&lt;/li&gt;
&lt;li&gt;Doing (&lt;em&gt;aka&lt;/em&gt; current work)&lt;/li&gt;
&lt;li&gt;Done (&lt;em&gt;aka&lt;/em&gt; completed work)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can add more swimlanes to capture other items such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;wishlist, icebox or parking lot - items for a rainy day&lt;/li&gt;
&lt;li&gt;won't do - items ruled out&lt;/li&gt;
&lt;li&gt;in review - almost done&lt;/li&gt;
&lt;li&gt;on deck - in between backlog and current work&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="pomodoro"&gt;Pomodoro&lt;/h3&gt;
&lt;p&gt;One aspect of an iterative workflow that can create anxiety is the measurement
of progress and the evaluation or prediction of the time to complete a task or
milestone. The
&lt;a href="https://cirillocompany.de/pages/pomodoro-technique"&gt;Pomodoro Technique&lt;/a&gt; is a
time management method from the 1980's that provides a concrete conversion rate
between units of work and units of time. One pomodoro is typically equivalent to
30-minutes. Using the Pomodoro technique is a diagnostic tool that eliminates
the need to normalize burndown rates since converting between pomodoros and time
is not subjective.&lt;/p&gt;
&lt;h4 id="trello-and-pomello"&gt;Trello and Pomello&lt;/h4&gt;
&lt;p&gt;Trello is an online tool that offers a Kanban-esque board to organize tasks and
iterations. The &lt;a href="http://www.pomelloapp.com/"&gt;Pomello app&lt;/a&gt; is a plugin for Trello
that integrates Pomodoro. It logs time on task, measures the number of pomodoros
spent on a task and lets you move completed tasks to their swimlane when done.&lt;/p&gt;
&lt;h2 id="current-work-option-1-kanban"&gt;Current Work (Option 1: Kanban)&lt;/h2&gt;
&lt;p&gt;This is my personal preference because IMO the workflow is much simpler since
there are only 3 swimlanes. I use this with the Pomodoro Technique. At the
beginning of each iteration, pull cards into the current work list and count
the number of pomodoros used on each. When a task is done, move it to the
"completed"  list and pull a new item from the backlog. That's it!&lt;/p&gt;
&lt;h2 id="current-iteration-option-2-scrum"&gt;Current Iteration (Option 2: Scrum)&lt;/h2&gt;
&lt;p&gt;Note this method won't work with Pomello, it's better to use
&lt;a href="http://scrumfortrello.com/"&gt;Scrum for Trello&lt;/a&gt; and
&lt;a href="https://www.burndownfortrello.com/"&gt;Burndown for Trello&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At the beginning of each iteration cycle move some cards from the backlog into a
new list called your current iteration. Iterations may be numbered or identified
by date or work-week as long as they are recorded. You should assign individuals
or small teams to tasks and encourage frequent communication and collaboration
between individuals and teams during the iteration. During the iteration teams
and individuals should measure their progress by deducting points from the
initial effort of each task until they are complete and the remaining effort is
zero.&lt;/p&gt;
&lt;p&gt;During the iteration communicate with your team frequently and make adjustments
as needed. For example, during an iteration you might do any or all of the
following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Take new cards from the back log as needed until the iteration is over.&lt;/li&gt;
&lt;li&gt;Reorder cards on the backlog or current iteration board to reprioritize.&lt;/li&gt;
&lt;li&gt;Choose tasks based on their estimated effort and the remaining time in the iteration.&lt;/li&gt;
&lt;li&gt;Reclassify a tasks as an epic and break it into to subtasks.&lt;/li&gt;
&lt;li&gt;Reassign individuals and teams to tasks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="burndown-scrum"&gt;Burndown (Scrum)&lt;/h3&gt;
&lt;p&gt;At the end of the iteration, tally up the effort points completed, add it to the
previous total and divide by the total number of iterations. Plot this data for
everyone, for individuals, for all iterations cumulatively and for each
iteration. These are your burndown rates. Use them to normalize team members and
to determine how much effort can be accomplished per iteration.&lt;/p&gt;
&lt;h4 id="table-1-example-effort-per-iteration"&gt;Table 1: Example Effort per Iteration&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Iteration&lt;/th&gt;
&lt;th&gt;Bobby&lt;/th&gt;
&lt;th&gt;Sally&lt;/th&gt;
&lt;th&gt;Juan&lt;/th&gt;
&lt;th&gt;Briana&lt;/th&gt;
&lt;th&gt;Effort&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;8.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;7.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="table-2-example-cummulative-effort"&gt;Table 2: Example Cummulative Effort&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Iteration&lt;/th&gt;
&lt;th&gt;Bobby&lt;/th&gt;
&lt;th&gt;Sally&lt;/th&gt;
&lt;th&gt;Juan&lt;/th&gt;
&lt;th&gt;Briana&lt;/th&gt;
&lt;th&gt;Cummulative&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;8.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;16.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;23.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;30.5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="table-3-example-burndown-calculation"&gt;Table 3: Example Burndown Calculation&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Iteration&lt;/th&gt;
&lt;th&gt;Bobby&lt;/th&gt;
&lt;th&gt;Sally&lt;/th&gt;
&lt;th&gt;Juan&lt;/th&gt;
&lt;th&gt;Briana&lt;/th&gt;
&lt;th&gt;Average&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;8.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;10.5&lt;/td&gt;
&lt;td&gt;8.125&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;8.67&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;7.67&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;7.83&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;4.5&lt;/td&gt;
&lt;td&gt;7.75&lt;/td&gt;
&lt;td&gt;9.25&lt;/td&gt;
&lt;td&gt;7.625&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Looking at the burndown rates and cumulative effort you can make several
observations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bobby and Briana are either the team’s top performers or are over estimating
  effort. Use this info to normalize the team by either using your own effort
  scale or pairing teammates together to see their joint effort estimates. For
  example if Briana is high performer, her burndown stands, and if Bobby’s
  performance is more equivalent to the team average then adjust it by a factor
  of around 85% to 7.65 instead of 9. Ditto for Sally if she is sandbagging her
  effort estimates, adjust hers by a factor of 1.7x to 7.65.&lt;/li&gt;
&lt;li&gt;Now that your burndown is normalized, the adjusted team average is around
  8 points of Effort per Iteration. Therefore you should only plan to add tasks
  summing up to around 32 effort points each iteration.&lt;/li&gt;
&lt;li&gt;If your iteration is half over, and Juan’s queue is empty, you can only expect
  him to deliver around 4 effort in the remaining time, so choose tasks summing
  up to this or less.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="reprioritization"&gt;Reprioritization&lt;/h2&gt;
&lt;p&gt;After calculating burndown your team can reprioritize tasks and epics, both in
response to data on progress but also due to a shift in the team mission and
associated objectives. Move incomplete tasks from the current iteration either
to epics because they were just too big to do in a single iteration, back to the
backlog because you over allocated effort or to a board called the “Icebox” or
“Parking Lot” to revisit on a rainy day.&lt;/p&gt;
&lt;h2 id="release"&gt;Release&lt;/h2&gt;
&lt;p&gt;At the end of the release, publish what you have. Report on it. Re-evaluate your
milestones and compare to your broad long term goals and your mission.  Adjust
your milestones as needed. Then celebrate!&lt;/p&gt;
&lt;h1 id="examples"&gt;Examples&lt;/h1&gt;
&lt;p&gt;Here are some typical workflows and how you can set them up using Trello.&lt;/p&gt;
&lt;h2 id="kanban_1"&gt;Kanban&lt;/h2&gt;
&lt;p&gt;Just take the default Trello layout and start working. Put ideas that come up
into the backlog. Break them up into tasks as needed. Each week move some items
into the current work list. When an item is finished move it to the completed
list. Use the Pomello app to measure how many pomodoros they take, and use that
info to make predictions about when projects will get done. That's it!&lt;/p&gt;
&lt;h2 id="scrum"&gt;Scrum&lt;/h2&gt;
&lt;p&gt;One way to use Trello for Scrum is to set up 4 boards:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Epics&lt;/li&gt;
&lt;li&gt;Backlog&lt;/li&gt;
&lt;li&gt;Current Iteration by date&lt;/li&gt;
&lt;li&gt;Icebox&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each iteration, I archive the old iteration and create a new iteration board
with the date. I move cards around all the time. I only assign effort to tasks
in the backlog which get dragged to the current iteration. I create links from
tasks to epics and vice-versa. I put lots of comments on the cards as I make
progress.&lt;/p&gt;
&lt;h1 id="rationale"&gt;Rationale&lt;/h1&gt;
&lt;p&gt;There are many arguments for using short-term iteration cycle planning. I will
list the major salient points here.&lt;/p&gt;
&lt;h2 id="uncertainty-is-hyperbolic"&gt;Uncertainty is Hyperbolic&lt;/h2&gt;
&lt;p&gt;Research and development projects are inherently unknowns. There may be some
data that indicates around doing small isolated tasks, but predicting a string
of tasks and how they will proceed from one another is not possible because as
predictions move out of the data centroid and extrapolate to larger projects and
longer time-frames the uncertainty explodes hyperbolically. Therefore better
predictions can only be made close to the data centroid centered on small atomic
tasks.&lt;/p&gt;
&lt;h2 id="humans-are-meta-cognitively-wired"&gt;Humans are Meta Cognitively Wired&lt;/h2&gt;
&lt;p&gt;Meta cognition is understanding about how we think, and we know a lot about how
we think we work.&lt;/p&gt;
&lt;h3 id="we-get-a-rush-from-checking-off-tasks-dopamine"&gt;We get a rush from checking off tasks. (Dopamine)&lt;/h3&gt;
&lt;p&gt;Using iterative project planning with shorter term iterations allows
contributors to feel more sense of accomplishment because they complete more
tasks, they receive recognition for completed work and have a clear sense of
expectations because data is explicit and meaningful. The feeling comes from
dopamine released each time we tick off a task.&lt;/p&gt;
&lt;h3 id="we-dislike-wasted-effort-endorphins"&gt;We dislike wasted effort. (Endorphins)&lt;/h3&gt;
&lt;p&gt;Only tasks are S.M.A.R.T.  Milestones, objectives and mission statements are
only loosely structured allowing teams to concentrate on the task at hand. By
spending less time planning out long-term objectives teams feel a strong sense
of purpose. Nothing is more aggravating than spending a long time planning
something only to have it switched around completely halfway through it. This
frustration leads ultimately to burnout and dysfunction. The corollary to this
is the excitement and morale boost of diving into work quickly and quickly
scoring small victories. Even a few small failures here are seen as positive
data toward the correct course of action because there isn’t a lot riding on
them. Working hard releases endorphins, which soothes anxiety and makes us feel
accomplished. A lack of endorphins however leads to anger and stress.&lt;/p&gt;
&lt;h3 id="we-are-more-productive-when-we-feel-self-directed-and-that-our-work-has-purpose-serotonin"&gt;We are more productive when we feel self-directed and that our work has purpose. (Serotonin)&lt;/h3&gt;
&lt;p&gt;Iterative project planning allows teams to be more productive with less oversite
since they participate in creating tasks and their current workload by taking
tasks from the backlog. Also by leverage Agile project management software to
organize tasks in iterations and calculate burndown, companies can allow
management to focus on the “Big Picture” giving the entire organization a
stronger sense of unity and focus. This sense of purpose and self-value comes
from serotonin released as we partake in planning our own work.&lt;/p&gt;
&lt;h3 id="we-like-to-work-in-groups-oxytocin"&gt;We like to work in groups. (Oxytocin)&lt;/h3&gt;
&lt;p&gt;Iterative project planning is inherently designed for groups. Working together
on a project build trust intimacy and ultimately synergy which multiply
productivity. Understanding and valuing each other’s strengths and weaknesses
makes us closer even in a professional setting. This releases oxytocin which
makes us feel appreciated and loved.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;IMHO iterative project planning is a no brainer for any research and development
group. It increases productivity while reducing costs by leveraging more
individual contributors and sharing leadership.&lt;/p&gt;
&lt;h1 id="links"&gt;Links&lt;/h1&gt;
&lt;p&gt;Wikipedia Articles and Perspectives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.hanselman.com/blog/OptimizeForTinyVictories.aspx"&gt;Optimize for tiny victories - Scott Hanselman&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pragdave.me/blog/2014/03/04/time-to-kill-agile.html"&gt;Agile is dead - Pragma Dave&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Agile_management"&gt;Agile Mangement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Agile_software_development"&gt;Agile Software Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Agile_software_development"&gt;Scrum (software development)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Kanban_(development)"&gt;Kanban (development)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Online Tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://trello.com/markmikofski/recommend"&gt;Trello planning tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.pomelloapp.com/"&gt;Pomello app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://scrumfortrello.com/"&gt;scrumfortrello&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.burndownfortrello.com/"&gt;burndownfortrello&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gantt-chart.com/"&gt;ganttify for Trello&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pivotaltracker.com/"&gt;Pivotal Tracker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redbooth.com/"&gt;Redbooth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="Productivity"/><category term="agile"/><category term="kanban"/><category term="scrum"/><category term="development"/><category term="research"/><category term="time-management"/><category term="project-management"/><category term="project-planning"/></entry><entry><title>Vertically Centered</title><link href="https://breakingbytes.github.io/vertically-centered.html" rel="alternate"/><published>2017-05-25T23:48:00-07:00</published><updated>2017-05-25T23:48:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2017-05-25:/vertically-centered.html</id><summary type="html">&lt;p&gt;Use jQuery to vertically center an element in a window.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I am porting a site, and the original login box would verticaly center, even as
I change the size of the window. There were several StackOverflow answers that
suggested creating a CSS class, which seemed perfect, but they didn't seem to
work. Then I noticed they had some JavaScript in their file to pad the top of
the login &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element. I made some simplifications using jQuery and voila!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Vertically&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;banner&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;banner&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;navbar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;elements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;log_form&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;containing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;minht&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb nb-Type"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;minimum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;separate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;banner&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jQuery&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s resize function.*/&lt;/span&gt;

&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;set_login_form_container_margin_top&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;banner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;log_form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;minht&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;banner_height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;banner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;banner&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;window_height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;login_form_height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;half&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;difference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;between&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;banner&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window_height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;login_form_height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;banner_height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;new_height = &amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;margin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;
&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;margin-top&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;minht&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;minht&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;px&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Use it on your web page with &lt;a href="https://api.jquery.com/resize/"&gt;jQuery's resize function&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/static/js/vertically_center.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;$(function(){
&lt;span class="w"&gt;    &lt;/span&gt;set_login_form_container_margin_top(
&lt;span class="w"&gt;        &lt;/span&gt;&amp;quot;#my_banner&amp;quot;,
&lt;span class="w"&gt;        &lt;/span&gt;&amp;quot;#login-form-container&amp;quot;,&lt;span class="w"&gt; &lt;/span&gt;0);
&lt;span class="w"&gt;  &lt;/span&gt;});
&lt;span class="w"&gt;  &lt;/span&gt;$(&lt;span class="w"&gt; &lt;/span&gt;window&lt;span class="w"&gt; &lt;/span&gt;).resize(function(){
&lt;span class="w"&gt;    &lt;/span&gt;set_login_form_container_margin_top(
&lt;span class="w"&gt;        &lt;/span&gt;&amp;quot;#my_banner&amp;quot;,
&lt;span class="w"&gt;        &lt;/span&gt;&amp;quot;#login-form-container&amp;quot;,&lt;span class="w"&gt; &lt;/span&gt;0);
&lt;span class="w"&gt;  &lt;/span&gt;});
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="JavaScript"/><category term="HTML"/><category term="web"/><category term="jQuery"/></entry><entry><title>Python-3 Virtual Environments on MS Windows</title><link href="https://breakingbytes.github.io/python-3-virtual-environments-on-ms-windows.html" rel="alternate"/><published>2017-05-18T11:01:00-07:00</published><updated>2017-05-18T11:01:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2017-05-18:/python-3-virtual-environments-on-ms-windows.html</id><summary type="html">&lt;p&gt;Python-3 venv module vs virtualenv package&lt;/p&gt;</summary><content type="html">&lt;p&gt;This is a follow up from my &lt;a href="https://breakingbytes.github.io/moving-to-pelican-at-github-pages.html"&gt;first post&lt;/a&gt;
on this new Pelican blog. I should have logged how I set up this, instead of
the glib cliché, "usage is a snap". Here I am posting for the second time, and
I'm wondering, "why didn't I just use the
&lt;a href="https://help.github.com/articles/about-github-pages-and-jekyll/"&gt;built in Jekyll static site generator&lt;/a&gt;?"&lt;/p&gt;
&lt;p&gt;Anyway, for posterity...&lt;/p&gt;
&lt;h1 id="publishing-to-gh-pages"&gt;Publishing to GH Pages&lt;/h1&gt;
&lt;p&gt;To publish my pages I use the &lt;code&gt;Makefile&lt;/code&gt; that Pelican generates when you start
your blog using &lt;code&gt;pelican-quickstart&lt;/code&gt;. I may have editted the &lt;code&gt;Makefile&lt;/code&gt; to
target my GitHub &lt;code&gt;master&lt;/code&gt; branch since I'm using
&lt;a href="http://docs.getpelican.com/en/stable/tips.html#user-pages"&gt;User Pages&lt;/a&gt; or maybe
Pelican asked me during the quickstart questionnaire, I can't remember. The
&lt;code&gt;Makefile&lt;/code&gt; uses a handy tool called
&lt;a href="https://github.com/davisp/ghp-import"&gt;&lt;code&gt;ghp-import&lt;/code&gt;&lt;/a&gt; that copies the contents of
the output directory to the target git branch. Then the &lt;code&gt;Makefile&lt;/code&gt; publishes
the blog by pushing the target branch to GitHub. The &lt;code&gt;ghp-import&lt;/code&gt;
package is described a bit in the
&lt;a href="http://docs.getpelican.com/en/stable/tips.html#publishing-to-github"&gt;Pelican Tips&lt;/a&gt;
on publishing to GitHub pages, but the &lt;code&gt;Makefile&lt;/code&gt; takes care of calling it. So
all I have to do to publish my posts is execute:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;make github
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You could even stick it into &lt;code&gt;.git/hooks/post-commit&lt;/code&gt; to automate publishing.&lt;/p&gt;
&lt;h1 id="python-3-virtual-environment"&gt;Python-3 Virtual Environment&lt;/h1&gt;
&lt;p&gt;Okay, I can't remeber exactly the reason, but &lt;code&gt;ghp-import&lt;/code&gt; worked better in
Python-3 than Python-2, and so I created a virtual environment for Python-3.
This brings me finally to the title of this post. How does one create a virtual
environment for Python-3? Well according to the
&lt;a href="https://packaging.python.org/installing/#creating-virtual-environments"&gt;Python Packaging Authority (PyPA)&lt;/a&gt;
since Python-3.3 there is a built in module called
&lt;a href="https://docs.python.org/3.5/library/venv.html"&gt;&lt;code&gt;venv&lt;/code&gt;&lt;/a&gt;. However the venerable
&lt;a href="https://virtualenv.pypa.io/en/stable/"&gt;&lt;code&gt;virtualenv&lt;/code&gt;&lt;/a&gt; package also works just
fine for Python-3.&lt;/p&gt;
&lt;p&gt;How do these packages differ? There is one major difference that really affects
me. The built in Python-3 &lt;code&gt;venv&lt;/code&gt; module only activates in MS Windows &lt;code&gt;CMD&lt;/code&gt;
terminal or PowerShell whereas Ian Bicking's indispensible &lt;code&gt;virtualenv&lt;/code&gt; package
works in BaSH as well. This is an issue with Python on MS Windows that I
encounter a lot, it's a PITA and breaks the whole concept of a common unified
experience regardless of user's platform. I should be able to use Python exactly
the same on any system with very minor exceptions. Since I tend to mostly
use BaSH, this means that to use the built in &lt;code&gt;venv&lt;/code&gt; module I have to
switch to a MS Windows &lt;code&gt;CMD&lt;/code&gt; shell. I guess it's not that big of a deal, but
since &lt;code&gt;virtualenv&lt;/code&gt; works fine with Python-3, I guess I'll stick with that.&lt;/p&gt;
&lt;p&gt;Okay, there's also one other important difference. Python-3 installs all of its
shared objects into the virtual environment, which is different from Python-2
which uses links mostly. This means that when you update your version of
Python-3 you need to &lt;em&gt;also&lt;/em&gt; update your virtual environment.&lt;/p&gt;
&lt;p&gt;For the &lt;code&gt;virtualenv&lt;/code&gt; package you'll have to create a new virtual environment on
top of the old one. But for the built in &lt;code&gt;venv&lt;/code&gt; module you can just run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;py -3 -m venv --upgrade &amp;lt;my-py3-venv&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;where &lt;code&gt;&amp;lt;my-py3-venv&amp;gt;&lt;/code&gt; is the name of your Python-3 virtual environment. I think
the source code for both are nearly the same, and I also think that under the
hood the &lt;code&gt;--upgrade&lt;/code&gt; option is really doing exactly the same thing as
&lt;code&gt;virtualenv&lt;/code&gt;, the difference is that if you try to create a virtual environment
on top of an existing one with the Python-3 &lt;code&gt;venv&lt;/code&gt; module is will raise an
exception.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Changed in version 3.4: In earlier versions, if the target directory already
existed, an error was raised, unless the &lt;code&gt;--clear&lt;/code&gt; or &lt;code&gt;--upgrade&lt;/code&gt; option was
provided. Now, if an existing directory is specified, its contents are removed
and the directory is processed as if it had been newly created.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In conclusion, I don't see much difference between the two methods. So if you
want to use BaSH on MS Windows, stick with the original, otherwise try the new.&lt;/p&gt;</content><category term="Python"/><category term="virtualenv"/><category term="pelican"/><category term="windows"/><category term="bash"/><category term="rant"/></entry><entry><title>Moving to Pelican at GitHub Pages</title><link href="https://breakingbytes.github.io/moving-to-pelican-at-github-pages.html" rel="alternate"/><published>2017-04-03T13:02:00-07:00</published><updated>2017-04-03T13:02:00-07:00</updated><author><name>Mark Mikofski</name></author><id>tag:breakingbytes.github.io,2017-04-03:/moving-to-pelican-at-github-pages.html</id><summary type="html">&lt;p&gt;So this is goodbye to Google Blogger and my old blog at
&lt;a href="http://poquitopicante.blogspot.com/"&gt;Poquito Picante&lt;/a&gt;, although I'll try to
keep &lt;a href="https://breakingbytes.blogspot.com/"&gt;Breaking Bytes&lt;/a&gt; up to date if I can
figure out how to make a commit hook that posts the generated content using the
&lt;a href="https://developers.google.com/blogger/"&gt;Blogger API v3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And this is hello to …&lt;/p&gt;</summary><content type="html">&lt;p&gt;So this is goodbye to Google Blogger and my old blog at
&lt;a href="http://poquitopicante.blogspot.com/"&gt;Poquito Picante&lt;/a&gt;, although I'll try to
keep &lt;a href="https://breakingbytes.blogspot.com/"&gt;Breaking Bytes&lt;/a&gt; up to date if I can
figure out how to make a commit hook that posts the generated content using the
&lt;a href="https://developers.google.com/blogger/"&gt;Blogger API v3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And this is hello to &lt;a href="https://blog.getpelican.com/"&gt;Pelican&lt;/a&gt;! I did a very quick
&lt;a href="https://www.fullstackpython.com/static-site-generator.html"&gt;search for static site generators&lt;/a&gt;
like Pelican, Hyde and a few others, and Pelican quickly emerged as the most
popular. &lt;a href="https://news.ycombinator.com/item?id=4928719"&gt;Also Kenneth Reitz said to use it.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So far Pelican usage is a snap, just follow
&lt;a href="http://docs.getpelican.com/en/stable/index.html"&gt;the quickstart in their documentation&lt;/a&gt;.
So this is my very first, albeit very short, post for my new blog site. Why leave
Google Blogger?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I've wanted to for a very long time, b/c I wanted to manage my blog using
  Git.&lt;/li&gt;
&lt;li&gt;My old blog is now being crawled every few hours which just seems weird.&lt;/li&gt;
&lt;li&gt;I prefer my &lt;code&gt;breakingbytes&lt;/code&gt; handle to "Poquito Picante" which is just a
  silly alliteration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more info about me please visit &lt;a href="https://mikofski.github.io/"&gt;me here&lt;/a&gt;.&lt;/p&gt;</content><category term="Announcements"/></entry></feed>