/// <summary> /// Creates a Gaussian wavepacket with given properties. /// </summary> public static WaveFunction CreateGaussianWavePacket( int gridSizeX, int gridSizeY, float latticeSpacing, bool originAtLatticeCenter, float mass, Vec2 packetCenter, Vec2 packetWidth, Vec2 avgMomentum, bool multiThread = true) { WaveFunction wf = new WaveFunction(gridSizeX, gridSizeY, latticeSpacing); Complex I = Complex.I; float rootPi = (float)Math.Sqrt(Math.PI); float sigmaXSq = packetWidth.X * packetWidth.X; float sigmaYSq = packetWidth.Y * packetWidth.Y; float norm = (float)Math.Sqrt((packetWidth.X / (rootPi * sigmaXSq)) * (packetWidth.Y / (rootPi * sigmaYSq))); int halfGridSizeX = (gridSizeX - 1) / 2; int halfGridSizeY = (gridSizeY - 1) / 2; TdseUtils.Misc.ForLoop(0, gridSizeY, (y) => { float yf = (originAtLatticeCenter) ? (y - halfGridSizeY) * latticeSpacing : (y * latticeSpacing); Complex expArgY = I * yf * avgMomentum.Y - (yf - packetCenter.Y) * (yf - packetCenter.Y) / (2 * sigmaYSq); float[] wfDataY = wf.Data[y]; for (int x = 0; x < gridSizeX; x++) { float xf = (originAtLatticeCenter) ? (x - halfGridSizeX) * latticeSpacing : (x * latticeSpacing); Complex expArgYX = expArgY + I * xf * avgMomentum.X - (xf - packetCenter.X) * (xf - packetCenter.X) / (2 * sigmaXSq); Complex wfVal = norm * Complex.Exp(expArgYX); wfDataY[2 * x] = wfVal.Re; wfDataY[2 * x + 1] = wfVal.Im; } }, multiThread); wf.Normalize(); return(wf); }
/// <summary> /// Constructor. Initializes a Visscher wavefunction from an ordinary wavefunction. /// </summary> public VisscherWf(WaveFunction inputWf, float[][] V, float mass, float dt, bool multiThread = true) { int sx = inputWf.GridSizeX; int sy = inputWf.GridSizeY; LatticeSpacing = inputWf.LatticeSpacing; // Allocate the arrays RealPart = TdseUtils.Misc.Allocate2DArray(sy, sx); ImagPartM = TdseUtils.Misc.Allocate2DArray(sy, sx); ImagPartP = TdseUtils.Misc.Allocate2DArray(sy, sx); // For the real part, just copy the values from the input wavefunction. for (int y = 0; y < sy; y++) { float[] realPartY = RealPart[y]; float[] inputWfY = inputWf.Data[y]; for (int x = 0; x < sx; x++) { realPartY[x] = inputWfY[2 * x]; } } // For the imaginary parts, we need to compute the time evolutions. // We use a power series expansion of the time-evolution operator, accurate to 2nd order in H*dt WaveFunction H_PsiIn = inputWf.ApplyH(V, mass, multiThread); WaveFunction H2_PsiIn = H_PsiIn.ApplyH(V, mass, multiThread); float halfDt = dt / 2; float eighthDt2 = dt * dt / 8; TdseUtils.Misc.ForLoop(0, sy, (y) => { float[] imPY = this.ImagPartP[y]; float[] imMY = this.ImagPartM[y]; float[] inputWfY = inputWf.Data[y]; float[] H_PsiInY = H_PsiIn.Data[y]; float[] H2_PsiInY = H2_PsiIn.Data[y]; for (int x = 0; x < sx; x++) { int x2 = 2 * x; int x2p1 = x2 + 1; float dt0Term = inputWfY[x2p1]; float dt1Term = halfDt * H_PsiInY[x2]; float dt2Term = eighthDt2 * H2_PsiInY[x2p1]; imPY[x] = dt0Term - dt1Term - dt2Term; // [1 - i*H*(dt/2) - (1/2)H^2*(dt/2)^2] * Psi imMY[x] = dt0Term + dt1Term - dt2Term; // [1 + i*H*(dt/2) - (1/2)H^2*(dt/2)^2] * Psi } }, multiThread); }
/// <summary> /// Converts a Visscher wavefunction to a regular wavefunction. /// </summary> public WaveFunction ToRegularWavefunction(bool multiThread = true) { int sx = GridSizeX; int sy = GridSizeY; WaveFunction result = new WaveFunction(sx, sy, LatticeSpacing); TdseUtils.Misc.ForLoop(0, sy, (y) => { float[] thisRy = RealPart[y]; float[] thisIMy = ImagPartM[y]; float[] thisIPy = ImagPartP[y]; float[] outWfy = result.Data[y]; for (int x = 0; x < sx; x++) { outWfy[2 * x] = thisRy[x]; // Take the square root of Im(t-dt/2) * I(t+dt/2), as this is the quantity that gives a conserved probability. float IM = thisIMy[x]; float IP = thisIPy[x]; float Iproduct = IM * IP; float Iout; if (Iproduct > 0.0f) { Iout = (float)Math.Sqrt(Iproduct); if (IM < 0.0f) { Iout = -Iout; } } else { Iout = 0.0f; } outWfy[2 * x + 1] = Iout; } }, multiThread); return(result); }
/// <summary> /// Worker method. /// </summary> protected override void WorkerMethod() { // Reset counters m_currentTimeStepIndex = 0; m_lastSavedFrame = -1; // Precompute the potential everywhere on the grid float[][] V = PrecomputeV(); // Create the initial relative wavefunction Vec2 r0 = m_initialPosition1 - m_initialPosition2; Vec2 p0 = (m_mass2 / m_totalMass) * m_initialMomentum1 - (m_mass1 / m_totalMass) * m_initialMomentum2; int sx = 2 * m_gridSizeX - 1; // The range of r = r1-r2 is twice the range of r1, r2 int sy = 2 * m_gridSizeY - 1; WaveFunction initialWf = WaveFunctionUtils.CreateGaussianWavePacket( sx, sy, m_latticeSpacing, true, m_reducedMass, r0, m_sigmaRel, p0, m_multiThread ); m_visscherWf = new VisscherWf(initialWf, V, m_reducedMass, m_deltaT, m_multiThread); initialWf = null; // Allow initialWf to be garbage collected TimeStepCompleted(); if (IsCancelled) { return; } // Main loop: Evolve the relative wavefunction while (m_currentTimeStepIndex < m_totalNumTimeSteps) { // Evolve the wavefunction by one timestep EvolveByOneTimeStep(m_visscherWf, V); m_currentTimeStepIndex++; // Report progress to the client TimeStepCompleted(); if (IsCancelled) { return; } } }
/// <summary> /// Computes the result of applying a given Hamiltonian operator to this wavefunction. /// </summary> public WaveFunction ApplyH(float[][] V, float mass, bool multiThread = true) { // Initialize locals int sx = GridSizeX; int sy = GridSizeY; int sxm1 = sx - 1; int sym1 = sy - 1; int sxm2 = sx - 2; int sym2 = sy - 2; int sx2 = 2 * sx; int sx2m2 = sx2 - 2; float keFactor = 1.0f / (2 * mass * m_latticeSpacing * m_latticeSpacing); float alpha = 5.0f; float beta = -4.0f / 3.0f; float delta = 1.0f / 12.0f; WaveFunction outWf = new WaveFunction(sx, sy, m_latticeSpacing); // Compute H * Wf TdseUtils.Misc.ForLoop(0, sy, (y) => { int yp = (y < sym1) ? y + 1 : 0; int ypp = (yp < sym1) ? yp + 1 : 0; int ym = (y > 0) ? y - 1 : sym1; int ymm = (ym > 0) ? ym - 1 : sym1; float[] inWf_y = m_data[y]; float[] inWf_ym = m_data[ym]; float[] inWf_yp = m_data[yp]; float[] inWf_ymm = m_data[ymm]; float[] inWf_ypp = m_data[ypp]; float[] outWf_y = outWf.m_data[y]; float[] V_y = V[y]; for (int rx = 0; rx < sx2; rx += 2) { int rxp = (rx < sx2m2) ? rx + 2 : 0; int rxpp = (rxp < sx2m2) ? rxp + 2 : 0; int rxm = (rx > 0) ? rx - 2 : sx2m2; int rxmm = (rxm > 0) ? rxm - 2 : sx2m2; int x = rx / 2; // Kinetic energy terms. float kR = keFactor * ( alpha * inWf_y[rx] + beta * (inWf_y[rxm] + inWf_y[rxp] + inWf_ym[rx] + inWf_yp[rx]) + delta * (inWf_y[rxmm] + inWf_y[rxpp] + inWf_ymm[rx] + inWf_ypp[rx]) ); int ix = rx + 1; float kI = keFactor * ( alpha * inWf_y[ix] + beta * (inWf_y[rxm + 1] + inWf_y[rxp + 1] + inWf_ym[ix] + inWf_yp[ix]) + delta * (inWf_y[rxmm + 1] + inWf_y[rxpp + 1] + inWf_ymm[ix] + inWf_ypp[ix]) ); // Potential energy terms float vR = V_y[x] * inWf_y[rx]; float vI = V_y[x] * inWf_y[ix]; outWf_y[rx] = kR + vR; outWf_y[ix] = kI + vI; } }, multiThread); return(outWf); }