hampton-survival-suite/thermal-solver.html

295 lines
10 KiB
HTML
Raw Normal View History

2026-07-18 13:48:48 +02:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thermal Diffusion Solver | Cathy Mcmasters</title>
2026-07-20 16:39:59 +02:00
<meta property="og:type" content="website">
<meta property="og:title" content="Thermal Diffusion Solver | Cathy Mcmasters">
<meta property="og:description" content="Calculating safe burial depth for root cellars in Hampton clay-loam (Q16019)">
<meta property="og:image" content="https://images.pexels.com/photos/11924683/pexels-photo-11924683.jpeg?auto=compress&amp;cs=tinysrgb&amp;dpr=2&amp;h=650&amp;w=940">
<meta property="og:url" content="https://cathy-mcmasters.4ort.net/thermal-solver.html">
<meta name="twitter:card" content="summary_large_image">
<meta name="description" content="Calculating safe burial depth for root cellars in Hampton clay-loam (Q16019)">
2026-07-18 13:48:48 +02:00
<style>
:root {
--bg: #0f0f0f;
--surface: #1a1a1a;
--primary: #d4af37; /* Hampton Gold */
--text: #e0e0e0;
--muted: #808080;
--border: #333333;
}
body {
font-family: 'Georgia', serif;
background: var(--bg);
color: var(--text);
margin: 0;
padding: 2rem;
line-height: 1.6;
}
header {
border-bottom: 1px solid var(--primary);
padding-bottom: 1rem;
margin-bottom: 2rem;
}
h1 {
font-size: 2rem;
color: var(--primary);
letter-spacing: 0.05em;
margin: 0;
}
p.subtitle {
color: var(--muted);
font-style: italic;
margin-top: 0.5rem;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
max-width: 1200px;
margin: 0 auto;
}
.panel {
background: var(--surface);
border: 1px solid var(--border);
padding: 1.5rem;
border-radius: 4px;
}
label {
display: block;
margin-bottom: 0.5rem;
color: var(--primary);
font-weight: bold;
font-size: 0.9rem;
}
input[type="number"] {
width: 100%;
padding: 0.5rem;
background: #000;
border: 1px solid var(--border);
color: var(--text);
font-family: monospace;
margin-bottom: 1rem;
box-sizing: border-box;
}
button {
background: var(--primary);
color: #000;
border: none;
padding: 0.75rem 1.5rem;
font-weight: bold;
cursor: pointer;
transition: all 0.2s;
width: 100%;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(212, 175, 55, 0.2);
}
.result {
margin-top: 1rem;
padding: 1rem;
background: #000;
border-left: 3px solid var(--primary);
font-family: monospace;
white-space: pre-wrap;
}
canvas {
background: #000;
border: 1px solid var(--border);
width: 100%;
height: 300px;
margin-top: 1rem;
}
.citation {
font-size: 0.75rem;
color: var(--muted);
margin-top: 1rem;
border-top: 1px dashed var(--border);
padding-top: 0.5rem;
}
.strata-img {
width: 100%;
height: 200px;
object-fit: cover;
margin-top: 1rem;
border: 1px solid var(--primary);
opacity: 0.8;
}
@media (max-width: 768px) {
.grid { grid-template-columns: 1fr; }
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<header>
<h1>Thermal Diffusion Solver</h1>
<p class="subtitle">Calculating safe burial depth for root cellars in Hampton clay-loam (Q16019)</p>
</header>
<div class="grid">
<section class="panel">
<h3>Input Parameters</h3>
<form id="solver-form">
<label for="deltaT">Surface Temperature Swing (ΔT)</label>
<input type="number" id="deltaT" step="0.1" value="28.0" title="Annual difference between summer peak and winter trough">
<label for="conductivity">Thermal Conductivity (k)</label>
<input type="number" id="conductivity" step="0.01" value="1.40" title="Watts per meter-Kelvin">
<label for="density">Soil Density (ρ)</label>
<input type="number" id="density" step="10" value="1600" title="Kilograms per cubic meter">
<label for="specificHeat">Specific Heat Capacity (c)</label>
<input type="number" id="specificHeat" step="100" value="2000" title="Joules per kilogram-Kelvin">
<label for="threshold">Safety Threshold (°C)</label>
<input type="number" id="threshold" step="0.1" value="2.0" title="Maximum allowable temp fluctuation at storage depth">
<button type="submit">Compute Burial Profile</button>
</form>
<div class="citation">
Constants derived from:<br>
• Soil thermal properties: ASTM C518<br>
• Hampton climate normals: NOAA NCEI (1991-2020)<br>
• Diffusion equation: Fourier (1822)
</div>
<img class="strata-img" src="https://images.pexels.com/photos/11924683/pexels-photo-11924683.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Vertical cross-section of soil strata with exposed root systems, illustrating the interface between biological and geological layers">
</section>
<section class="panel">
<h3>Computation Results</h3>
<div id="output" class="result">Awaiting computation...</div>
<canvas id="gradientChart"></canvas>
</section>
</div>
<script>
/**
* Solves the 1D Heat Equation for periodic boundary conditions.
* Amplitude decay follows: A(z) = A_0 * exp(-z * sqrt(πω / k))
* where ω = angular frequency, k = thermal diffusivity (α)
*/
document.getElementById('solver-form').addEventListener('submit', function(e) {
e.preventDefault();
const deltaT = parseFloat(document.getElementById('deltaT').value);
const k_cond = parseFloat(document.getElementById('conductivity').value); // W/(m·K)
const rho = parseFloat(document.getElementById('density').value); // kg/m³
const c_spec = parseFloat(document.getElementById('specificHeat').value); // J/(kg·K)
const threshold = parseFloat(document.getElementById('threshold').value); // °C
// Calculate thermal diffusivity (alpha)
const alpha = k_cond / (rho * c_spec); // m²/s
// Angular frequency for annual cycle (seconds/year ≈ 31,557,600)
const omega = (2 * Math.PI) / 31557600;
// Decay constant
const decayConst = Math.sqrt(Math.PI * omega / alpha);
// Solve for depth where amplitude drops to threshold
// threshold = (deltaT/2) * exp(-depth * decayConst)
const initialAmplitude = deltaT / 2;
const targetRatio = threshold / initialAmplitude;
let requiredDepth = 0;
if (targetRatio > 0 && targetRatio < 1) {
requiredDepth = -Math.log(targetRatio) / decayConst;
}
// Generate profile points
const points = [];
for (let z = 0; z <= requiredDepth * 1.5; z += 0.1) {
const ampAtZ = initialAmplitude * Math.exp(-z * decayConst);
points.push({ depth: z, amplitude: ampAtZ });
}
// Render results
const outputDiv = document.getElementById('output');
outputDiv.innerHTML = `
<span style="color:#d4af37">MINIMUM BURIAL DEPTH:</span> ${requiredDepth.toFixed(2)} meters (${(requiredDepth * 3.28084).toFixed(2)} ft)
<span style="color:#d4af37">THERMAL DIFFUSIVITY (α):</span> ${(alpha * 1e6).toFixed(2)} mm²/s
<span style="color:#d4af37">DECAY CONSTANT:</span> ${decayConst.toFixed(4)} m⁻¹
<span style="color:#d4af37">SAFETY FACTOR:</span> ${((initialAmplitude / threshold)).toFixed(2)}x
`;
// Draw Chart
drawGradient(points, requiredDepth, threshold);
});
function drawGradient(data, criticalDepth, threshold) {
const canvas = document.getElementById('gradientChart');
const ctx = canvas.getContext('2d');
const w = canvas.width = canvas.offsetWidth;
const h = canvas.height = 300;
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, w, h);
// Axes
ctx.strokeStyle = '#333';
ctx.beginPath();
ctx.moveTo(40, 20); ctx.lineTo(40, h-40); // Y axis
ctx.lineTo(w-20, h-40); // X axis
ctx.stroke();
// Labels
ctx.fillStyle = '#808080';
ctx.font = '10px monospace';
ctx.fillText('DEPTH (m)', 10, h-20);
ctx.fillText('AMPLITUDE (°C)', 10, 20);
// Plot curve
ctx.strokeStyle = '#d4af37';
ctx.lineWidth = 2;
ctx.beginPath();
const xMax = data[data.length-1].depth;
const yMax = data[0].amplitude;
data.forEach(p => {
const x = 40 + (p.depth / xMax) * (w - 60);
const y = h - 40 - (p.amplitude / yMax) * (h - 80);
if (p.depth === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.stroke();
// Critical line
ctx.strokeStyle = '#ff4444';
ctx.setLineDash([5, 5]);
ctx.beginPath();
const critY = h - 40 - (threshold / yMax) * (h - 80);
ctx.moveTo(40, critY); ctx.lineTo(w-20, critY);
ctx.stroke();
ctx.setLineDash([]);
// Intersection marker
const intersectIndex = data.findIndex(p => p.amplitude <= threshold);
if (intersectIndex !== -1) {
const p = data[intersectIndex];
const ix = 40 + (p.depth / xMax) * (w - 60);
const iy = h - 40 - (p.amplitude / yMax) * (h - 80);
ctx.fillStyle = '#d4af37';
ctx.beginPath();
ctx.arc(ix, iy, 4, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = '#fff';
ctx.fillText(`${p.depth.toFixed(1)}m`, ix + 10, iy + 15);
}
}
</script>
</body>
</html>