示例#1
0
        /// <summary>
        /// Execute the transient simulation
        /// </summary>
        /// <param name="ckt">The circuit</param>
        public override void Execute(Circuit ckt)
        {
            var state  = ckt.State;
            var rstate = state.Real;
            var config = CurrentConfig ?? throw new CircuitException("No configuration");
            var method = config.Method ?? throw new CircuitException("No integration method");

            double delta = Math.Min(FinalTime / 50.0, Step) / 10.0;

            // Setup breakpoints
            method.Breaks.Clear();
            method.Breaks.SetBreakpoint(0.0);
            method.Breaks.SetBreakpoint(FinalTime);
            method.Breaks.MinBreak = MaxStep * 5e-5;

            // Initialize before starting the simulation
            state.UseIC          = config.UseIC;
            state.UseDC          = true;
            state.UseSmallSignal = false;
            state.Domain         = CircuitState.DomainTypes.Time;
            state.Gmin           = config.Gmin;

            // Setup breakpoints
            method.Initialize();
            state.ReinitStates(method);

            // Call events for initializing the simulation
            Initialize(ckt);

            // Calculate the operating point
            Op(config, ckt, config.DcMaxIterations);
            ckt.Statistics.TimePoints++;
            for (int i = 0; i < method.DeltaOld.Length; i++)
            {
                method.DeltaOld[i] = MaxStep;
            }
            method.Delta     = delta;
            method.SaveDelta = FinalTime / 50.0;

            // Initialize the method
            ckt.Method = method;

            // Stop calculating a DC solution
            state.UseIC = false;
            state.UseDC = false;
            state.States[0].CopyTo(state.States[1]);

            // Start our statistics
            ckt.Statistics.TransientTime.Start();
            int startIters    = ckt.Statistics.NumIter;
            var startselapsed = ckt.Statistics.SolveTime.Elapsed;

            try
            {
nextTime:

                // Accept the current timepoint (CKTaccept())
                foreach (var c in ckt.Objects)
                {
                    c.Accept(ckt);
                }
                method.SaveSolution(rstate.Solution);
                // end of CKTaccept()

                // Check if current breakpoint is outdated; if so, clear
                method.UpdateBreakpoints();
                ckt.Statistics.Accepted++;

                // Export the current timepoint
                if (method.Time >= InitTime)
                {
                    Export(ckt);
                }

                // Detect the end of the simulation
                if (method.Time >= FinalTime)
                {
                    // Keep our statistics
                    ckt.Statistics.TransientTime.Stop();
                    ckt.Statistics.TranIter           += ckt.Statistics.NumIter - startIters;
                    ckt.Statistics.TransientSolveTime += ckt.Statistics.SolveTime.Elapsed - startselapsed;

                    // Finished!
                    Finalize(ckt);
                    return;
                }

                // Pause test - pausing not supported

                // resume:
                method.Delta = Math.Min(method.Delta, MaxStep);
                method.Resume();
                state.ShiftStates();

                // Calculate a new solution
                while (true)
                {
                    method.TryDelta();

                    // Compute coefficients and predict a solution and reset states to our previous solution
                    method.ComputeCoefficients(ckt);
                    method.Predict(ckt);

                    // Try to solve the new point
                    bool converged = Iterate(config, ckt, config.TranMaxIterations);
                    ckt.Statistics.TimePoints++;
                    if (method.SavedTime == 0.0)
                    {
                        state.States[1].CopyTo(state.States[2]);
                        state.States[1].CopyTo(state.States[3]);
                    }

                    // Spice copies the states the first time, we're not
                    // I believe this is because Spice treats the first timepoint after the OP as special (MODEINITTRAN)
                    // We don't treat it special (we just assume it started from a circuit in rest)

                    if (!converged)
                    {
                        // Failed to converge, let's try again with a smaller timestep
                        method.Rollback();
                        ckt.Statistics.Rejected++;
                        method.Delta /= 8.0;
                        method.CutOrder();

                        var data = new TimestepCutData(ckt, method.Delta / 8.0, TimestepCutData.TimestepCutReason.Convergence);
                        TimestepCut?.Invoke(this, data);
                    }
                    else
                    {
                        // Do not check the first time point
                        if (method.SavedTime == 0.0)
                        {
                            goto nextTime;
                        }

                        if (method.LteControl(ckt))
                        {
                            goto nextTime;
                        }
                        else
                        {
                            ckt.Statistics.Rejected++;
                            var data = new TimestepCutData(ckt, method.Delta, TimestepCutData.TimestepCutReason.Truncation);
                            TimestepCut?.Invoke(this, data);
                        }
                    }

                    if (method.Delta <= DeltaMin)
                    {
                        if (method.OldDelta > DeltaMin)
                        {
                            method.Delta = DeltaMin;
                        }
                        else
                        {
                            throw new CircuitException($"Timestep too small at t={method.SavedTime.ToString("g")}: {method.Delta.ToString("g")}");
                        }
                    }
                }
            }
            catch (CircuitException)
            {
                // Keep our statistics
                ckt.Statistics.TransientTime.Stop();
                ckt.Statistics.TranIter           += ckt.Statistics.NumIter - startIters;
                ckt.Statistics.TransientSolveTime += ckt.Statistics.SolveTime.Elapsed - startselapsed;
                throw;
            }
        }
