Skip to content

Commit d734abd

Browse files
create and embed widgets for vrm, trm, and crm
1 parent 646d55b commit d734abd

7 files changed

Lines changed: 2288 additions & 9 deletions

File tree

book/chapters/chapter7.md

Lines changed: 100 additions & 9 deletions
Large diffs are not rendered by default.
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>CRM Acquisition: Grain Growth at Room Temperature</title>
7+
<script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script>
8+
<style>
9+
@import url('https://fonts.googleapis.com/css2?family=Source+Serif+4:ital,opsz,wght@0,8..60,300;0,8..60,400;0,8..60,600;1,8..60,400&family=JetBrains+Mono:wght@400&display=swap');
10+
11+
*{box-sizing:border-box;margin:0;padding:0}
12+
body{
13+
font-family:'Source Serif 4',Georgia,serif;
14+
background:#f8f7f4;
15+
color:#1e293b;
16+
padding:20px 20px 40px;
17+
min-height:100vh;
18+
}
19+
.container{max-width:620px;margin:0 auto}
20+
21+
.description{
22+
font-size:0.84rem;
23+
color:#475569;
24+
line-height:1.55;
25+
margin-bottom:14px;
26+
}
27+
.description b{color:#1e293b}
28+
.description .pill{
29+
font-family:'JetBrains Mono',monospace;
30+
font-size:0.8rem;
31+
background:#ece9e3;
32+
padding:1px 5px;border-radius:3px;
33+
}
34+
35+
.controls{
36+
display:flex;align-items:center;gap:12px;
37+
flex-wrap:wrap;
38+
margin-bottom:12px;
39+
padding:10px 14px;
40+
background:#fff;
41+
border:1px solid #d6d3cd;
42+
border-radius:8px;
43+
box-shadow:0 1px 3px rgba(0,0,0,0.04);
44+
}
45+
.controls label{font-size:0.9rem;font-weight:600;white-space:nowrap}
46+
.slider-wrap{flex:1;min-width:180px;display:flex;align-items:center;gap:10px}
47+
.slider-wrap input[type=range]{
48+
flex:1;height:6px;-webkit-appearance:none;appearance:none;
49+
background:#d6d3cd;border-radius:3px;outline:none;cursor:pointer;
50+
}
51+
.slider-wrap input[type=range]::-webkit-slider-thumb{
52+
-webkit-appearance:none;width:18px;height:18px;
53+
background:#b91c1c;border:2px solid #fff;border-radius:50%;
54+
box-shadow:0 1px 4px rgba(0,0,0,0.25);cursor:pointer;
55+
}
56+
.slider-wrap input[type=range]::-moz-range-thumb{
57+
width:18px;height:18px;
58+
background:#b91c1c;border:2px solid #fff;border-radius:50%;
59+
box-shadow:0 1px 4px rgba(0,0,0,0.25);cursor:pointer;
60+
}
61+
.time-val{
62+
font-family:'JetBrains Mono',monospace;
63+
font-size:1.05rem;font-weight:600;
64+
min-width:90px;text-align:right;
65+
color:#b91c1c;
66+
}
67+
.btn{
68+
font-family:inherit;font-size:0.82rem;font-weight:600;
69+
padding:5px 12px;border:1px solid #d6d3cd;border-radius:5px;
70+
background:#fff;color:#1e293b;cursor:pointer;
71+
transition:background 0.15s,border-color 0.15s;
72+
white-space:nowrap;
73+
}
74+
.btn:hover{background:#f1f0ec;border-color:#94a3b8}
75+
.btn.active{background:#b91c1c;color:#fff;border-color:#b91c1c}
76+
77+
.status{
78+
font-size:0.86rem;line-height:1.6;
79+
padding:7px 14px;
80+
margin-bottom:10px;
81+
background:#fff;
82+
border:1px solid #d6d3cd;
83+
border-radius:8px;
84+
}
85+
.status .lbl{font-weight:600}
86+
87+
#plot{
88+
background:#fff;
89+
border:1px solid #d6d3cd;
90+
border-radius:8px;
91+
box-shadow:0 1px 3px rgba(0,0,0,0.04);
92+
overflow:hidden;
93+
}
94+
</style>
95+
</head>
96+
<body>
97+
<div class="container">
98+
<div class="description">
99+
At fixed temperature, <b>hematite</b> grains precipitating from solution
100+
grow through their blocking volumes, acquiring a
101+
<b>chemical remanent magnetization</b> (CRM). The iso-&tau; contours
102+
are static; as the grain population (ellipse) grows upward in volume,
103+
grains crossing the &tau;&nbsp;=&nbsp;100&nbsp;s boundary transition from
104+
<b style="color:#0072B2">superparamagnetic</b> to
105+
<b style="color:#B8860B">blocked CRM</b> carriers.
106+
&nbsp;
107+
<span class="pill">Hematite</span> &middot;
108+
<span class="pill">T = 20 &deg;C</span> &middot;
109+
<span class="pill">&tau;&#8320; = 10&#8315;&#8313; s</span>
110+
</div>
111+
112+
<div class="controls">
113+
<label>Grain volume:</label>
114+
<div class="slider-wrap">
115+
<input type="range" id="tSlider" min="0" max="29" value="0" step="1">
116+
<div class="time-val" id="tVal">3 %</div>
117+
</div>
118+
<button class="btn" id="btnPlay">&#9654; Grow</button>
119+
<button class="btn" id="btnReset">Reset</button>
120+
</div>
121+
122+
<div class="status" id="status">
123+
<span class="lbl">Grain Volume:</span> 3 % of maximum
124+
</div>
125+
126+
<div id="plot"></div>
127+
</div>
128+
129+
<script>
130+
/* --- Physics constants --- */
131+
var kB = 1.380649e-23;
132+
var tau0 = 1e-9;
133+
var T_K = 293.15; /* fixed room temperature */
134+
135+
/* Hematite grain population (reference = fully grown) */
136+
var refCx = 0.6; /* kJ/m³ — center of K distribution */
137+
var refCy = 0.35; /* zm³ — center volume at full growth */
138+
var ellW = 0.8; /* kJ/m³ — spread in K */
139+
var ellH = 0.20; /* zm³ — spread in volume at full growth */
140+
141+
/* --- Growth steps: 30 steps, volume factor 0.03 to 1.0 --- */
142+
var nSteps = 30;
143+
var gFactors = [];
144+
for (var i = 0; i < nSteps; i++)
145+
gFactors[i] = 0.03 + 0.97 * i / (nSteps - 1);
146+
147+
/* --- K axis (kJ/m³) --- */
148+
var nK = 300;
149+
var K_kJ = new Array(nK);
150+
var K_J = new Array(nK);
151+
for (var i = 0; i < nK; i++) {
152+
K_kJ[i] = 0.01 + (5.0 - 0.01) * i / (nK - 1);
153+
K_J[i] = K_kJ[i] * 1000;
154+
}
155+
156+
/* --- Ellipse angular coords --- */
157+
var nE = 200;
158+
var ex = new Array(nE);
159+
var ey_base = new Array(nE); /* at full growth (g = 1) */
160+
for (var i = 0; i < nE; i++) {
161+
var th = 2 * Math.PI * i / (nE - 1);
162+
ex[i] = refCx + (ellW / 2) * Math.cos(th);
163+
ey_base[i] = refCy + (ellH / 2) * Math.sin(th);
164+
}
165+
166+
/* --- Reference tau values --- */
167+
var tauDefs = [
168+
{label: '100 s', val: 100, color: '#0072B2', w: 2.5},
169+
{label: '1 Myr', val: 1e6 * 3.15e7, color: '#E69F00', w: 2.5},
170+
{label: '4.5 Gyr', val: 4.5e9 * 3.15e7, color: '#D55E00', w: 2.5}
171+
];
172+
173+
function calcV(tau, kj) {
174+
return (kB * T_K * Math.log(tau / tau0)) / Math.max(kj, 1e-5) * 1e21;
175+
}
176+
177+
function interp(x_new, x_arr, y_arr) {
178+
var n = x_new.length, nArr = x_arr.length;
179+
var result = new Array(n);
180+
for (var i = 0; i < n; i++) {
181+
var xn = x_new[i];
182+
if (xn <= x_arr[0]) { result[i] = y_arr[0]; continue; }
183+
if (xn >= x_arr[nArr - 1]) { result[i] = y_arr[nArr - 1]; continue; }
184+
var lo = 0, hi = nArr - 1;
185+
while (hi - lo > 1) {
186+
var mid = (lo + hi) >> 1;
187+
if (x_arr[mid] <= xn) lo = mid; else hi = mid;
188+
}
189+
var frac = (xn - x_arr[lo]) / (x_arr[hi] - x_arr[lo]);
190+
result[i] = y_arr[lo] + frac * (y_arr[hi] - y_arr[lo]);
191+
}
192+
return result;
193+
}
194+
195+
/* --- Precompute static tau curves --- */
196+
var tauCurves = [];
197+
for (var t = 0; t < tauDefs.length; t++) {
198+
var curve = new Array(nK);
199+
for (var i = 0; i < nK; i++)
200+
curve[i] = Math.min(Math.max(calcV(tauDefs[t].val, K_J[i]), 0), 0.5);
201+
tauCurves[t] = curve;
202+
}
203+
204+
/* Precompute boundary (tau = 100 s) at ellipse x positions — constant */
205+
var bnd_at_ex = interp(ex, K_kJ, tauCurves[0]);
206+
207+
/* --- Initial state (smallest grains) --- */
208+
var g0 = gFactors[0];
209+
var initEy = ey_base.map(function(y) { return y * g0; });
210+
var initSsd = initEy.map(function(y, i) {
211+
return Math.min(Math.max(y, bnd_at_ex[i]), 0.6);
212+
});
213+
var initSp = initEy.map(function(y, i) {
214+
return Math.min(y, bnd_at_ex[i]);
215+
});
216+
217+
/* --- Traces ---
218+
0: SSD fill (gold) — dynamic (y only)
219+
1: SP fill (light blue) — dynamic (y only)
220+
2: Ellipse outline — dynamic (y only)
221+
3–5: tau curves — STATIC
222+
*/
223+
var ff = 'Source Serif 4, Georgia, serif';
224+
var gridC = '#e8e6e1', axC = '#334155';
225+
226+
var traces = [
227+
{x: ex, y: initSsd, fill: 'toself', mode: 'lines',
228+
line: {width: 0.5, color: 'gray'}, fillcolor: 'rgba(255,215,0,0.5)',
229+
hoverinfo: 'skip', showlegend: false},
230+
231+
{x: ex, y: initSp, fill: 'toself', mode: 'lines',
232+
line: {width: 0.5, color: 'gray'}, fillcolor: 'rgba(173,216,230,0.6)',
233+
hoverinfo: 'skip', showlegend: false},
234+
235+
{x: ex, y: initEy, mode: 'lines',
236+
line: {width: 1.5, color: 'gray'},
237+
hoverinfo: 'skip', showlegend: false}
238+
];
239+
240+
for (var t = 0; t < tauDefs.length; t++) {
241+
traces.push({
242+
x: K_kJ, y: tauCurves[t], mode: 'lines',
243+
line: {color: tauDefs[t].color, width: tauDefs[t].w},
244+
name: '\u03C4 = ' + tauDefs[t].label,
245+
showlegend: true
246+
});
247+
}
248+
249+
var layout = {
250+
width: 600, height: 390,
251+
margin: {l: 68, r: 80, t: 10, b: 50},
252+
paper_bgcolor: 'transparent',
253+
plot_bgcolor: '#fff',
254+
font: {family: ff, size: 12, color: '#1e293b'},
255+
256+
xaxis: {
257+
title: {text: 'Anisotropy Energy Density (kJ/m\u00B3)', font: {size: 13}},
258+
range: [0, 3],
259+
showgrid: true, gridcolor: gridC, gridwidth: 1,
260+
zeroline: false, linecolor: axC, linewidth: 1.5, mirror: true,
261+
ticks: 'inside', ticklen: 5,
262+
tickfont: {family: ff, size: 11}
263+
},
264+
yaxis: {
265+
title: {text: 'Grain Volume (zm\u00B3)', font: {size: 13}},
266+
range: [0, 0.48],
267+
showgrid: true, gridcolor: gridC, gridwidth: 1,
268+
zeroline: false, linecolor: axC, linewidth: 1.5, mirror: true,
269+
ticks: 'inside', ticklen: 5,
270+
tickfont: {family: ff, size: 11}
271+
},
272+
273+
legend: {
274+
bgcolor: 'rgba(255,255,255,0.92)',
275+
bordercolor: '#d6d3cd', borderwidth: 1,
276+
font: {size: 11},
277+
x: 0.98, y: 0.98, xanchor: 'right', yanchor: 'top',
278+
borderpad: 6
279+
},
280+
281+
annotations: [
282+
{x: 0.15, y: 0.04, text: 'Superparamagnetic<br>(growing)',
283+
showarrow: false, font: {size: 12, color: '#0072B2', family: ff},
284+
bgcolor: 'rgba(255,255,255,0.85)', borderpad: 2},
285+
{x: 1.8, y: 0.38, text: 'Blocked<br>(CRM acquired)',
286+
showarrow: false, font: {size: 12, color: '#B8860B', family: ff},
287+
bgcolor: 'rgba(255,255,255,0.85)', borderpad: 2}
288+
],
289+
290+
hovermode: 'closest'
291+
};
292+
293+
Plotly.newPlot('plot', traces, layout, {displayModeBar: false, responsive: true});
294+
295+
/* --- Interactivity --- */
296+
var slider = document.getElementById('tSlider');
297+
var tValEl = document.getElementById('tVal');
298+
var statusEl = document.getElementById('status');
299+
var btnPlay = document.getElementById('btnPlay');
300+
var btnReset = document.getElementById('btnReset');
301+
302+
function updateToIdx(idx) {
303+
var g = gFactors[idx];
304+
var ey_new = ey_base.map(function(y) { return y * g; });
305+
var ssd_y = ey_new.map(function(y, i) {
306+
return Math.min(Math.max(y, bnd_at_ex[i]), 0.6);
307+
});
308+
var sp_y = ey_new.map(function(y, i) {
309+
return Math.min(y, bnd_at_ex[i]);
310+
});
311+
312+
/* Only traces 0–2 update; tau curves (3–5) are static */
313+
Plotly.restyle('plot', {y: [ssd_y, sp_y, ey_new]}, [0, 1, 2]);
314+
315+
var pct = Math.round(g * 100);
316+
tValEl.textContent = pct + ' %';
317+
slider.value = idx;
318+
statusEl.innerHTML =
319+
'<span class="lbl">Grain Volume:</span> ' + pct + ' % of maximum';
320+
}
321+
322+
slider.addEventListener('input', function() {
323+
updateToIdx(parseInt(this.value));
324+
});
325+
326+
var playing = false, playTimer = null;
327+
328+
function stopPlay() {
329+
playing = false;
330+
clearInterval(playTimer);
331+
btnPlay.innerHTML = '&#9654; Grow';
332+
btnPlay.classList.remove('active');
333+
}
334+
335+
btnPlay.addEventListener('click', function() {
336+
if (playing) { stopPlay(); return; }
337+
playing = true;
338+
btnPlay.innerHTML = '&#9646;&#9646; Pause';
339+
btnPlay.classList.add('active');
340+
var idx = parseInt(slider.value);
341+
playTimer = setInterval(function() {
342+
idx++;
343+
if (idx >= nSteps) { stopPlay(); return; }
344+
updateToIdx(idx);
345+
}, 200);
346+
});
347+
348+
btnReset.addEventListener('click', function() {
349+
stopPlay();
350+
updateToIdx(0);
351+
});
352+
</script>
353+
</body>
354+
</html>

0 commit comments

Comments
 (0)