-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.xml
619 lines (462 loc) · 74.8 KB
/
index.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Zach Perzan</title>
<link>https://zperzan.github.io/</link>
<atom:link href="https://zperzan.github.io/index.xml" rel="self" type="application/rss+xml" />
<description>Zach Perzan</description>
<generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><lastBuildDate>Tue, 03 Oct 2023 00:00:00 +0000</lastBuildDate>
<image>
<url>https://zperzan.github.io/media/icon_hud1d0979e3910d321b321c3c963a9ae6e_14691_512x512_fill_lanczos_center_3.png</url>
<title>Zach Perzan</title>
<link>https://zperzan.github.io/</link>
</image>
<item>
<title>Research</title>
<link>https://zperzan.github.io/research/</link>
<pubDate>Sun, 01 Oct 2023 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/research/</guid>
<description></description>
</item>
<item>
<title>CrunchFlow</title>
<link>https://zperzan.github.io/software/crunchflow/</link>
<pubDate>Sat, 30 Sep 2023 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/software/crunchflow/</guid>
<description></description>
</item>
<item>
<title>ParFlow</title>
<link>https://zperzan.github.io/software/parflow/</link>
<pubDate>Wed, 30 Aug 2023 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/software/parflow/</guid>
<description></description>
</item>
<item>
<title>ParCrunchFlow</title>
<link>https://zperzan.github.io/software/parcrunchflow/</link>
<pubDate>Sun, 30 Jul 2023 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/software/parcrunchflow/</guid>
<description></description>
</item>
<item>
<title>Biogeochemical cycling in high-elevation floodplains</title>
<link>https://zperzan.github.io/currentresearch/floodplains/</link>
<pubDate>Sat, 01 Oct 2022 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/currentresearch/floodplains/</guid>
<description><p>Floodplains are complex, heterogeneous systems that retain, transmit and transform nutrients and contaminants as they move between land and surface water. Moreover, an array of biogeochemical and hydrologic processes interact within these heterogeneous environments, from preferential flow along root channels to microbially mediated reactions. In the face of this complexity, identifying the dominant processes that control floodplain solute exports &mdash; and hence surface water quality &mdash; is difficult.</p>
<p>We are using both measurements and modeling to understand the dominant processes that govern nutrient and contaminant cycling within floodplains. Our work focuses on select field sites across the western U.S. in which we have installed sensor arrays and collected manual samples. We then pair these measurements with reactive transport models to identify and understand the drivers behind our observations.</p>
</description>
</item>
<item>
<title>EcoSLIM</title>
<link>https://zperzan.github.io/software/ecoslim/</link>
<pubDate>Fri, 30 Sep 2022 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/software/ecoslim/</guid>
<description></description>
</item>
<item>
<title>Controls on water quality during managed aquifer recharge</title>
<link>https://zperzan.github.io/currentresearch/recharge/</link>
<pubDate>Sat, 01 Jan 2022 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/currentresearch/recharge/</guid>
<description><body>
<figure>
<video width="991" height="529" loop autoplay muted>
<source src="crosssec.mp4" type="video/mp4">
</video>
<figcaption>
<p>
A cross-section showing water particles moving to the water table over an 80-year flood-MAR simulation.
</p>
</figcaption>
</figure>
</body>
<p>Over the coming decades, increased interannual variability in precipitation &mdash; wetter wet years and drier dry years &mdash; will place increased demand on our water infrastructure. Though we need additional water storage capacity to store water from rainy years for use during drought years, in many regions of the world water storage infrastructure is already maxed out. Flood managed aquifer recharge (flood-MAR) &mdash; an emerging technique in which working landscapes are flooded to replenish depleted aquifers &mdash; could meet this gap. However, identifying the most promising flood-MAR sites presents a challenge; many prospective sites are underlain by heterogeneous layers of sand and clay that can obstruct flow. In addition, the vadose zone beneath many flood-MAR sites can host a variety of contaminants, including fertilizers, salts and pesticide byproducts. A common conceptual model posits that initial flood-MAR operations will flush these contaminants to the saturated zone, degrading water quality, but in the long term, recharge operations will improve groundwater quality by diluting contaminant concentrations (<a href="https://doi.org/10.1021/es501115c" target="_blank" rel="noopener">Bachand et al., 2014</a>).</p>
<p>We are combining high-resolution geophysical surveys with numerical modeling to understand the processes that control the flow of water and solutes during managed aquifer recharge. Future work will use machine learning surrogate models and airborne geophysical surveys to scale up our analyses from the site- to basin-scale. This work addresses a gap in our understanding of solute transport during extreme floods and provides actionable information to mitigate both flood damage and groundwater depletion.</p>
<body>
<figure>
<video width="1224" height="964" loop autoplay muted>
<source src="voxel.mp4" type="video/mp4">
</video>
<figcaption>
<p>
Simulated water content following a flood-MAR recharge event.
</p>
</figcaption>
</figure>
</body>
</description>
</item>
<item>
<title>A new tool to measure groundwater velocity</title>
<link>https://zperzan.github.io/pastresearch/wellstics/</link>
<pubDate>Fri, 01 Oct 2021 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/pastresearch/wellstics/</guid>
<description><p>Groundwater velocity is inherently difficult to measure, especially at sites with limited infrastructure. Most modern groundwater velocity sensors are expensive ($1,110 to $18,000). More than that, they can only be installed in wells 2 inches in diameter or larger, which are expensive and time-consuming to install.</p>
<p>We&rsquo;ve developed a new sensor system that can be installed in narrow wells (as small as 8 mm ID), like <a href="https://www.solinst.com/products/direct-push-equipment/615-drive-point-piezometers/datasheet/" target="_blank" rel="noopener">Solinst drive-point piezometers</a>. These piezometer systems can be installed by hand (even in cobble!) and we&rsquo;ve successfully used the system to measure groundwater velocity several sites. Read more about it in our recent <a href="https://doi.org/10.1029/2022WR033223" target="_blank" rel="noopener">paper</a> in Water Resources Research!</p>
</description>
</item>
<item>
<title>MIN3P</title>
<link>https://zperzan.github.io/software/min3p/</link>
<pubDate>Thu, 30 Sep 2021 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/software/min3p/</guid>
<description></description>
</item>
<item>
<title>Scraping 5-min weather data from Weather Underground</title>
<link>https://zperzan.github.io/projects/scrape-weather-underground/</link>
<pubDate>Sun, 25 Jul 2021 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/projects/scrape-weather-underground/</guid>
<description><p>Weather Undergound stores data from over 250,000 personal weather stations across the world. Unfortunately, historical data are not easy to access. It&rsquo;s possible to view tables of 5-min data from a single day &ndash; see <a href="https://www.wunderground.com/dashboard/pws/KCOCREST39/table/2021-07-25/2021-07-25/daily" target="_blank" rel="noopener">this example</a> from a station outside Crested Butte, Colorado &ndash; but if you try to scrape the http using something like Python&rsquo;s <code>requests</code> library, the tables appear blank.</p>
<p>Weather Underground has a security policy that blocks automated requests from viewing data stored in each table. This is where <a href="https://www.selenium.dev/documentation/en/webdriver/" target="_blank" rel="noopener">Selenium WebDriver</a> comes in. WebDriver is an toolbox for natively running web browsers, so when you render a page with WebDriver, Weather Underground thinks a regular user is accessing their website and you can access the full source code.</p>
<p>To run the script, the first thing to do is ensure that <a href="https://chromedriver.chromium.org/" target="_blank" rel="noopener">ChromeDriver</a> is installed. Note that you have to match the ChromeDriver version to whichever version of Chrome is installed on your machine. It&rsquo;s also possible to use something other than Chrome, for example <a href="https://github.com/mozilla/geckodriver/releases" target="_blank" rel="noopener">geckodriver</a> for Firefox or <a href="https://webkit.org/blog/6900/webdriver-support-in-safari-10/" target="_blank" rel="noopener">safaridriver</a> for Safari.</p>
<p>Next, update the path to chromedriver in <code>scrape_wunderground.py</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Set the absolute path to chromedriver</span>
</span></span><span class="line"><span class="cl"><span class="n">chromedriver_path</span> <span class="o">=</span> <span class="s1">&#39;/path/to/chromedriver&#39;</span>
</span></span></code></pre></div><p>As long as BeautifulSoup and Selenium are installed, the script should work fine after that. However, there are a few important points to note about processing the data once it&rsquo;s downloaded:</p>
<ol>
<li>All data is listed in local time. So summer data is in daylight savings time and winter data is in standard time.</li>
<li>Depending on the quality of the station,</li>
<li>All pressure data is reported as sea-level pressure. Depending on the weather station, it may be possible to back-calculate to absolute pressure; some manufacturers (e.g., Ambient Weather WS-2902) use a constant offset whereas others (e.g., Davis Vantage Pro2) perform a more complicated barometric pressure reduction using the station&rsquo;s 12-hr temperature and humidity history.</li>
</ol>
<p>The full Python script is available <a href="https://github.com/zperzan/scrape_wunderground" target="_blank" rel="noopener">here</a> but is also included below.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;Module to scrape 5-min personal weather station data from Weather Underground.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Usage is:
</span></span></span><span class="line"><span class="cl"><span class="s2">&gt;&gt;&gt; python scrape_wunderground.py STATION DATE
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">where station is a personal weather station (e.g., KCAJAMES3) and date is in the
</span></span></span><span class="line"><span class="cl"><span class="s2">format YYYY-MM-DD.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Alternatively, each function below can be imported and used in a separate python
</span></span></span><span class="line"><span class="cl"><span class="s2">script. Note that a working version of chromedriver must be installed and the absolute
</span></span></span><span class="line"><span class="cl"><span class="s2">path to executable has to be updated below (&#34;chromedriver_path&#34;).
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Zach Perzan, 2021-07-28&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">bs4</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span> <span class="k">as</span> <span class="n">BS</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">selenium</span> <span class="kn">import</span> <span class="n">webdriver</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Set the absolute path to chromedriver</span>
</span></span><span class="line"><span class="cl"><span class="n">chromedriver_path</span> <span class="o">=</span> <span class="s1">&#39;/path/to/chromedriver&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">render_page</span><span class="p">(</span><span class="n">url</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34;Given a url, render it with chromedriver and return the html source
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2"> Parameters
</span></span></span><span class="line"><span class="cl"><span class="s2"> ----------
</span></span></span><span class="line"><span class="cl"><span class="s2"> url : str
</span></span></span><span class="line"><span class="cl"><span class="s2"> url to render
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2"> Returns
</span></span></span><span class="line"><span class="cl"><span class="s2"> -------
</span></span></span><span class="line"><span class="cl"><span class="s2"> r :
</span></span></span><span class="line"><span class="cl"><span class="s2"> rendered page source
</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">driver</span> <span class="o">=</span> <span class="n">webdriver</span><span class="o">.</span><span class="n">Chrome</span><span class="p">(</span><span class="n">chromedriver_path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">driver</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="c1"># Could potentially decrease the sleep time</span>
</span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="n">driver</span><span class="o">.</span><span class="n">page_source</span>
</span></span><span class="line"><span class="cl"> <span class="n">driver</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">r</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">scrape_wunderground</span><span class="p">(</span><span class="n">station</span><span class="p">,</span> <span class="n">date</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34;Given a PWS station ID and date, scrape that day&#39;s data from Weather
</span></span></span><span class="line"><span class="cl"><span class="s2"> Underground and return it as a dataframe.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2"> Parameters
</span></span></span><span class="line"><span class="cl"><span class="s2"> ----------
</span></span></span><span class="line"><span class="cl"><span class="s2"> station : str
</span></span></span><span class="line"><span class="cl"><span class="s2"> The personal weather station ID
</span></span></span><span class="line"><span class="cl"><span class="s2"> date : str
</span></span></span><span class="line"><span class="cl"><span class="s2"> The date for which to acquire data, formatted as &#39;YYYY-MM-DD&#39;
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2"> Returns
</span></span></span><span class="line"><span class="cl"><span class="s2"> -------
</span></span></span><span class="line"><span class="cl"><span class="s2"> df : dataframe or None
</span></span></span><span class="line"><span class="cl"><span class="s2"> A dataframe of weather observations, with index as pd.DateTimeIndex
</span></span></span><span class="line"><span class="cl"><span class="s2"> and columns as the observed data
</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Render the url and open the page source as BS object</span>
</span></span><span class="line"><span class="cl"> <span class="n">url</span> <span class="o">=</span> <span class="s1">&#39;https://www.wunderground.com/dashboard/pws/</span><span class="si">%s</span><span class="s1">/table/</span><span class="si">%s</span><span class="s1">/</span><span class="si">%s</span><span class="s1">/daily&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">station</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">date</span><span class="p">,</span> <span class="n">date</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="n">render_page</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">soup</span> <span class="o">=</span> <span class="n">BS</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="s2">&#34;html.parser&#34;</span><span class="p">,)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">container</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s1">&#39;lib-history-table&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Check that lib-history-table is found</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">container</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&#34;could not find lib-history-table in html source for </span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Get the timestamps and data from two separate &#39;tbody&#39; tags</span>
</span></span><span class="line"><span class="cl"> <span class="n">all_checks</span> <span class="o">=</span> <span class="n">container</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="s1">&#39;tbody&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">time_check</span> <span class="o">=</span> <span class="n">all_checks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="n">data_check</span> <span class="o">=</span> <span class="n">all_checks</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Iterate through &#39;tr&#39; tags and get the timestamps</span>
</span></span><span class="line"><span class="cl"> <span class="n">hours</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">time_check</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="s1">&#39;tr&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">trial</span> <span class="o">=</span> <span class="n">i</span><span class="o">.</span><span class="n">get_text</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">hours</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">trial</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># For data, locate both value and no-value (&#34;--&#34;) classes</span>
</span></span><span class="line"><span class="cl"> <span class="n">classes</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;wu-value wu-value-to&#39;</span><span class="p">,</span> <span class="s1">&#39;wu-unit-no-value ng-star-inserted&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Iterate through span tags and get data</span>
</span></span><span class="line"><span class="cl"> <span class="n">data</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">data_check</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="s1">&#39;span&#39;</span><span class="p">,</span> <span class="n">class_</span><span class="o">=</span><span class="n">classes</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">trial</span> <span class="o">=</span> <span class="n">i</span><span class="o">.</span><span class="n">get_text</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">data</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">trial</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">columns</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;Temperature&#39;</span><span class="p">,</span> <span class="s1">&#39;Dew Point&#39;</span><span class="p">,</span> <span class="s1">&#39;Humidity&#39;</span><span class="p">,</span> <span class="s1">&#39;Wind Speed&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Wind Gust&#39;</span><span class="p">,</span> <span class="s1">&#39;Pressure&#39;</span><span class="p">,</span> <span class="s1">&#39;Precip. Rate&#39;</span><span class="p">,</span> <span class="s1">&#39;Precip. Accum.&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Convert NaN values (stings of &#39;--&#39;) to np.nan</span>
</span></span><span class="line"><span class="cl"> <span class="n">data_nan</span> <span class="o">=</span> <span class="p">[</span><span class="n">np</span><span class="o">.</span><span class="n">nan</span> <span class="k">if</span> <span class="n">x</span> <span class="o">==</span> <span class="s1">&#39;--&#39;</span> <span class="k">else</span> <span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">data</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Convert list of data to an array</span>
</span></span><span class="line"><span class="cl"> <span class="n">data_array</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">data_nan</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">float</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">data_array</span> <span class="o">=</span> <span class="n">data_array</span><span class="o">.</span><span class="n">reshape</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">columns</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Prepend date to HH:MM strings</span>
</span></span><span class="line"><span class="cl"> <span class="n">timestamps</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1"> </span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">date</span><span class="p">,</span> <span class="n">t</span><span class="p">)</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">hours</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Convert to dataframe</span>
</span></span><span class="line"><span class="cl"> <span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="n">index</span><span class="o">=</span><span class="n">timestamps</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">data_array</span><span class="p">,</span> <span class="n">columns</span><span class="o">=</span><span class="n">columns</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">df</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">to_datetime</span><span class="p">(</span><span class="n">df</span><span class="o">.</span><span class="n">index</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">df</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">scrape_multiattempt</span><span class="p">(</span><span class="n">station</span><span class="p">,</span> <span class="n">date</span><span class="p">,</span> <span class="n">attempts</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">wait_time</span><span class="o">=</span><span class="mf">5.0</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34;Try to scrape data from Weather Underground. If there is an error on the
</span></span></span><span class="line"><span class="cl"><span class="s2"> first attempt, try again.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2"> Parameters
</span></span></span><span class="line"><span class="cl"><span class="s2"> ----------
</span></span></span><span class="line"><span class="cl"><span class="s2"> station : str
</span></span></span><span class="line"><span class="cl"><span class="s2"> The personal weather station ID
</span></span></span><span class="line"><span class="cl"><span class="s2"> date : str
</span></span></span><span class="line"><span class="cl"><span class="s2"> The date for which to acquire data, formatted as &#39;YYYY-MM-DD&#39;
</span></span></span><span class="line"><span class="cl"><span class="s2"> attempts : int, default 4
</span></span></span><span class="line"><span class="cl"><span class="s2"> Maximum number of times to try accessing before failuer
</span></span></span><span class="line"><span class="cl"><span class="s2"> wait_time : float, default 5.0
</span></span></span><span class="line"><span class="cl"><span class="s2"> Amount of time to wait in between attempts
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2"> Returns
</span></span></span><span class="line"><span class="cl"><span class="s2"> -------
</span></span></span><span class="line"><span class="cl"><span class="s2"> df : dataframe or None
</span></span></span><span class="line"><span class="cl"><span class="s2"> A dataframe of weather observations, with index as pd.DateTimeIndex
</span></span></span><span class="line"><span class="cl"><span class="s2"> and columns as the observed data
</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Try to download data limited number of attempts</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">attempts</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">df</span> <span class="o">=</span> <span class="n">scrape_wunderground</span><span class="p">(</span><span class="n">station</span><span class="p">,</span> <span class="n">date</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">except</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># if unsuccessful, pause and retry</span>
</span></span><span class="line"><span class="cl"> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">wait_time</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># if successful, then break</span>
</span></span><span class="line"><span class="cl"> <span class="k">break</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># If all attempts failed, return empty df</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">df</span>
</span></span></code></pre></div></description>
</item>
<item>
<title>Data-driven water quality modeling</title>
<link>https://zperzan.github.io/currentresearch/sensors/</link>
<pubDate>Thu, 01 Jul 2021 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/currentresearch/sensors/</guid>
<description><p>Traditionally, hydrologic modeling has relied on a suite of process-based models developed by many different researchers over years or even decades. These models contain a wealth of domain knowledge about contaminant transport &mdash; the physics of water flow and the chemistry of water-rock interactions &mdash; but they are relatively fixed and not designed to handle new streams of incoming data; a calibrated reactive transport model, for example, has to be completely re-calibrated if new observations are not in agreement with model predictions.</p>
<p>Recent advances in data science and <em>in situ</em> sensors &mdash; when combined with robust biogeochemical models &mdash; offer a unique opportunity to address this challenge. We have installed a network of solar-powered environmental sensors at sites in Colorado and Wyoming that provide continuous, high-frequency measurements of hydrologic conditions, microbial metabolic activity and key biogeochemical constituents. Remote connection via cellular modem allows us to access this data and update models in real time.</p>
<p>We are using several techniques to combine the knowledge contained within (process-based) reactive transport models with the flexibility and adaptability of modern machine learning/deep learning models. This includes:</p>
<ol>
<li>Assimilating sensor data into a basic reactive transport model with an ensemble Kalman filter</li>
<li>Training an LSTM with data from reactive transport model simulations, then using transfer learning to apply the model to real-world data</li>
<li>The same as above, but using sensor data from other sites across the world</li>
<li>Augementing sensor training data with generative adverarial networks (GAN) then building a GRU, LSTM, or sequence-to-sequence model</li>
</ol>
</description>
</item>
<item>
<title>python-crunchflow</title>
<link>https://zperzan.github.io/software/python-crunchflow/</link>
<pubDate>Thu, 06 May 2021 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/software/python-crunchflow/</guid>
<description></description>
</item>
<item>
<title>Reimagining the nitrogen cycle</title>
<link>https://zperzan.github.io/pastresearch/ednr/</link>
<pubDate>Sat, 01 May 2021 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/pastresearch/ednr/</guid>
<description><p>Nitrogen is one of the most ubiquitous, costly and challenging environmental pollutants of the 21st century. In addition to clean water issues, the 21st century will require more efficient fertilizer production to meet the needs of a growing population. As part of an interdisciplinary research team, we aim to tackle these two challenges by converting waterborne nitrogen pollutants into high-purity fertilizer.</p>
<p>Collaborators in the Dept. of Chemical Engineering have developed a new methodology for exactly this purpose: electrodialysis and nitrate reduction (EDNR). The next step is to build and optimize a device that in a real-world setting. In collaboration with our industry partners (the New York City Dept. of Environmental Protection, San Francisco Water Power Sewer, Jasper Ridge Biological Preserve, and others), we aim to deploy EDNR systems in both urban and rural settings.</p>
</description>
</item>
<item>
<title>pyDGSA</title>
<link>https://zperzan.github.io/software/pydgsa/</link>
<pubDate>Wed, 30 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/software/pydgsa/</guid>
<description></description>
</item>
<item>
<title>Teaching an RNN to write movie scripts</title>
<link>https://zperzan.github.io/projects/script-generator/</link>
<pubDate>Thu, 25 Oct 2018 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/projects/script-generator/</guid>
<description><p><em>Note: all code and data for this project can be found in a <a href="https://github.com/zperzan/tarantino" target="_blank" rel="noopener">GitHub repository</a></em></p>
<p>Around Christmas, I was home visiting family and sat through an overload of Hallmark Channel holiday movies. To me, Hallmark holiday movies all have the same plot and same characters with different names, so I joked that a well-trained neural net could easily write one and no one would know the difference.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> That joke made me wonder how hard it would be to train an RNN to write movie scripts, so I set out to try.</p>
<p>It turns out scripts for movies on the Hallmark Channel are hard to find, so I decided to use screenplays written by Quentin Tarantino instead. I couldn&rsquo;t find a usable copy of <em>Grindhouse: Death Proof</em>, but I got vector PDFs for everything else &ndash;
12 out of 13 isn&rsquo;t bad.</p>
<p>I converted the PDFs to text, cleaned up the text using a combination of <code>sed</code> and <code>awk</code>, embedded the characters as one-hot vectors, and fed that into a bidirectional LSTM. Words in all caps have special meaning in screenplays (names of characters, camera directions), so I embedded upper and lower case letters separately.</p>
<p>The finished model consists of 2 bidirectional LSTM layers &ndash; each with 512 nodes and 20% recurrent dropout &ndash; topped off by fully-connected Softmax layer with 82 nodes (there are 82 total characters in the model). The full details and all the code is available in the <a href="https://github.com/zperzan/tarantino" target="_blank" rel="noopener">repo</a>. For now, let&rsquo;s see some sample output:</p>
<div class="gallery-grid">
<div class="gallery-item gallery-item--medium">
<a data-fancybox="gallery-tarantino" href="https://zperzan.github.io/media/albums/tarantino/TextSample2.jpg" data-caption="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words.">
<img src="https://zperzan.github.io/media/albums/tarantino/TextSample2_hu8f2ae577097e28cb24b40b2fead3709a_78458_750x750_fit_q75_h2_lanczos.webp" loading="lazy" alt="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words." width="750" height="663">
</a>
</div>
<div class="gallery-item gallery-item--medium">
<a data-fancybox="gallery-tarantino" href="https://zperzan.github.io/media/albums/tarantino/TextSample3.jpg" data-caption="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words.">
<img src="https://zperzan.github.io/media/albums/tarantino/TextSample3_hu8f2ae577097e28cb24b40b2fead3709a_81169_750x750_fit_q75_h2_lanczos.webp" loading="lazy" alt="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words." width="750" height="663">
</a>
</div>
<div class="gallery-item gallery-item--medium">
<a data-fancybox="gallery-tarantino" href="https://zperzan.github.io/media/albums/tarantino/TextSample4.jpg" data-caption="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words.">
<img src="https://zperzan.github.io/media/albums/tarantino/TextSample4_hu8f2ae577097e28cb24b40b2fead3709a_81305_750x750_fit_q75_h2_lanczos.webp" loading="lazy" alt="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words." width="750" height="663">
</a>
</div>
<div class="gallery-item gallery-item--medium">
<a data-fancybox="gallery-tarantino" href="https://zperzan.github.io/media/albums/tarantino/TextSample5.jpg" data-caption="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words.">
<img src="https://zperzan.github.io/media/albums/tarantino/TextSample5_hu8f2ae577097e28cb24b40b2fead3709a_80077_750x750_fit_q75_h2_lanczos.webp" loading="lazy" alt="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words." width="750" height="663">
</a>
</div>
<div class="gallery-item gallery-item--medium">
<a data-fancybox="gallery-tarantino" href="https://zperzan.github.io/media/albums/tarantino/TextSample6.jpg" data-caption="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words. The only change">
<img src="https://zperzan.github.io/media/albums/tarantino/TextSample6_hu8f2ae577097e28cb24b40b2fead3709a_80887_750x750_fit_q75_h2_lanczos.webp" loading="lazy" alt="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words. The only change" width="750" height="663">
</a>
</div>
<div class="gallery-item gallery-item--medium">
<a data-fancybox="gallery-tarantino" href="https://zperzan.github.io/media/albums/tarantino/TextSample7.jpg" data-caption="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words.">
<img src="https://zperzan.github.io/media/albums/tarantino/TextSample7_hu8f2ae577097e28cb24b40b2fead3709a_83587_750x750_fit_q75_h2_lanczos.webp" loading="lazy" alt="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words." width="750" height="663">
</a>
</div>
<div class="gallery-item gallery-item--medium">
<a data-fancybox="gallery-tarantino" href="https://zperzan.github.io/media/albums/tarantino/TextSample8.jpg" data-caption="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words.">
<img src="https://zperzan.github.io/media/albums/tarantino/TextSample8_hu8f2ae577097e28cb24b40b2fead3709a_80904_750x750_fit_q75_h2_lanczos.webp" loading="lazy" alt="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words." width="750" height="663">
</a>
</div>
<div class="gallery-item gallery-item--medium">
<a data-fancybox="gallery-tarantino" href="https://zperzan.github.io/media/albums/tarantino/TextSample9.jpg" data-caption="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words.">
<img src="https://zperzan.github.io/media/albums/tarantino/TextSample9_hu8f2ae577097e28cb24b40b2fead3709a_80850_750x750_fit_q75_h2_lanczos.webp" loading="lazy" alt="Sample text generated by the model. All formatting (line length, capitalization, line breaks, etc) are from the model itself. The only alteration was to blur out specific curse words." width="750" height="663">
</a>
</div>
</div>
<p>A few observations:</p>
<ul>
<li>The model is great at picking up on the general structure of a screenplay; characters exchange dialog and occasionally you get camera and scene instructions &ndash; &ldquo;CU of Mickey&rdquo; (ie, a close up shot of Mickey) and &ldquo;INT. - BARTHOUSE - DAY&rdquo; (ie, an interior shot at the &ldquo;barthouse&rdquo; during the day).</li>
<li>Scenes are an amalgam of characters from all of Tarantino&rsquo;s movies (&ldquo;THE BRIDE&rdquo; from <em>Kill Bill</em>, &ldquo;ORDELL&rdquo; from <em>Jackie Brown</em>, etc), but unsurprisingly it doesn&rsquo;t create any new character names.</li>
<li>It&rsquo;s also pretty good at word completion (short-term memory). It completes &ldquo;the burn on the side of his f&rdquo; with &ldquo;face&rdquo; and &ldquo;walking towards the hos&rdquo; with &ldquo;hostages&rdquo;.</li>
<li>I really like the &ldquo;MAX &hellip; INT. - BARTHOUSE - DAY &hellip; MAX (CONT&rsquo;D)&rdquo; sequence, though I think that was
coincidence more than anything. With an input sequence of 50 characters, the model could not have known that MAX was talking prior to the scene change.</li>
</ul>
<p>I didn&rsquo;t fully tune the model because I felt bad wasting cluster resources on a silly task, but it achieves 60% accuracy on the test set. That&rsquo;s pretty good considering the messy text and paucity of data (1.7M chars total). It trained relatively quickly as well (9 epochs with early stopping).</p>
<p>Update 2019: I would be curious to see how well one of the &ldquo;Sesame Street&rdquo; models &ndash; ELMo, ERNIE, BERT, XLNet, RoBERTa, Transfo-XL, GPT-2, etc &ndash; would perform on the same task.
Thomas Dehaene must have had the same thought regarding Hallmark movies, as he just posted <a href="https://towardsdatascience.com/an-nlp-view-on-holiday-movies-part-ii-text-generation-using-lstms-in-keras-36dc1ff8a6d2" target="_blank" rel="noopener">this tutorial</a> on his blog. He couldn&rsquo;t find any screenplays either and used subtitles instead.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I subsequently found <a href="https://twitter.com/keatonpatti/status/1072877290902745089?lang=en" target="_blank" rel="noopener">this post</a> from a comedian making the same point, though that script was clearly human-generated.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
</description>
</item>
<item>
<title>The spillover effect of drinking water violations</title>
<link>https://zperzan.github.io/pastresearch/bottled-water/</link>
<pubDate>Wed, 01 Mar 2017 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/pastresearch/bottled-water/</guid>
<description><p>The ongoing water crisis in Flint, Michigan, was &ndash; and still is &ndash; a major news story that exposed governmental oversight and brought the question of safe drinking water into the news. A switch in the municipal drinking water supply, combined with insufficient water treatment, exposed over 100,000 Flint residents to elevated levels of coliform bacteria, lead and trihalomethans (THMs). Reporting on the crisis made national headlines and caused many Americans to reconsider the safety of their tap water. Even though the crisis affected a small, working-class city, we can see that bottled water sales spiked around the same time period in other states all across the country.</p>
<p>We are examining consumer spending habits and public sentiment data to analyze how many Americans distrust their tap and rely on bottled water as an alternate drinking water supply. Recent research has shown that U.S. households who perceive the tap as unsafe spend $5.65 billion per year on alternate water supplies<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, despite the fact that bottled water is less regulated than tap water and isn&rsquo;t any safer on average.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Javidi, A., Pierce, G., 2018. U.S. Households’ Perception of Drinking Water as Unsafe and its Consequences: Examining Alternative Choices to the Tap. Water Resources Research, 54, pp. 6100-6113.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
</description>
</item>
<item>
<title>Studying paleoclimate through New England cave sediments</title>
<link>https://zperzan.github.io/pastresearch/weybridge-cave/</link>
<pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/pastresearch/weybridge-cave/</guid>
<description><p>As part of my undergraduate thesis at Middlebury College, I worked with Will Amidon and Jeff Munroe to search for potential paleoclimate records in New England caves. We found a sequence of sediments in Weybridge Cave, Vermont, and used luminescence and paleomagnetic technqiues to show that the sediments span from 35 to 70 ka. This is significantly older than any surface sediments in New England, which were all deposited after the last glacial maximum (~26 ka).<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> We combined the Weybridge Cave record with other sedimentary records from the region to calculate rates of Laurentide Ice Sheet advance during the last glaciation. Read the full details at:</p>
<p>Munroe, J., Perzan, Z., and Amidon, W., 2016. Cave sediments constrain the latest Pleistocene advance of the Laurentide ice sheet in the Champlain Valley, Vermont, USA. Journal of Quaternary Science, 31 (8), pp. 893-904.</p>
<p>During this research, I was lucky enough to be the first to use Middlebury&rsquo;s new luminescence geochronology lab and spent many hours toiling away in the dark room figuring out our brand new instrument. Given the potential for partial bleaching and the fact that most samples were at or near saturation, New England cave sediemnts probably weren&rsquo;t the easiest samples to start out with.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>As part of this project, I also collected a young stalactite and took it to the clean lab at the University of Quebec at Montreal for U-Th age dating. For his undergraduate thesis, Drew Gorin (another Middlebury geology student) then combined those ages with stable isotope measurements to construct a late Holocene precipitation and temperature record.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Estimates differ, but see Dyke et al. (2002), Mickelson and Colgan (2003) and Ridge (2004) for more info.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Counterintuitively, luminescence-based dating techniques are not well suited to cave sediments because of the high potential for partial bleaching; sediments deposited at the surface usually have direct exposure to sunlight, whereas sediments deposited in one chamber of a cave could be eroded and deposited in another chamber, without ever being exposed to light. In this way, luminescence ages on cave sediments represent the time they first entered the cave, not the time they were deposited in the sequence.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Gorin, Andrew L., 2016. Paleoclimate Reconstruction from a Weybridge Cave Speleothem, Vermont. Undergraduate Thesis, Middlebury College, Middlebury, VT, 72p.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
</description>
</item>
<item>
<title>Mapping lava tube caves with LiDAR</title>
<link>https://zperzan.github.io/projects/lidar-caves/</link>
<pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
<guid>https://zperzan.github.io/projects/lidar-caves/</guid>
<description><p>In 2015, I worked for the Bureau of Land Management in Shoshone, Idaho, as part of the Geological Society of America&rsquo;s GeoCorps program. Most of my time was spent driving around the desert, dodging rattlesnakes and exploring lava tube caves. In collaboration with researchers from Idaho State University, we used a tripod-mounted LiDAR system to map the interior of several caves.</p>
<figure id="figure-full-map-of-maze-cave-viewed-from-the-side-a-single-map-consists-of-several-20-50-different-individual-scans-from-the-lidar-unit-that-are-then-merged-together-during-post-processing">
<div class="d-flex justify-content-center">
<div class="w-100" ><img alt="Full map of Maze Cave, viewed from the side. A single map consists of several (20-50) different individual scans from the LiDAR unit that are then merged together during post-processing." srcset="
/projects/lidar-caves/MazeCave_Side_hu1b0a5dd10fb2a163a7328ad1ca1feb65_519024_09dac9c8a3a8d01d50ab1b68d6840175.webp 400w,
/projects/lidar-caves/MazeCave_Side_hu1b0a5dd10fb2a163a7328ad1ca1feb65_519024_b4c8feaa78695affce60e1853a706fc1.webp 760w,
/projects/lidar-caves/MazeCave_Side_hu1b0a5dd10fb2a163a7328ad1ca1feb65_519024_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://zperzan.github.io/projects/lidar-caves/MazeCave_Side_hu1b0a5dd10fb2a163a7328ad1ca1feb65_519024_09dac9c8a3a8d01d50ab1b68d6840175.webp"
width="760"
height="184"
loading="lazy" data-zoomable /></div>
</div><figcaption>
Full map of Maze Cave, viewed from the side. A single map consists of several (20-50) different individual scans from the LiDAR unit that are then merged together during post-processing.
</figcaption></figure>
<p>The BLM was interested in detailed cave maps for practical reasons &ndash; in case of a rescue, for example &ndash; as well as for basic research purposes. Precise cross-sections of lava tubes can be used to infer the velocity and viscocity of lava that once flowed through the caves, while a time series of LiDAR scans can be used to better understand cave wall deformation and cave collapse.</p>
</description>
</item>
</channel>
</rss>