示例#2
0
        /// <summary>
        /// Execute the transient simulation
        /// The timesteps are always too small... I can't find what it is, must have checked almost 10 times now.
        /// </summary>
        /// <param name="ckt">The circuit</param>
        public override void Execute(Circuit ckt)
        {
            var state  = ckt.State;
            var rstate = state.Real;
            var method = MyConfig.Method;

            // Initialize
            state.UseIC          = MyConfig.UseIC;
            state.UseDC          = true;
            state.UseSmallSignal = false;
            state.Domain         = CircuitState.DomainTypes.Time;

            // Setup breakpoints
            method.Breaks.SetBreakpoint(MyConfig.InitTime);
            method.Breaks.SetBreakpoint(MyConfig.FinalTime);
            if (method.Breaks.MinBreak == 0.0)
            {
                method.Breaks.MinBreak = 5e-5 * MyConfig.MaxStep;
            }

            // Initialize the method
            ckt.Method = method;
            method.Initialize();
            method.DeltaMin = MyConfig.DeltaMin;
            method.FillOldDeltas(MyConfig.MaxStep);

            // Calculate the operating point
            this.Op(ckt, MyConfig.DcMaxIterations);
            ckt.Statistics.TimePoints++;

            // Stop calculating a DC solution
            state.UseIC = false;
            state.UseDC = false;
            for (int i = 1; i < state.States.Length; i++)
            {
                state.States[0].CopyTo(state.States[i]);
            }

            // Start our statistics
            ckt.Statistics.TransientTime.Start();
            int startIters    = ckt.Statistics.NumIter;
            var startselapsed = ckt.Statistics.SolveTime.Elapsed;

            try
            {
                while (true)
                {
                    // Accept the current timepoint
                    foreach (var c in ckt.Components)
                    {
                        c.Accept(ckt);
                    }
                    method.SaveSolution(rstate.Solution);

                    method.UpdateBreakpoints();
                    ckt.Statistics.Accepted++;

                    // Export the current timepoint
                    if (method.Time >= MyConfig.InitTime)
                    {
                        Export(ckt);
                    }

                    // Detect the end of the simulation
                    if (method.Time >= MyConfig.FinalTime)
                    {
                        // Keep our statistics
                        ckt.Statistics.TransientTime.Stop();
                        ckt.Statistics.TranIter           += ckt.Statistics.NumIter - startIters;
                        ckt.Statistics.TransientSolveTime += ckt.Statistics.SolveTime.Elapsed - startselapsed;

                        // Finished!
                        break;
                    }

                    // Advance time
                    double delta = method.Time > 0.0 ? method.Delta : Math.Min(MyConfig.FinalTime / 50, MyConfig.Step) / 10.0;
                    method.Advance(Math.Min(delta, MyConfig.MaxStep));
                    state.ShiftStates();

                    // Calculate a new solution
                    while (true)
                    {
                        double olddelta = method.Delta;

                        // Check validity of the delta
                        if (double.IsNaN(olddelta))
                        {
                            throw new CircuitException("Invalid timestep");
                        }

                        // Compute coefficients and predict a solution and reset states to our previous solution
                        method.ComputeCoefficients(ckt);
                        method.Predict(ckt);
                        state.States[1].CopyTo(state.States[0]);

                        // Try to solve the new point
                        bool converged = this.Iterate(ckt, MyConfig.TranMaxIterations);
                        ckt.Statistics.TimePoints++;

                        // Spice copies the states the first time, we're not
                        // I believe this is because Spice treats the first timepoint after the OP as special (MODEINITTRAN)
                        // We don't treat it special (we just assume it started from a circuit in rest)

                        if (!converged)
                        {
                            // Failed to converge, let's try again with a smaller timestep
                            ckt.Statistics.Rejected++;
                            var data = new TimestepCutData(ckt, method.Delta / 8.0, TimestepCutData.TimestepCutReason.Convergence);
                            TimestepCut?.Invoke(this, data);
                            method.Retry(method.Delta / 8.0);
                        }
                        else
                        {
                            // Spice does not truncate the first timepoint (it deliberately makes it small)
                            // We just check the first timepoint just like any other, and assume the circuit
                            // has always been at that voltage.

                            // Calculate a new value based on the local truncation error
                            if (!method.NewDelta(ckt))
                            {
                                // Reject the timepoint if the calculated timestep shrinks too fast
                                ckt.Statistics.Rejected++;
                                var data = new TimestepCutData(ckt, method.Delta, TimestepCutData.TimestepCutReason.Truncation);
                                TimestepCut?.Invoke(this, data);
                            }
                            else
                            {
                                break;
                            }
                        }

                        // Stop simulation if timesteps are consistently too small
                        if (method.Delta <= MyConfig.DeltaMin)
                        {
                            if (olddelta <= MyConfig.DeltaMin)
                            {
                                throw new CircuitException($"Timestep too small: {method.Delta}");
                            }
                        }
                    }
                }
            }
            catch (CircuitException)
            {
                // Keep our statistics
                ckt.Statistics.TransientTime.Stop();
                ckt.Statistics.TranIter           += ckt.Statistics.NumIter - startIters;
                ckt.Statistics.TransientSolveTime += ckt.Statistics.SolveTime.Elapsed - startselapsed;
                throw;
            }
        }