Setting up a Potential
Let's refresh our memory with a look at the full Schrödinger equation:
Now that we have a particle in our simulation, we make things really interesting by giving it something to interact with. In quantum mechanics, this interaction is determined by the potential term, , in the Schrödinger equation. Indeed it is in the interactions with changing potentials that quantum mechanics displays behavior that differs strongly from classical physics.
Being able to experiment with, and visualize, these interactions and behaviors is a powerful tool for developing a strong intuitive understanding of quantum behaviors. The wave function's interaction with a varying potential also affords us another opportunity to examine the code's correctness and stability.
In order to visualize the potential and its effect on the wave function, we need to expand the SchrodingerResults to include the potential, the energy of the incoming particle, and also the real and imaginary components of the wave function. To differentiate it from the previous version we'll call it SchrodingerRenderer.
The big change for the SchrodingerRenderer is that the
components of the wave function can be negative. Instead of ranging from 0 to PsiMax, the y axis
now ranges from -PsiMax to PsiMax. At texture coordinate t
= 0, psi = -PsiMax,
and at t
= 1.0, psi = PsiMax. Of course, psi = 0 at t
= 0.5. We get
exactly this scale if we take the psi that pixel corresponds to as psi = 2*PsiMax*t - PsiMax
We make two additional modifications to the rendering code. We use a combination of smoothsteps
to soften the lines a bit, and when the values have changed by more than one vertical pixel we
fill in the curve with a short vertical line. We utilize the same pixelColor
method
for each of the
,
,
,
and the initial particle energy
curves.
vec4 pixelColor(vec4 color, float scale, float t, float halfDeltaY, float value, float previousValue)
{
vec4 pixel;
float scale2;
scale2 = 2.0*scale;
pixel = color*(smoothstep(scale2*(t-2.0*halfDeltaY)-scale, scale2*(t-halfDeltaY)-scale, value)
- smoothstep(scale2*(t+halfDeltaY)-scale, scale2*(t+2.0*halfDeltaY)-scale, value))
// Fill in discontinuities in the function
+ color*(step(previousValue+scale2*halfDeltaY, value)
*(step(previousValue, scale2*t-scale) - step(value, scale2*t-scale)))
+ color*(step(value, previousValue+scale2*halfDeltaY)
*(step(value, scale2*t-scale) - step(previousValue, scale2*t-scale)));
return pixel;
}
This code produces a readily understandable graph of the wave function as it propagates and interacts with the potential step.
This example shows our first unambiguously quantum behavior. At the potential step, the wave functions splits in two, and is half reflected, and half transmitted. This represents roughly equal probabilities of detecting the particle as being reflected or as being transmitted. It does not correspond to the particle splitting into two.
We also did something a bit different in setting up the potential. Rather than write a new shader to populate the potential array, we utilize a JavaScript function. This makes it much easier for later users and content authors to modify the potential to their exact needs.
/**
* Populate the potential array
*/
function stepPotential(potential)
{
var nelements;
// Remember, RGBA components for each texel
nelements = potential.length/4;
for(var i=0; i<nelements; ++i) {
if (i<nelements/3) {
potential[4*i] = 0.0;
}
else if (i<nelements/3+10) {
potential[4*i] = 100.0;
}
else {
potential[4*i] = 0.0;
}
}
return potential;
}
