Skip to content

Commit 77f054d

Browse files
committed
Create posts about the harmonic oscillator
1 parent ebbf677 commit 77f054d

16 files changed

+4227
-0
lines changed
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
<meta charset="UTF-8">
2+
<!-- Include Plotly.js from CDN -->
3+
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
4+
<!-- Include Math.js library for mathematical computations -->
5+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/11.5.0/math.min.js"></script>
6+
<style>
7+
#container {
8+
width: 110%;
9+
margin: auto;
10+
padding: 20px;
11+
transform: translateX(-5%); /* This helps center the wider container */
12+
}
13+
#title {
14+
text-align: center;
15+
}
16+
#slider-container {
17+
width: 80%;
18+
margin: auto;
19+
padding: 20px;
20+
text-align: center;
21+
}
22+
#plot {
23+
width: 100%;
24+
height: 600px;
25+
}
26+
#n-display {
27+
font-weight: bold;
28+
}
29+
</style>
30+
31+
<div id="container">
32+
<div id="slider-container">
33+
<label for="n-slider">Energy Level (n): <span id="n-display">0</span></label>
34+
<input type="range" id="n-slider" min="0" max="100" value="0" step="1" style="width: 80%;">
35+
</div>
36+
<div id="plot"></div>
37+
</div>
38+
39+
<script>
40+
// Constants
41+
const N_POINTS = 1000;
42+
const RANGE_FACTOR = 1.2;
43+
44+
// Hermite Polynomial Function
45+
function hermite(n, x) {
46+
if (n === 0) {
47+
return x.map(() => 1.0);
48+
} else if (n === 1) {
49+
return x.map(xi => 2 * xi);
50+
} else {
51+
let h_prev = x.map(() => 1.0); // H_0
52+
let h_curr = x.map(xi => 2 * xi); // H_1
53+
let h_next = [];
54+
55+
for (let i = 2; i <= n; i++) {
56+
h_next = x.map((xi, idx) => 2 * xi * h_curr[idx] - 2 * (i - 1) * h_prev[idx]);
57+
h_prev = h_curr;
58+
h_curr = h_next;
59+
}
60+
return h_curr;
61+
}
62+
}
63+
64+
// Wavefunction ψₙ(x)
65+
function wavefunction(n, x) {
66+
// Normalization constant
67+
const normalization = 1.0 / (Math.sqrt(Math.pow(2, n) * math.factorial(n) * Math.sqrt(Math.PI)));
68+
const hermite_n = hermite(n, x);
69+
return x.map((xi, idx) => normalization * hermite_n[idx] * Math.exp(-xi * xi / 2));
70+
}
71+
72+
// Classical Position Distribution ρₙ(x)
73+
function classical_distribution(n, x) {
74+
const energy = n + 0.5;
75+
const result = x.map(() => 0);
76+
const mask = x.map(xi => xi * xi < 2 * energy);
77+
const denominator = x.map(xi => Math.PI * Math.sqrt(2 * energy - xi * xi));
78+
79+
return result.map((val, idx) => mask[idx] ? 1 / denominator[idx] : 0);
80+
}
81+
82+
// Get x-axis range
83+
function get_x_range(n) {
84+
const turning_point = Math.sqrt(2 * n + 1);
85+
const x_max = RANGE_FACTOR * turning_point;
86+
return [-x_max, x_max];
87+
}
88+
89+
// Get y-axis max value
90+
function get_y_max(probability) {
91+
return Math.max(...probability);
92+
}
93+
94+
// Update the plot
95+
function updatePlot(n) {
96+
const [xmin, xmax] = get_x_range(n);
97+
const x = Array.from({length: N_POINTS}, (_, i) => xmin + i * (xmax - xmin) / (N_POINTS - 1));
98+
99+
// Quantum probability
100+
const psi = wavefunction(n, x);
101+
const probability = psi.map(psi_i => psi_i * psi_i);
102+
103+
// Classical probability
104+
const classical_prob = classical_distribution(n, x);
105+
106+
// Y-axis range
107+
const y_max = get_y_max(probability);
108+
const y_range_max = y_max * RANGE_FACTOR;
109+
110+
// Classical turning point
111+
const turning_point = Math.sqrt(2 * n + 1);
112+
113+
const data = [
114+
{
115+
x: x,
116+
y: probability,
117+
type: 'scatter',
118+
name: 'Quantum |ψₙ(x)|²',
119+
line: {color: 'blue', width: 2}
120+
},
121+
{
122+
x: x,
123+
y: classical_prob,
124+
type: 'scatter',
125+
name: 'Classical ρₙ(x)',
126+
line: {color: 'red', width: 2, dash: 'dot'}
127+
}
128+
];
129+
130+
const layout = {
131+
title: `Quantum vs Classical Probability Distribution for n = ${n}`,
132+
xaxis: {
133+
title: 'Position (x)',
134+
range: [xmin, xmax],
135+
showgrid: true,
136+
gridwidth: 1,
137+
gridcolor: 'LightGray',
138+
zeroline: false,
139+
showticklabels: false // Hide x-axis numerical values
140+
},
141+
yaxis: {
142+
title: 'Probability Density',
143+
range: [0, y_range_max],
144+
showgrid: true,
145+
gridwidth: 1,
146+
gridcolor: 'LightGray'
147+
},
148+
showlegend: true,
149+
hovermode: 'x',
150+
plot_bgcolor: 'white',
151+
margin: {
152+
l: 50, // left margin
153+
r: 20, // right margin
154+
t: 40, // top margin
155+
b: 40 // bottom margin
156+
},
157+
shapes: [
158+
{
159+
type: 'line',
160+
x0: turning_point,
161+
y0: 0,
162+
x1: turning_point,
163+
y1: y_range_max,
164+
line: {
165+
color: 'gray',
166+
width: 1,
167+
dash: 'dash'
168+
}
169+
},
170+
{
171+
type: 'line',
172+
x0: -turning_point,
173+
y0: 0,
174+
x1: -turning_point,
175+
y1: y_range_max,
176+
line: {
177+
color: 'gray',
178+
width: 1,
179+
dash: 'dash'
180+
}
181+
}
182+
],
183+
annotations: [
184+
{
185+
x: turning_point,
186+
y: y_range_max * 0.95,
187+
xref: 'x',
188+
yref: 'y',
189+
text: 'Turning point',
190+
showarrow: false,
191+
xanchor: 'left',
192+
font: {
193+
color: 'gray'
194+
}
195+
},
196+
{
197+
x: -turning_point,
198+
y: y_range_max * 0.95,
199+
xref: 'x',
200+
yref: 'y',
201+
text: 'Turning point',
202+
showarrow: false,
203+
xanchor: 'right',
204+
font: {
205+
color: 'gray'
206+
}
207+
}
208+
]
209+
};
210+
211+
Plotly.newPlot('plot', data, layout);
212+
}
213+
214+
// Initialize the plot
215+
updatePlot(0);
216+
217+
// Event listener for slider
218+
document.getElementById('n-slider').addEventListener('input', function() {
219+
const n = parseInt(this.value);
220+
document.getElementById('n-display').innerText = n;
221+
updatePlot(n);
222+
});
223+
</script>

0 commit comments

Comments
 (0)