/// <summary> /// Constructor. /// </summary> public Evolver(RunParams parms, VDelegate V, string outputDir) { m_gridSizeX = parms.GridSpec.SizeX; m_gridSizeY = parms.GridSpec.SizeY; m_gridSizeZ = parms.GridSpec.SizeZ; m_latticeSpacing = parms.LatticeSpacing; m_totalTime = parms.TotalTime; m_deltaT = parms.TimeStep; m_totalNumTimeSteps = (int)Math.Round(parms.TotalTime / parms.TimeStep) + 1; m_currentTimeStepIndex = 0; m_mass1 = parms.Mass1; m_mass2 = parms.Mass2; m_totalMass = (parms.Mass1 + parms.Mass2); m_reducedMass = (parms.Mass1 * parms.Mass2) / m_totalMass; m_potential = V; m_initialMomentum1 = new Vec3(parms.InitialWavePacketMomentum1); m_initialMomentum2 = new Vec3(parms.InitialWavePacketMomentum2); m_initialPosition1 = new Vec3(parms.InitialWavePacketCenter1); m_initialPosition2 = new Vec3(parms.InitialWavePacketCenter2); m_sigmaRel = parms.InitialWavePacketSize * Math.Sqrt(m_totalMass / m_mass2); m_sigmaCm = parms.InitialWavePacketSize * Math.Sqrt(m_mass1 / m_totalMass); m_dampingBorderWidth = parms.DampingBorderWidth; m_dampingFactor = parms.DampingFactor; m_numFramesToSave = parms.NumFramesToSave; m_lastSavedFrame = -1; m_outputDir = outputDir; m_multiThread = parms.MultiThread; m_visscherWf = null; }
/// <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 Vec3 r0 = m_initialPosition1 - m_initialPosition2; Vec3 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; int sz = 2 * m_gridSizeZ - 1; WaveFunction initialWf = WaveFunctionUtils.CreateGaussianWavePacket( new GridSpec(sx, sy, sz), 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> /// Damps the wavefunction amplitude near the region boundary. /// </summary> private void ApplyDamping(VisscherWf wf) { int sx = wf.GridSpec.SizeX; int sy = wf.GridSpec.SizeY; int sz = wf.GridSpec.SizeZ; int d = m_dampingBorderWidth; float[] factors = new float[d]; for (int i = 0; i < d; i++) { factors[i] = (float)(1.0 - m_dampingFactor * m_deltaT * (1.0 - Math.Sin(((Math.PI / 2) * i) / d))); } // Front Z border for (int z = 0; z < d; z++) { float f = factors[z]; for (int y = 0; y < sy; y++) { float[] wfDataIPzy = wf.ImagPartP[z][y]; float[] wfDataIMzy = wf.ImagPartM[z][y]; float[] wfDataRzy = wf.RealPart[z][y]; for (int x = 0; x < sx; x++) { wfDataRzy[x] *= f; wfDataIPzy[x] *= f; wfDataIMzy[x] *= f; } } } // Back Z border for (int z = sz - d; z < sz; z++) { float f = factors[sz - 1 - z]; for (int y = 0; y < sy; y++) { float[] wfDataIPzy = wf.ImagPartP[z][y]; float[] wfDataIMzy = wf.ImagPartM[z][y]; float[] wfDataRzy = wf.RealPart[z][y]; for (int x = 0; x < sx; x++) { wfDataRzy[x] *= f; wfDataIPzy[x] *= f; wfDataIMzy[x] *= f; } } } // Y borders for (int z = 0; z < sz; z++) { for (int y = 0; y < d; y++) { float f = factors[y]; float[] wfDataIPzy = wf.ImagPartP[z][y]; float[] wfDataIMzy = wf.ImagPartM[z][y]; float[] wfDataRzy = wf.RealPart[z][y]; for (int x = 0; x < sx; x++) { wfDataRzy[x] *= f; wfDataIPzy[x] *= f; wfDataIMzy[x] *= f; } } for (int y = sy - d; y < sy; y++) { float f = factors[sy - 1 - y]; float[] wfDataIPzy = wf.ImagPartP[z][y]; float[] wfDataIMzy = wf.ImagPartM[z][y]; float[] wfDataRzy = wf.RealPart[z][y]; for (int x = 0; x < sx; x++) { wfDataRzy[x] *= f; wfDataIPzy[x] *= f; wfDataIMzy[x] *= f; } } } // X borders for (int z = 0; z < sz; z++) { for (int y = 0; y < sy; y++) { float[] wfDataIPzy = wf.ImagPartP[z][y]; float[] wfDataIMzy = wf.ImagPartM[z][y]; float[] wfDataRzy = wf.RealPart[z][y]; for (int x = 0; x < d; x++) { float f = factors[x]; wfDataRzy[x] *= f; wfDataIPzy[x] *= f; wfDataIMzy[x] *= f; } for (int x = sx - d; x < sx; x++) { float f = factors[sx - 1 - x]; wfDataRzy[x] *= f; wfDataIPzy[x] *= f; wfDataIMzy[x] *= f; } } } }
/// <summary> /// Evolves the wavefunction by a single time step /// </summary> private void EvolveByOneTimeStep(VisscherWf wf, float[][][] V) { GridSpec wfGrid = wf.GridSpec; int sx = wfGrid.SizeX; int sy = wfGrid.SizeY; int sz = wfGrid.SizeZ; int sxm1 = sx - 1; int sym1 = sy - 1; int szm1 = sz - 1; float keFactor = (1.0f / 12.0f) / (2 * m_reducedMass * wf.LatticeSpacing * wf.LatticeSpacing); // Compute the next real part in terms of the current imaginary part TdseUtils.Misc.ForLoop(0, sz, z => { int zp = (z < szm1) ? z + 1 : 0; int zpp = (zp < szm1) ? zp + 1 : 0; int zm = (z > 0) ? z - 1 : szm1; int zmm = (zm > 0) ? zm - 1 : szm1; for (int y = 0; y < 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[] V_z_y = V[z][y]; float[] wfR_z_y = wf.RealPart[z][y]; float[] wfI_z_y = wf.ImagPartP[z][y]; float[] wfI_z_ym = wf.ImagPartP[z][ym]; float[] wfI_z_yp = wf.ImagPartP[z][yp]; float[] wfI_z_ymm = wf.ImagPartP[z][ymm]; float[] wfI_z_ypp = wf.ImagPartP[z][ypp]; float[] wfI_zm_y = wf.ImagPartP[zm][y]; float[] wfI_zp_y = wf.ImagPartP[zp][y]; float[] wfI_zmm_y = wf.ImagPartP[zmm][y]; float[] wfI_zpp_y = wf.ImagPartP[zpp][y]; for (int x = 0; x < sx; x++) { int xp = (x < sxm1) ? x + 1 : 0; int xpp = (xp < sxm1) ? xp + 1 : 0; int xm = (x > 0) ? x - 1 : sxm1; int xmm = (xm > 0) ? xm - 1 : sxm1; // Discretization of the 2nd derivative float ke = keFactor * ( 90.0f * wfI_z_y[x] - 16.0f * (wfI_zm_y[x] + wfI_zp_y[x] + wfI_z_yp[x] + wfI_z_ym[x] + wfI_z_y[xm] + wfI_z_y[xp]) + (wfI_zmm_y[x] + wfI_zpp_y[x] + wfI_z_ypp[x] + wfI_z_ymm[x] + wfI_z_y[xmm] + wfI_z_y[xpp]) ); float pe = V_z_y[x] * wfI_z_y[x]; wfR_z_y[x] += m_deltaT * (ke + pe); } } }, m_multiThread); // Swap prev and post imaginary parts float[][][] temp = wf.ImagPartM; wf.ImagPartM = wf.ImagPartP; wf.ImagPartP = temp; // Compute the next imaginary part in terms of the current real part TdseUtils.Misc.ForLoop(0, sz, z => { int zp = (z < szm1) ? z + 1 : 0; int zpp = (zp < szm1) ? zp + 1 : 0; int zm = (z > 0) ? z - 1 : szm1; int zmm = (zm > 0) ? zm - 1 : szm1; for (int y = 0; y < 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[] V_z_y = V[z][y]; float[] wfIM_z_y = wf.ImagPartM[z][y]; float[] wfIP_z_y = wf.ImagPartP[z][y]; float[] wfR_z_y = wf.RealPart[z][y]; float[] wfR_z_ym = wf.RealPart[z][ym]; float[] wfR_z_yp = wf.RealPart[z][yp]; float[] wfR_z_ymm = wf.RealPart[z][ymm]; float[] wfR_z_ypp = wf.RealPart[z][ypp]; float[] wfR_zm_y = wf.RealPart[zm][y]; float[] wfR_zp_y = wf.RealPart[zp][y]; float[] wfR_zmm_y = wf.RealPart[zmm][y]; float[] wfR_zpp_y = wf.RealPart[zpp][y]; for (int x = 0; x < sx; x++) { int xp = (x < sxm1) ? x + 1 : 0; int xpp = (xp < sxm1) ? xp + 1 : 0; int xm = (x > 0) ? x - 1 : sxm1; int xmm = (xm > 0) ? xm - 1 : sxm1; // Discretization of the 2nd derivative float ke = keFactor * ( 90.0f * wfR_z_y[x] - 16.0f * (wfR_zm_y[x] + wfR_zp_y[x] + wfR_z_yp[x] + wfR_z_ym[x] + wfR_z_y[xm] + wfR_z_y[xp]) + (wfR_zmm_y[x] + wfR_zpp_y[x] + wfR_z_ypp[x] + wfR_z_ymm[x] + wfR_z_y[xmm] + wfR_z_y[xpp]) ); float pe = V_z_y[x] * wfR_z_y[x]; wfIP_z_y[x] = wfIM_z_y[x] - m_deltaT * (ke + pe); } } }, m_multiThread); // Optionally perform damping to suppress reflection and transmission at the borders. if ((m_dampingBorderWidth > 0) && (m_dampingFactor > 0.0f)) { ApplyDamping(wf); } }