// Euler method, can be used as solver.Step. func (s *BackwardEuler) Step() { util.AssertMsg(MaxErr > 0, "Backward euler solver requires MaxErr > 0") t0 := Time y := M.Buffer() y0 := cuda.Buffer(VECTOR, y.Size()) defer cuda.Recycle(y0) data.Copy(y0, y) dy0 := cuda.Buffer(VECTOR, y.Size()) defer cuda.Recycle(dy0) if s.dy1 == nil { s.dy1 = cuda.Buffer(VECTOR, y.Size()) } dy1 := s.dy1 Dt_si = FixDt dt := float32(Dt_si * GammaLL) util.AssertMsg(dt > 0, "Backward Euler solver requires fixed time step > 0") // Fist guess Time = t0 + 0.5*Dt_si // 0.5 dt makes it implicit midpoint method // with temperature, previous torque cannot be used as predictor if Temp.isZero() { cuda.Madd2(y, y0, dy1, 1, dt) // predictor euler step with previous torque M.normalize() } torqueFn(dy0) cuda.Madd2(y, y0, dy0, 1, dt) // y = y0 + dt * dy M.normalize() // One iteration torqueFn(dy1) cuda.Madd2(y, y0, dy1, 1, dt) // y = y0 + dt * dy1 M.normalize() Time = t0 + Dt_si err := cuda.MaxVecDiff(dy0, dy1) * float64(dt) // adjust next time step //if err < MaxErr || Dt_si <= MinDt || FixDt != 0 { // mindt check to avoid infinite loop // step OK NSteps++ setLastErr(err) setMaxTorque(dy1) //} else { // undo bad step // util.Assert(FixDt == 0) // Time = t0 // data.Copy(y, y0) // NUndone++ //} }
// Euler method, can be used as solver.Step. func (_ *Euler) Step() { y := M.Buffer() dy0 := cuda.Buffer(VECTOR, y.Size()) defer cuda.Recycle(dy0) torqueFn(dy0) setMaxTorque(dy0) // Adaptive time stepping: treat MaxErr as the maximum magnetization delta // (proportional to the error, but an overestimation for sure) var dt float32 if FixDt != 0 { Dt_si = FixDt dt = float32(Dt_si * GammaLL) } else { dt = float32(MaxErr / LastTorque) Dt_si = float64(dt) / GammaLL } util.AssertMsg(dt > 0, "Euler solver requires fixed time step > 0") setLastErr(float64(dt) * LastTorque) cuda.Madd2(y, y, dy0, 1, dt) // y = y + dt * dy M.normalize() Time += Dt_si NSteps++ }
// For a nanowire magnetized in-plane, with mx = mxLeft on the left end and // mx = mxRight on the right end (both -1 or +1), add a B field needed to compensate // for the surface charges on the left and right edges. // This will mimic an infinitely long wire. func RemoveLRSurfaceCharge(region int, mxLeft, mxRight float64) { SetBusy(true) defer SetBusy(false) util.Argument(mxLeft == 1 || mxLeft == -1) util.Argument(mxRight == 1 || mxRight == -1) bsat := Msat.GetRegion(region) * mag.Mu0 util.AssertMsg(bsat != 0, "RemoveSurfaceCharges: Msat is zero in region "+fmt.Sprint(region)) B_ext.Add(compensateLRSurfaceCharges(Mesh(), mxLeft, mxRight, bsat), nil) }
// Adds the current exchange field to dst func AddExchangeField(dst *data.Slice) { inter := !Dind.isZero() bulk := !Dbulk.isZero() switch { case !inter && !bulk: cuda.AddExchange(dst, M.Buffer(), lex2.Gpu(), regions.Gpu(), M.Mesh()) case inter && !bulk: // DMI kernel has space-dependent parameters, but // correct averaging between regions not yet clear nor tested, so disallow. util.AssertMsg(allowUnsafe || (Msat.IsUniform() && Aex.IsUniform() && Dind.IsUniform()), "DMI: Msat, Aex, Dex must be uniform") cuda.AddDMI(dst, M.Buffer(), lex2.Gpu(), din2.Gpu(), regions.Gpu(), M.Mesh()) // dmi+exchange case bulk && !inter: util.AssertMsg(allowUnsafe || (Msat.IsUniform() && Aex.IsUniform() && Dbulk.IsUniform()), "DMI: Msat, Aex, Dex must be uniform") cuda.AddDMIBulk(dst, M.Buffer(), lex2.Gpu(), dbulk2.Gpu(), regions.Gpu(), M.Mesh()) // dmi+exchange case inter && bulk: util.Fatal("Cannot have induced and interfacial DMI at the same time") } }
// adapt time step: dt *= corr, but limited to sensible values. func adaptDt(corr float64) { if FixDt != 0 { Dt_si = FixDt return } // corner case triggered by err = 0: just keep time step. // see test/regression017.mx3 if math.IsNaN(corr) { corr = 1 } util.AssertMsg(corr != 0, "Time step too small, check if parameters are sensible") corr *= Headroom if corr > 2 { corr = 2 } if corr < 1./2. { corr = 1. / 2. } Dt_si *= corr if MinDt != 0 && Dt_si < MinDt { Dt_si = MinDt } if MaxDt != 0 && Dt_si > MaxDt { Dt_si = MaxDt } if Dt_si == 0 { util.Fatal("time step too small") } // do not cross alarm time if Time < alarm && Time+Dt_si > alarm { Dt_si = alarm - Time } util.AssertMsg(Dt_si > 0, fmt.Sprint("Time step too small: ", Dt_si)) }
// Adds the current exchange field to dst func AddExchangeField(dst *data.Slice) { inter := !Dind.isZero() bulk := !Dbulk.isZero() switch { case !inter && !bulk: cuda.AddExchange(dst, M.Buffer(), lex2.Gpu(), regions.Gpu(), M.Mesh()) case inter && !bulk: cuda.AddDMI(dst, M.Buffer(), lex2.Gpu(), din2.Gpu(), regions.Gpu(), M.Mesh()) // dmi+exchange case bulk && !inter: util.AssertMsg(allowUnsafe || (Msat.IsUniform() && Aex.IsUniform() && Dbulk.IsUniform()), "DMI: Msat, Aex, Dex must be uniform") cuda.AddDMIBulk(dst, M.Buffer(), lex2.Gpu(), dbulk2.Gpu(), regions.Gpu(), M.Mesh()) // dmi+exchange case inter && bulk: util.Fatal("Cannot have induced and interfacial DMI at the same time") } }
// Adds the current spin transfer torque to dst func AddSTTorque(dst *data.Slice) { if J.isZero() { return } util.AssertMsg(!Pol.isZero(), "spin polarization should not be 0") jspin, rec := J.Slice() if rec { defer cuda.Recycle(jspin) } fl, rec := FixedLayer.Slice() if rec { defer cuda.Recycle(fl) } if !DisableZhangLiTorque { msat := Msat.MSlice() defer msat.Recycle() j := J.MSlice() defer j.Recycle() alpha := Alpha.MSlice() defer alpha.Recycle() xi := Xi.MSlice() defer xi.Recycle() pol := Pol.MSlice() defer pol.Recycle() cuda.AddZhangLiTorque(dst, M.Buffer(), msat, j, alpha, xi, pol, Mesh()) } if !DisableSlonczewskiTorque && !FixedLayer.isZero() { msat := Msat.MSlice() defer msat.Recycle() j := J.MSlice() defer j.Recycle() fixedP := FixedLayer.MSlice() defer fixedP.Recycle() alpha := Alpha.MSlice() defer alpha.Recycle() pol := Pol.MSlice() defer pol.Recycle() lambda := Lambda.MSlice() defer lambda.Recycle() epsPrime := EpsilonPrime.MSlice() defer epsPrime.Recycle() cuda.AddSlonczewskiTorque2(dst, M.Buffer(), msat, j, fixedP, alpha, pol, lambda, epsPrime, Mesh()) } }
// Adds the current spin transfer torque to dst func AddSTTorque(dst *data.Slice) { if J.isZero() { return } util.AssertMsg(!Pol.isZero(), "spin polarization should not be 0") jspin, rec := J.Slice() if rec { defer cuda.Recycle(jspin) } if !DisableZhangLiTorque { cuda.AddZhangLiTorque(dst, M.Buffer(), jspin, Bsat.gpuLUT1(), Alpha.gpuLUT1(), Xi.gpuLUT1(), Pol.gpuLUT1(), regions.Gpu(), Mesh()) } if !DisableSlonczewskiTorque && !FixedLayer.isZero() { cuda.AddSlonczewskiTorque(dst, M.Buffer(), jspin, FixedLayer.gpuLUT(), Msat.gpuLUT1(), Alpha.gpuLUT1(), Pol.gpuLUT1(), Lambda.gpuLUT1(), EpsilonPrime.gpuLUT1(), regions.Gpu(), Mesh()) } }
// Kernel for the vertical derivative of the force on an MFM tip due to mx, my, mz. // This is the 2nd derivative of the energy w.r.t. z. func MFMKernel(mesh *d.Mesh, lift, tipsize float64) (kernel [3]*d.Slice) { const TipCharge = 1 / Mu0 // tip charge const Δ = 1e-9 // tip oscillation, take 2nd derivative over this distance util.AssertMsg(lift > 0, "MFM tip crashed into sample, please lift the new one higher") { // Kernel mesh is 2x larger than input, instead in case of PBC pbc := mesh.PBC() sz := padSize(mesh.Size(), pbc) cs := mesh.CellSize() mesh = d.NewMesh(sz[X], sz[Y], sz[Z], cs[X], cs[Y], cs[Z], pbc[:]...) } // Shorthand size := mesh.Size() pbc := mesh.PBC() cellsize := mesh.CellSize() volume := cellsize[X] * cellsize[Y] * cellsize[Z] fmt.Println("calculating MFM kernel") // Sanity check { util.Assert(size[Z] >= 1 && size[Y] >= 2 && size[X] >= 2) util.Assert(cellsize[X] > 0 && cellsize[Y] > 0 && cellsize[Z] > 0) util.AssertMsg(size[X]%2 == 0 && size[Y]%2 == 0, "Even kernel size needed") if size[Z] > 1 { util.AssertMsg(size[Z]%2 == 0, "Even kernel size needed") } } // Allocate only upper diagonal part. The rest is symmetric due to reciprocity. var K [3][][][]float32 for i := 0; i < 3; i++ { kernel[i] = d.NewSlice(1, mesh.Size()) K[i] = kernel[i].Scalars() } r1, r2 := kernelRanges(size, pbc) progress, progmax := 0, (1+r2[Y]-r1[Y])*(1+r2[Z]-r1[Z]) for iz := r1[Z]; iz <= r2[Z]; iz++ { zw := wrap(iz, size[Z]) z := float64(iz) * cellsize[Z] for iy := r1[Y]; iy <= r2[Y]; iy++ { yw := wrap(iy, size[Y]) y := float64(iy) * cellsize[Y] progress++ util.Progress(progress, progmax, "Calculating MFM kernel") for ix := r1[X]; ix <= r2[X]; ix++ { x := float64(ix) * cellsize[X] xw := wrap(ix, size[X]) for s := 0; s < 3; s++ { // source index Ksxyz m := d.Vector{0, 0, 0} m[s] = 1 var E [3]float64 // 3 energies for 2nd derivative for i := -1; i <= 1; i++ { I := float64(i) R := d.Vector{-x, -y, z - (lift + (I * Δ))} r := R.Len() B := R.Mul(TipCharge / (4 * math.Pi * r * r * r)) R = d.Vector{-x, -y, z - (lift + tipsize + (I * Δ))} r = R.Len() B = B.Add(R.Mul(-TipCharge / (4 * math.Pi * r * r * r))) E[i+1] = B.Dot(m) * volume // i=-1 stored in E[0] } dFdz_tip := ((E[0] - E[1]) + (E[2] - E[1])) / (Δ * Δ) // dFz/dz = d2E/dz2 K[s][zw][yw][xw] += float32(dFdz_tip) // += needed in case of PBC } } } } return kernel }
func (r *reader) readSlice() (s *data.Slice, info data.Meta, err error) { r.err = nil // clear previous error, if any magic := r.readString() if r.err != nil { return nil, data.Meta{}, r.err } if magic != MAGIC { r.err = fmt.Errorf("dump: bad magic number:%v", magic) return nil, data.Meta{}, r.err } nComp := r.readInt() size := [3]int{} size[2] = r.readInt() // backwards compatible coordinates! size[1] = r.readInt() size[0] = r.readInt() cell := [3]float64{} cell[2] = r.readFloat64() cell[1] = r.readFloat64() cell[0] = r.readFloat64() info.CellSize = cell info.MeshUnit = r.readString() info.Time = r.readFloat64() _ = r.readString() // time unit s = data.NewSlice(nComp, size) info.Name = r.readString() info.Unit = r.readString() precission := r.readUint64() util.AssertMsg(precission == 4, "only single precission supported") if r.err != nil { return } host := s.Tensors() ncomp := s.NComp() for c := 0; c < ncomp; c++ { for iz := 0; iz < size[2]; iz++ { for iy := 0; iy < size[1]; iy++ { for ix := 0; ix < size[0]; ix++ { host[c][iz][iy][ix] = r.readFloat32() } } } } // Check CRC var mycrc uint64 // checksum by this reader if r.crc != nil { mycrc = r.crc.Sum64() } storedcrc := r.readUint64() // checksum from data stream. 0 means not set if r.err != nil { return nil, data.Meta{}, r.err } if r.crc != nil { r.crc.Reset() // reset for next frame } if r.crc != nil && storedcrc != 0 && mycrc != storedcrc { r.err = fmt.Errorf("dump CRC error: expected %16x, got %16x", storedcrc, mycrc) return nil, data.Meta{}, r.err } return s, info, nil }
func EvalTo(q Q, dst *data.Slice) { util.AssertMsg(q.NComp() == dst.NComp() && SizeOf(q) == dst.Size(), "size mismatch") q.EvalTo(dst) }
// Returns the saturation magnetization in Tesla. // Cannot be set. Set Msat and bsat() will automatically be updated. func bSat() float64 { util.AssertMsg(Msat.IsUniform(), "Remove surface charge: Msat must be uniform") return mag.Mu0 * Msat.GetRegion(0) }