/// <summary> /// Creates a Gaussian wavepacket with given properties. /// </summary> public static WaveFunction CreateGaussianWavePacket( int gridSizeX, int gridSizeY, float latticeSpacing, float mass, PointF packetCenter, PointF packetWidth, PointF 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))); TdseUtils.Misc.ForLoop(0, gridSizeY, (y) => { float yf = 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 = 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> /// Multiplication operator. /// </summary> public static WaveFunction operator *(TdseUtils.Complex s, WaveFunction A) { int sx = A.GridSizeX; int sy = A.GridSizeY; int sx2 = 2 * sx; float sr = s.Re; float si = s.Im; WaveFunction result = new WaveFunction(sx, sy, A.LatticeSpacing); for (int y = 0; y < sy; y++) { float[] AdataY = A.m_data[y]; float[] RdataY = result.m_data[y]; for (int x = 0; x < sx; x++) { int x2 = 2 * x; int x2p = x2 + 1; float ar = AdataY[x2]; float ai = AdataY[x2p]; RdataY[x2] = sr * ar - si * ai; RdataY[x2p] = sr * ai + si * ar; } } return(result); }
/// <summary> /// Worker method. /// </summary> protected override void WorkerMethod() { // Precompute the potential everywhere on the grid float[][] V = PrecomputeV(0); // Create a Visscher wf from the input wf m_visscherWf = new VisscherWf(m_intialWf, V, m_particleMass, m_deltaT, m_multiThread); m_intialWf = null; // Allow m_initialWf to be garbage collected // Main loop m_currentTimeStepIndex = 0; while (m_currentTimeStepIndex < m_totalNumTimeSteps) { // Compute the potential at the current time, if necessary if (m_isTimeDependentV && (m_currentTimeStepIndex > 0)) { V = PrecomputeV(m_currentTimeStepIndex * m_deltaT); } // Evolve the wavefunction by one timestep EvolveByOneTimeStep(m_visscherWf, V); m_currentTimeStepIndex++; // Report progress to the caller if (m_currentTimeStepIndex % m_reportInterval == 0) { ReportProgress(); if (IsCancelled) { return; } } } }
/// <summary> /// Subtraction operator. /// </summary> public static WaveFunction operator -(WaveFunction A, WaveFunction B) { int sx = A.GridSizeX; int sy = A.GridSizeY; int sx2 = 2 * sx; if ((sx != B.GridSizeX) || (sy != B.GridSizeY) || Math.Abs(A.LatticeSpacing - B.LatticeSpacing) > 1.0e-5) { throw new ArgumentException("Incompatible WaveFunctions, in WaveFunction.operator+"); } WaveFunction result = new WaveFunction(sx, sy, A.LatticeSpacing); for (int y = 0; y < sy; y++) { float[] AdataY = A.m_data[y]; float[] BdataY = B.m_data[y]; float[] RdataY = result.m_data[y]; for (int nx = 0; nx < sx2; nx++) { RdataY[nx] = AdataY[nx] - BdataY[nx]; } } return(result); }
/// <summary> /// Evolves the wavefunction and saves keyframes to disk. /// </summary> private void CreateAnimationFrames() { // Get a fresh output directory m_outputDir = CreateOutputDir(); // Write the run parameters to a file string paramsFile = Path.Combine(m_outputDir, "Params.txt"); File.WriteAllText(paramsFile, m_params.ToString().Replace("\n", "\r\n")); // Create the initial wavefunction WaveFunction wf = WaveFunctionUtils.CreateGaussianWavePacket( m_params.GridSizeX, m_params.GridSizeY, m_params.LatticeSpacing, m_params.ParticleMass, m_params.InitialWavePacketCenter, m_params.InitialWavePacketSize, m_params.InitialWavePacketMomentum, m_params.MultiThread ); // Save the initial wf as Frame 0 wf.SaveToVtkFile(Path.Combine(m_outputDir, "Frame_0000.vtk"), m_params.SaveFormat); m_lastSavedFrame = 0; // Create an Evolver and run it in the background Evolver.VDelegate V = (x, y, t, m, sx, sy) => { return(m_VBuilder.V(x, y, t, m, sx, sy)); }; m_evolver = new Evolver(wf, m_params.TotalTime, m_params.TimeStep, V, false, m_params.ParticleMass, 1, m_params.DampingBorderWidth, m_params.DampingFactor, m_params.MultiThread); m_evolver.ProgressEvent += Evolver_ProgressEvent; m_evolver.CompletionEvent += Evolver_CompletionEvent; m_evolver.RunInBackground(); }
/// <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> /// Worker method. /// </summary> protected override void WorkerMethod() { string[] vtkFiles = Directory.GetFiles(m_inputDir, "*.vtk"); if ((vtkFiles == null) || (vtkFiles.Length == 0)) { return; } // Write the color parameters to a file string outputDir = CreateOutputDir(m_inputDir); string colorParamsFile = Path.Combine(outputDir, "ColorParams.txt"); File.WriteAllText(colorParamsFile, m_colorBuilder.GetLastSavedCode().Replace("\n", "\r\n")); string paramsFile = Path.Combine(m_inputDir, "Params.txt"); if (File.Exists(paramsFile)) { File.Copy(paramsFile, Path.Combine(outputDir, Path.GetFileName(paramsFile))); } WaveFunction.ColorDelegate colorFunc = (re, im, maxAmpl) => { return(m_colorBuilder.CalcColor(re, im, maxAmpl)); }; if (colorFunc(1, 1, 1) == Color.Empty) { colorFunc = null; } m_numFilesToProcess = vtkFiles.Length; int chunkSize = Environment.ProcessorCount; for (int iStart = 0; iStart < m_numFilesToProcess; iStart += chunkSize) { int iEnd = Math.Min(iStart + chunkSize, m_numFilesToProcess); Parallel.For(iStart, iEnd, i => { // Re-color one file string inputFile = vtkFiles[i]; WaveFunction wf = WaveFunction.ReadFromVtkFile(inputFile); string outFile = Path.Combine(outputDir, Path.GetFileName(inputFile)); wf.SaveToVtkFile(outFile, WaveFunction.WfSaveFormat.AMPLITUDE_AND_COLOR, colorFunc); }); // Report progress to the caller m_currentFileIndex = iEnd - 1; ReportProgress(); if (IsCancelled) { return; } } }
/// <summary> /// Resamples a wavefunction to a higher resolution. /// </summary> public static WaveFunction Upsample(WaveFunction inputWf, double factor) { float[][] modData = UpsampleBicubic(inputWf.Data, factor); WaveFunction outputWf = new WaveFunction(modData, (float)(inputWf.LatticeSpacing / factor)); // Match the normalization of the input wavefunction float normFactor = (float)Math.Sqrt(inputWf.NormSq() / outputWf.NormSq()); outputWf.ScaleBy(normFactor); return(outputWf); }
/// <summary> /// Constructor. /// </summary> public Evolver(WaveFunction initialWf, float totalTime, float timeStep, VDelegate V, bool isVTimeDependent, float particleMass, int progressReportInterval, int dampingBorderWidth = 0, float dampingFactor = 0.0f, bool multiThread = true) { m_intialWf = initialWf; m_totalTime = totalTime; m_deltaT = timeStep; m_totalNumTimeSteps = (int)Math.Round(totalTime / timeStep) + 1; m_potential = V; m_isTimeDependentV = isVTimeDependent; m_particleMass = particleMass; m_reportInterval = progressReportInterval; m_dampingBorderWidth = dampingBorderWidth; m_dampingFactor = dampingFactor; m_multiThread = multiThread; }
/// <summary> /// Worker method. /// </summary> protected override void WorkerMethod() { string[] vtkFiles = Directory.GetFiles(m_inputDir, "*.vtk"); if ((vtkFiles == null) || (vtkFiles.Length == 0)) { return; } m_outputDir = CreateOutputDir(m_inputDir); string paramsFile = Path.Combine(m_inputDir, "Params.txt"); if (File.Exists(paramsFile)) { File.Copy(paramsFile, Path.Combine(m_outputDir, Path.GetFileName(paramsFile))); } m_numFilesToProcess = vtkFiles.Length; int chunkSize = Environment.ProcessorCount; for (int iStart = 0; iStart < m_numFilesToProcess; iStart += chunkSize) { int iEnd = Math.Min(iStart + chunkSize, m_numFilesToProcess); Parallel.For(iStart, iEnd, i => { // Upsample one file, and save the result string inputFile = vtkFiles[i]; WaveFunction wf = WaveFunction.ReadFromVtkFile(inputFile); wf = Upsample(wf, m_upsampFactor); string outFile = Path.Combine(m_outputDir, Path.GetFileName(inputFile)); wf.SaveToVtkFile(outFile, WaveFunction.WfSaveFormat.REAL_AND_IMAG); }); // Report progress to the caller m_currentFileIndex = iEnd - 1; ReportProgress(); if (IsCancelled) { return; } } }
/// <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> /// Multiplication operator. /// </summary> public static WaveFunction operator *(double s, WaveFunction A) { int sx = A.GridSizeX; int sy = A.GridSizeY; int sx2 = 2 * sx; float fs = (float)s; WaveFunction result = new WaveFunction(sx, sy, A.LatticeSpacing); for (int y = 0; y < sy; y++) { float[] AdataY = A.m_data[y]; float[] RdataY = result.m_data[y]; for (int nx = 0; nx < sx2; nx++) { RdataY[nx] = AdataY[nx] * fs; } } return(result); }
/// <summary> /// Processes a single input file. /// </summary> private void ProcessFile(string inFile, string outFile) { unsafe { int sx = -1, sy = -1; string format = ""; float latticeSpacing = 0.0f; string nl = Environment.NewLine; using (BinaryReader br = new BinaryReader(File.Open(inFile, FileMode.Open))) { // Parse the header of the input file while (br.BaseStream.Position < br.BaseStream.Length) { string textLine = WaveFunction.ReadTextLine(br); if (textLine.StartsWith("Wavefunction2D")) { string[] comps = textLine.Split(null); format = comps[1]; latticeSpacing = Single.Parse(comps[3]); } else if (textLine.StartsWith("DIMENSIONS")) { string[] comps = textLine.Split(null); sx = Int32.Parse(comps[1]); sy = Int32.Parse(comps[2]); } else if (textLine.StartsWith("LOOKUP_TABLE default")) { break; } } // Bail out if the header was not what we expected if (string.IsNullOrEmpty(format) || (sx < 0) || (sy < 0)) { throw new ArgumentException("Invalid Wavefunction file, in Colorer.ProcessFile."); } if ((format != "AMPLITUDE_ONLY") && (format != "AMPLITUDE_AND_COLOR")) { throw new ArgumentException("Unsupported Wavefunction format, in Smoother.ProcessFile. " + "(" + format + ")"); } // Read the amplitude values byte[] bytePlane = br.ReadBytes(sx * sy * 4); float[][] amplitudes = TdseUtils.Misc.Allocate2DArray(sy, sx); float floatVal = 0.0f; byte *floatBytes0 = (byte *)(&floatVal); byte *floatBytes1 = floatBytes0 + 1; byte *floatBytes2 = floatBytes0 + 2; byte *floatBytes3 = floatBytes0 + 3; int n = 0; for (int y = 0; y < sy; y++) { float[] amplitudesY = amplitudes[y]; for (int x = 0; x < sx; x++) { *floatBytes3 = bytePlane[n]; *floatBytes2 = bytePlane[n + 1]; *floatBytes1 = bytePlane[n + 2]; *floatBytes0 = bytePlane[n + 3]; amplitudesY[x] = floatVal; n += 4; } } // Smooth the amplitudes float[][] smoothedAmplitudes = Smooth(amplitudes, m_smoothingFactor, true); // Create and open the output file using (FileStream fileStream = File.Create(outFile)) { using (BinaryWriter bw = new BinaryWriter(fileStream)) { // Write the output header bw.Write(Encoding.ASCII.GetBytes("# vtk DataFile Version 3.0" + nl)); bw.Write(Encoding.ASCII.GetBytes("Wavefunction2D " + format + " " + "spacing: " + latticeSpacing.ToString() + nl)); bw.Write(Encoding.ASCII.GetBytes("BINARY" + nl)); bw.Write(Encoding.ASCII.GetBytes("DATASET STRUCTURED_POINTS" + nl)); bw.Write(Encoding.ASCII.GetBytes("DIMENSIONS " + sx + " " + sy + " 1" + nl)); bw.Write(Encoding.ASCII.GetBytes("ORIGIN 0 0 0" + nl)); bw.Write(Encoding.ASCII.GetBytes("SPACING 1 1 1" + nl)); bw.Write(Encoding.ASCII.GetBytes("POINT_DATA " + sx * sy + nl)); // Write out the smoothed amplitudes bw.Write(Encoding.ASCII.GetBytes("SCALARS amplitude float" + nl)); bw.Write(Encoding.ASCII.GetBytes("LOOKUP_TABLE default" + nl)); n = 0; for (int y = 0; y < sy; y++) { float[] smY = smoothedAmplitudes[y]; for (int x = 0; x < sx; x++) { floatVal = smY[x]; bytePlane[n] = *floatBytes3; bytePlane[n + 1] = *floatBytes2; bytePlane[n + 2] = *floatBytes1; bytePlane[n + 3] = *floatBytes0; n += 4; } } bw.Write(bytePlane); // Copy the color data from input to output if (format == "AMPLITUDE_AND_COLOR") { WaveFunction.ReadTextLine(br); WaveFunction.ReadTextLine(br); bw.Write(Encoding.ASCII.GetBytes("SCALARS colors unsigned_char 3" + nl)); bw.Write(Encoding.ASCII.GetBytes("LOOKUP_TABLE default" + nl)); bw.Write(br.ReadBytes(sx * sy * 3)); } } } } } }
/// <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); }
/// <summary> /// Crops a wavefunction. /// </summary> public static WaveFunction Crop(WaveFunction wf, int xminCrop, int xmaxCrop, int yminCrop, int ymaxCrop) { float[][] modData = Crop(wf.Data, xminCrop, xmaxCrop, yminCrop, ymaxCrop); return(new WaveFunction(modData, wf.LatticeSpacing)); }