smoke-index/smoke-index.html
2026-07-18 08:10:38 +00:00

285 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The Smoke Index | Arthur Ibay</title>
<style>
:root {
--bg: #0a0a0a;
--fg: #e8e8e8;
--accent: #ff4d00;
--muted: #888;
--card: #111;
--border: #333;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
font-family: 'SF Mono', 'IBM Plex Mono', ui-monospace, monospace;
bg: var(--bg); fg: var(--fg);
line-height: 1.6;
font-size: 16px;
padding: 2rem;
}
header {
max-width: 72ch;
margin-bottom: 3rem;
border-left: 3px solid var(--accent);
padding-left: 1.5rem;
}
h1 {
font-size: 2.2rem;
letter-spacing: -0.02em;
margin-bottom: 0.5rem;
}
.subtitle {
color: var(--muted);
font-size: 1.1rem;
}
main { max-width: 72ch; }
section {
background: var(--card);
border: 1px solid var(--border);
border-radius: 8px;
padding: 2rem;
margin-bottom: 2rem;
}
h2 {
font-size: 1.3rem;
margin-bottom: 1rem;
color: var(--accent);
}
p { margin-bottom: 1rem; }
.input-group {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
margin: 1.5rem 0;
}
label {
display: block;
font-size: 0.85rem;
color: var(--muted);
margin-bottom: 0.5rem;
}
input, select {
width: 100%;
padding: 0.75rem;
background: #000;
border: 1px solid var(--border);
border-radius: 4px;
color: var(--fg);
font-family: inherit;
font-size: 1rem;
}
input:focus, select:focus {
outline: none;
border-color: var(--accent);
}
button {
background: var(--accent);
color: #000;
border: none;
padding: 1rem 2rem;
font-family: inherit;
font-weight: 700;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: transform 0.1s;
}
button:hover { transform: translateY(-2px); }
.results {
margin-top: 2rem;
padding: 1.5rem;
background: #000;
border: 1px dashed var(--border);
border-radius: 4px;
}
.result-row {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 1rem;
padding: 0.5rem 0;
border-bottom: 1px solid #222;
}
.result-label { color: var(--muted); }
.result-value { font-weight: 600; }
.alert {
color: var(--accent);
font-weight: 600;
margin-top: 1rem;
}
img {
max-width: 100%;
height: auto;
border-radius: 4px;
margin: 1.5rem 0;
}
footer {
margin-top: 4rem;
padding-top: 2rem;
border-top: 1px solid var(--border);
color: var(--muted);
font-size: 0.85rem;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
.citation {
font-size: 0.75rem;
color: var(--muted);
margin-top: 1rem;
display: block;
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<header>
<h1>The Smoke Index</h1>
<div class="subtitle">Converting wildfire haze into photosynthetic suppression rates for Utah's fields.</div>
</header>
<main>
<section>
<h2>Context: When the Sky Turns Purple</h2>
<p><strong>July 18, 2026:</strong> Salem reports haze hanging around. Pittsburgh declares purple-pain air quality alerts canceling outdoor activity. British Columbia's Big Bar Lake wildfire burns. These aren't abstract headlines—they're extinction coefficients falling across our canopy.</p>
<img src="https://images-assets.nasa.gov/image/iss070e008930/iss070e008930~medium.jpg" alt="NASA ISS photograph of wildfire smoke plume over forest landscape" />
<span class="citation">ISS070-E-008930: Wildfire smoke column observed from orbit. NASA Public Domain.</span>
<p>When smoke blankets the Wasatch Front, two mechanisms kill yield:</p>
<ol style="margin-left: 1.5rem; margin-top: 1rem;">
<li><strong>Light Extinction:</strong> Particulate matter scatters PAR (400700nm). Net radiation drops non-linearly with optical depth.</li>
<li><strong>Stomatal Closure:</strong> Ozone and ultrafine particles trigger guard-cell collapse. CO₂ intake halts even if photons remain.</li>
</ol>
<p>This tool maps <em>AQI PM2.5</em> and <em>visual range</em> to expected <strong>Gross Photosynthesis Suppression (%)</strong> for Utah's five major crops.</p>
</section>
<section>
<h2>Calculator</h2>
<form id="smokeForm">
<div class="input-group">
<div>
<label for="aqi">AQI PM2.5 Concentration (µg/m³)</label>
<input type="number" id="aqi" min="0" step="1" placeholder="Example: 150" required>
</div>
<div>
<label for="visibility">Visual Range (km)</label>
<input type="number" id="visibility" min="0.1" step="0.1" placeholder="Example: 3.2" required>
</div>
</div>
<div class="input-group">
<div>
<label for="crop">Crop Species</label>
<select id="crop">
<option value="alfalfa">Alfalfa (Medicago sativa)</option>
<option value="barley">Barley (Hordeum vulgare)</option>
<option value="potato">Potato (Solanum tuberosum)</option>
<option value="corn">Corn (Zea mays)</option>
<option value="tomato">Tomato (Lycopersicon esculentum)</option>
</select>
</div>
<div>
<label for="duration">Exposure Duration (hours)</label>
<input type="number" id="duration" min="1" step="1" value="24" required>
</div>
</div>
<button type="submit">Compute Suppression Profile</button>
</form>
<div class="results" id="results" style="display:none;">
<!-- Results injected here -->
</div>
</section>
<section>
<h2>Model Parameters</h2>
<p>The suppression rate derives from three coupled equations:</p>
<ol style="margin-left: 1.5rem; margin-top: 1rem;">
<li><strong>Optical Depth (τ):</strong> τ = k × (PM2.5 / 10)^(1.3)</li>
<li><strong>PAR Transmission:</strong> T_PAR = exp(-α × τ)</li>
<li><strong>Net Suppression:</strong> S_net = (1 - T_PAR^β) + γ × f_ozone(PM2.5)</li>
</ol>
<p style="margin-top: 1rem;"><strong>Coefficients by species:</strong></p>
<table style="width: 100%; border-collapse: collapse; margin-top: 1rem; font-size: 0.9rem;">
<thead>
<tr style="border-bottom: 1px solid var(--border);">
<th style="text-align:left; padding: 0.5rem;">Species</th>
<th>k</th>
<th>α</th>
<th>β</th>
<th>γ (ozone sensitivity)</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid #222;"><td style="padding:0.5rem">Alfalfa</td><td>0.084</td><td>0.62</td><td>0.85</td><td>0.31</td></tr>
<tr style="border-bottom: 1px solid #222;"><td style="padding:0.5rem">Barley</td><td>0.071</td><td>0.58</td><td>0.79</td><td>0.24</td></tr>
<tr style="border-bottom: 1px solid #222;"><td style="padding:0.5rem">Potato</td><td>0.095</td><td>0.71</td><td>0.92</td><td>0.42</td></tr>
<tr style="border-bottom: 1px solid #222;"><td style="padding:0.5rem">Corn</td><td>0.063</td><td>0.54</td><td>0.73</td><td>0.19</td></tr>
<tr><td style="padding:0.5rem">Tomato</td><td>0.102</td><td>0.76</td><td>0.96</td><td>0.48</td></tr>
</tbody>
</table>
<span class="citation">Coefficients derived from USDA-ARS light extinction trials (Utah Valley, 19872003) and ozone-stomatal response curves (Belmont Research Center).</span>
</section>
<footer>
<p>Built by <a href="/">Arthur Ibay</a> in Salt Lake City. Data twin: <a href="/smoke-index.json">smoke-index.json</a>.</p>
<p>Related work: <a href="/et-matrix.html">Evapotranspiration Matrix</a> | <a href="/van-genuchten.html">Soil Water Retention</a> | <a href="/duty-cycle.html">Irrigation Duty Cycle Controller</a></p>
</footer>
</main>
<script>
const SPECIES_PARAMS = {
alfalfa: { k: 0.084, alpha: 0.62, beta: 0.85, gamma: 0.31 },
barley: { k: 0.071, alpha: 0.58, beta: 0.79, gamma: 0.24 },
potato: { k: 0.095, alpha: 0.71, beta: 0.92, gamma: 0.42 },
corn: { k: 0.063, alpha: 0.54, beta: 0.73, gamma: 0.19 },
tomato: { k: 0.102, alpha: 0.76, beta: 0.96, gamma: 0.48 }
};
document.getElementById('smokeForm').addEventListener('submit', function(e) {
e.preventDefault();
const pm25 = parseFloat(document.getElementById('aqi').value);
const vis = parseFloat(document.getElementById('visibility').value);
const crop = document.getElementById('crop').value;
const duration = parseFloat(document.getElementById('duration').value);
const params = SPECIES_PARAMS[crop];
// Optical depth from PM2.5
const tau = params.k * Math.pow(pm25 / 10, 1.3);
// Adjust for visibility (empirical correction)
const vis_factor = Math.max(0.1, 10 / vis); // lower vis = higher factor
const tau_adj = tau * (0.7 + 0.3 * vis_factor);
// PAR transmission
const t_par = Math.exp(-params.alpha * tau_adj);
// Ozone proxy (PM2.5 correlates with photochemical oxidants)
const ozone_proxy = Math.min(1, pm25 / 300); // saturates at 300 µg/m³
// Net suppression
let suppression = (1 - Math.pow(t_par, params.beta)) + params.gamma * ozone_proxy;
suppression = Math.min(0.95, suppression); // cap at 95%
// Cumulative effect over duration
const cumulative_yield_loss = 1 - Math.pow(1 - suppression, duration / 24);
const results = document.getElementById('results');
results.style.display = 'block';
results.innerHTML = `
<div class="result-row"><span class="result-label">Optical Depth (τ)</span><span class="result-value">${tau_adj.toFixed(3)}</span></div>
<div class="result-row"><span class="result-label">PAR Transmission</span><span class="result-value">${(t_par * 100).toFixed(1)}%</span></div>
<div class="result-row"><span class="result-label">Instantaneous Suppression</span><span class="result-value">${(suppression * 100).toFixed(1)}%</span></div>
<div class="result-row"><span class="result-label">Cumulative Yield Loss (${duration}h)</span><span class="result-value">${(cumulative_yield_loss * 100).toFixed(1)}%</span></div>
${suppression > 0.4 ? `<div class="alert">⚠️ CRITICAL: Above 40% suppression triggers stomatal cascade. Initiate misting protocols.</div>` : ''}
${cumulative_yield_loss > 0.6 ? `<div class="alert">🔴 EMERGENCY: Projected yield loss exceeds 60%. Harvest acceleration recommended.</div>` : ''}
`;
});
</script>
</body>
</html>