/// <summary> /// Computes the intergrated probability density. /// </summary> public float Norm() { GridSpec gridSpec = GridSpec; int sx = gridSpec.SizeX; int sy = gridSpec.SizeY; int sz = gridSpec.SizeZ; float[] norm = new float[sz]; TdseUtils.Misc.ForLoop(0, sz, z => { float xySum = 0.0f; for (int y = 0; y < sy; y++) { float[] dataZY = m_data[z][y]; for (int x = 0; x < sx; x++) { xySum += dataZY[x]; } } norm[z] = xySum; }, true); float normTot = 0.0f; for (int z = 0; z < sz; z++) { normTot += norm[z]; } normTot *= (m_latticeSpacing * m_latticeSpacing * m_latticeSpacing); return(normTot); }
/// <summary> /// Writes a set of densities to a file, in VTK format. /// </summary> public static void SaveToVtkFile(ProbabilityDensity[] probs, string fileSpec) { using (FileStream fileStream = File.Create(fileSpec)) { using (BinaryWriter bw = new BinaryWriter(fileStream)) { GridSpec gridSpec = probs[0].GridSpec; int sx = gridSpec.SizeX; int sy = gridSpec.SizeY; int sz = gridSpec.SizeZ; string nl = Environment.NewLine; // Write the header bw.Write(Encoding.ASCII.GetBytes("# vtk DataFile Version 3.0" + nl)); bw.Write(Encoding.ASCII.GetBytes("Probability3D2P " + "spacing: " + probs[0].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 + " " + sz + 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 * sz + nl)); for (int i = 0; i < probs.Length; i++) { probs[i].ToVtkStream(bw, "prob_" + i.ToString()); } } } }
/// <summary> /// Creates a Gaussian wavepacket with given properties. /// </summary> public static WaveFunction CreateGaussianWavePacket( GridSpec gridSpec, float latticeSpacing, bool originAtLatticeCenter, float mass, Vec3 packetCenter, Vec3 packetWidth, Vec3 avgMomentum, bool multiThread = true) { WaveFunction wf = new WaveFunction(gridSpec, latticeSpacing); int sx = gridSpec.SizeX; int sy = gridSpec.SizeY; int sz = gridSpec.SizeZ; float[][][] wfData = wf.Data; Complex I = Complex.I; float rootPi = (float)Math.Sqrt(Math.PI); float sigmaXSq = packetWidth.X * packetWidth.X; float sigmaYSq = packetWidth.Y * packetWidth.Y; float sigmaZSq = packetWidth.Z * packetWidth.Z; float norm = (float)Math.Sqrt(1.0 / (rootPi * packetWidth.X * rootPi * packetWidth.Y * rootPi * packetWidth.Z)); int halfGridSizeX = (sx - 1) / 2; int halfGridSizeY = (sy - 1) / 2; int halfGridSizeZ = (sz - 1) / 2; TdseUtils.Misc.ForLoop(0, sz, (z) => { float zf = (originAtLatticeCenter) ? (z - halfGridSizeZ) * latticeSpacing : (z * latticeSpacing); Complex expArgZ = I * zf * avgMomentum.Z - (zf - packetCenter.Z) * (zf - packetCenter.Z) / (2 * sigmaZSq); for (int y = 0; y < sy; y++) { float yf = (originAtLatticeCenter) ? (y - halfGridSizeY) * latticeSpacing : (y * latticeSpacing); Complex expArgZY = expArgZ + I * yf * avgMomentum.Y - (yf - packetCenter.Y) * (yf - packetCenter.Y) / (2 * sigmaYSq); float[] wfDataZY = wf.Data[z][y]; for (int x = 0; x < sx; x++) { float xf = (originAtLatticeCenter) ? (x - halfGridSizeX) * latticeSpacing : (x * latticeSpacing); Complex expArgZYX = expArgZY + I * xf * avgMomentum.X - (xf - packetCenter.X) * (xf - packetCenter.X) / (2 * sigmaXSq); Complex wfVal = norm * Complex.Exp(expArgZYX); wfDataZY[2 * x] = wfVal.Re; wfDataZY[2 * x + 1] = wfVal.Im; } } }, multiThread); wf.Normalize(); return(wf); }
/// <summary> /// Writes the density values to a stream, in vtk format. /// </summary> private void ToVtkStream(BinaryWriter bw, string name) { GridSpec gridSpec = GridSpec; int sx = gridSpec.SizeX; int sy = gridSpec.SizeY; int sz = gridSpec.SizeZ; string nl = Environment.NewLine; unsafe { // For performance, we keep a temporary float value with pointers to its bytes float floatVal = 0.0f; byte *floatBytes0 = (byte *)(&floatVal); byte *floatBytes1 = floatBytes0 + 1; byte *floatBytes2 = floatBytes0 + 2; byte *floatBytes3 = floatBytes0 + 3; bw.Write(Encoding.ASCII.GetBytes("SCALARS " + name + " float" + nl)); bw.Write(Encoding.ASCII.GetBytes("LOOKUP_TABLE default" + nl)); byte[] bytePlane = new byte[sx * sy * 4]; for (int z = 0; z < sz; z++) { int n = 0; for (int y = 0; y < sy; y++) { float[] dataZY = m_data[z][y]; for (int x = 0; x < sx; x++) { floatVal = dataZY[x]; bytePlane[n] = *floatBytes3; // Reverse the bytes, since VTK wants big-endian data bytePlane[n + 1] = *floatBytes2; bytePlane[n + 2] = *floatBytes1; bytePlane[n + 3] = *floatBytes0; n += 4; } } bw.Write(bytePlane); } } }
/// <summary> /// Constructor. /// </summary> public WaveFunction(GridSpec gridSpec, float latticeSpacing) { m_data = TdseUtils.Misc.Allocate3DArray(gridSpec.SizeZ, gridSpec.SizeY, 2 * gridSpec.SizeX); m_latticeSpacing = latticeSpacing; }
/// <summary> /// Constructor. /// </summary> public ProbabilityDensity(GridSpec gridSpec, float latticeSpacing) { m_data = TdseUtils.Misc.Allocate3DArray(gridSpec.SizeZ, gridSpec.SizeY, gridSpec.SizeX); m_latticeSpacing = latticeSpacing; }
/// <summary> /// Compares two GridSpecs by value. /// </summary> public bool ValueEquals(GridSpec that) { return((this.SizeX == that.SizeX) && (this.SizeY == that.SizeY) && (this.SizeZ == that.SizeZ)); }
/// <summary> /// Copy constructor. /// </summary> public GridSpec(GridSpec src) { SizeX = src.SizeX; SizeY = src.SizeY; SizeZ = src.SizeZ; }
/// <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); } }