func (s *HeunSolver) Step() { e := GetEngine() equation := e.equation // First update all inputs dt := engine.dt.Scalar() for i := range equation { Assert(equation[i].kind == EQN_PDE1) equation[i].input[0].Update() } // Then step all outputs // and invalidate them. // stage 0 for i := range equation { y := equation[i].output[0] dy := equation[i].input[0] dyMul := dy.multiplier checkUniform(dyMul) s.buffer[i] = Pool.Get(y.NComp(), y.Size3D()) s.buffer[i].CopyFromDevice(dy.Array()) // save for later gpu.Madd(y.Array(), y.Array(), dy.Array(), dt*dyMul[0]) // initial euler step y.Invalidate() } // Advance time e.time.SetScalar(e.time.Scalar() + dt) // update inputs again for i := range equation { Assert(equation[i].kind == EQN_PDE1) equation[i].input[0].Update() } // stage 1 for i := range equation { y := equation[i].output[0] dy := equation[i].input[0] dyMul := dy.multiplier h := float64(dt * dyMul[0]) gpu.MAdd2Async(y.Array(), dy.Array(), 0.5*h, s.buffer[i], -0.5*h, y.Array().Stream) // corrected step y.Array().Sync() Pool.Recycle(&s.buffer[i]) y.Invalidate() } e.step.SetScalar(e.step.Scalar() + 1) // advance time step }
// Take one time step func (s *RK12Solver) Step() { e := GetEngine() equation := e.equation // First update all inputs for i := range equation { Assert(equation[i].kind == EQN_PDE1) equation[i].input[0].Update() } // Then step all outputs // and invalidate them. // stage 0 t0 := e.time.Scalar() for i := range equation { y := equation[i].output[0] dy := equation[i].input[0] dyMul := dy.multiplier checkUniform(dyMul) s.dybuffer[i].CopyFromDevice(dy.Array()) // save for later s.y0buffer[i].CopyFromDevice(y.Array()) // save for later } const maxTry = 10 // undo at most this many bad steps const headRoom = 0.8 try := 0 for { // We need to update timestep if the step has failed dt := engine.dt.Scalar() // initial euler step for i := range equation { y := equation[i].output[0] dy := equation[i].input[0] dyMul := dy.multiplier if try > 0 { // restore previous initial conditions y.Array().CopyFromDevice(s.y0buffer[i]) dy.Array().CopyFromDevice(s.dybuffer[i]) } gpu.Madd(y.Array(), y.Array(), dy.Array(), dt*dyMul[0]) y.Invalidate() } // Advance time e.time.SetScalar(t0 + dt) // update inputs again for i := range equation { equation[i].input[0].Update() } // stage 1 badStep := false minFactor := 2.0 for i := range equation { y := equation[i].output[0] dy := equation[i].input[0] dyMul := dy.multiplier h := float64(dt * dyMul[0]) gpu.MAdd2Async(y.Array(), dy.Array(), 0.5*h, s.dybuffer[i], -0.5*h, y.Array().Stream) // corrected step y.Array().Sync() // error estimate stepDiff := s.diff[i].MaxDiff(dy.Array(), s.dybuffer[i]) * h err := float64(stepDiff) s.err[i].SetScalar(err) maxErr := s.maxErr[i].Scalar() if err > maxErr { s.badSteps.SetScalar(s.badSteps.Scalar() + 1) badStep = true } if (!badStep || try == maxTry-1) && err > s.peakErr[i].Scalar() { // peak error should be that of good step, unless last trial which will not be undone s.peakErr[i].SetScalar(err) } factor := 0.0 if !badStep { factor = math.Sqrt(maxErr / err) //maxErr / err } else { factor = math.Pow(maxErr/err, 1./3.) } factor *= headRoom // do not increase/cut too much // TODO: give user the control: if factor > 1.5 { factor = 1.5 } if factor < 0.1 { factor = 0.1 } if factor < minFactor { minFactor = factor } // take minimum time increase factor of all eqns. y.Invalidate() //if badStep{break} // do not waste time on other equations } // Set new time step but do not go beyond min/max bounds newDt := dt * minFactor if newDt < s.minDt.Scalar() { newDt = s.minDt.Scalar() } if newDt > s.maxDt.Scalar() { newDt = s.maxDt.Scalar() } e.dt.SetScalar(newDt) if !badStep || newDt == s.minDt.Scalar() { break } if try > maxTry { panic(Bug(fmt.Sprint("The solver cannot converge after ", maxTry, " badsteps"))) } try++ } // end try // advance time step e.step.SetScalar(e.step.Scalar() + 1) }