285 lines
11 KiB
HTML
285 lines
11 KiB
HTML
<!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 (400–700nm). 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, 1987–2003) 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>
|