/// <summary>
        /// Updates the integration method in case the solution did not converge.
        /// </summary>
        /// <param name="simulation">The time-based simulation.</param>
        /// <param name="newDelta">The next timestep to be probed.</param>
        public virtual void NonConvergence(TimeSimulation simulation, out double newDelta)
        {
            // Call event
            var args = new TruncateEvaluateEventArgs(simulation, MaxOrder);

            OnTruncateNonConvergence(args);

            // Set the new timestep
            newDelta = args.Delta;
        }
Exemple #2
0
        /// <summary>
        /// Updates the integration method in case the solution did not converge.
        /// </summary>
        /// <param name="simulation">The time-based simulation.</param>
        /// <param name="newDelta">The next timestep to be probed.</param>
        public virtual void NonConvergence(TimeSimulation simulation, out double newDelta)
        {
            // Call event
            var args = new TruncateEvaluateEventArgs(simulation, MaxOrder);

            OnTruncateNonConvergence(args);

            // Set the new timestep
            newDelta = args.Delta;

            // Copy the last accepted solution back into the time simulation
            // The simulator could have diverged to some crazy value, so we'll start again from the last known correct solution
            IntegrationStates[1].Solution.CopyTo(simulation.RealState.Solution);
        }
        /// <summary>
        /// Evaluates whether or not the current solution can be accepted.
        /// </summary>
        /// <param name="simulation">The time-based simulation.</param>
        /// <param name="newDelta">The next requested timestep in case the solution is not accepted.</param>
        /// <returns>
        ///   <c>true</c> if the time point is accepted; otherwise, <c>false</c>.
        /// </returns>
        public virtual bool Evaluate(TimeSimulation simulation, out double newDelta)
        {
            // Call event
            var args = new TruncateEvaluateEventArgs(simulation, MaxOrder)
            {
                Order = Order
            };

            OnTruncateEvaluate(args);

            // Update values
            Order    = args.Order;
            newDelta = args.Delta;
            return(args.Accepted);
        }
        /// <summary>
        /// Truncates the timestep based on the states.
        /// </summary>
        /// <param name="sender">The sender (integration method).</param>
        /// <param name="args">The <see cref="TruncateEvaluateEventArgs"/> instance containing the event data.</param>
        protected virtual void TruncateStates(object sender, TruncateEvaluateEventArgs args)
        {
            args.ThrowIfNull(nameof(args));

            // Don't truncate the first step
            if (BaseTime.Equals(0.0))
            {
                return;
            }

            // Truncate!
            var newDelta = args.Delta;

            foreach (var state in TruncatableStates)
            {
                newDelta = Math.Min(newDelta, state.Truncate());
            }

            if (newDelta > 0.9 * IntegrationStates[0].Delta)
            {
                if (Order < MaxOrder)
                {
                    // Try increasing the order
                    Order++;
                    args.Order = Order;

                    // Try truncation again
                    newDelta = args.Delta;
                    foreach (var state in TruncatableStates)
                    {
                        newDelta = Math.Min(newDelta, state.Truncate());
                    }

                    // Increasing the order doesn't make a significant difference
                    if (newDelta <= 1.05 * IntegrationStates[0].Delta)
                    {
                        Order--;
                        args.Order = Order;
                    }
                }
            }
            else
            {
                args.Accepted = false;
            }

            args.Delta = newDelta;
        }
Exemple #5
0
        /// <summary>
        /// Truncates the timestep using nodes.
        /// </summary>
        /// <param name="sender">The sender (integration method).</param>
        /// <param name="args">The <see cref="T:SpiceSharp.IntegrationMethods.TruncateEvaluateEventArgs" /> instance containing the event data.</param>
        protected override void TruncateNodes(object sender, TruncateEvaluateEventArgs args)
        {
            args.ThrowIfNull(nameof(args));

            // Get the state
            var state   = args.Simulation.RealState;
            var timetmp = double.PositiveInfinity;
            var nodes   = args.Simulation.Variables;

            var delsum = 0.0;

            for (var i = 0; i <= Order; i++)
            {
                delsum += IntegrationStates[i].Delta;
            }

            for (var i = 0; i < nodes.Count; i++)
            {
                var node  = nodes[i];
                var index = node.Index;
                var tol   = Math.Max(Math.Abs(state.Solution[index]), Math.Abs(Prediction[index])) * RelTol + AbsTol;
                var diff  = state.Solution[index] - Prediction[i];

                if (!diff.Equals(0.0))
                {
                    var tmp = tol * TrTol * delsum / (diff * IntegrationStates[0].Delta);
                    tmp = Math.Abs(tmp);
                    switch (Order)
                    {
                    case 0: break;

                    case 1:
                        tmp = Math.Sqrt(tmp);
                        break;

                    default:
                        tmp = Math.Exp(Math.Log(tmp) / (Order + 1));
                        break;
                    }

                    tmp    *= IntegrationStates[0].Delta;
                    timetmp = Math.Min(timetmp, tmp);
                }

                args.Delta = timetmp;
            }
        }
Exemple #6
0
        /// <summary>
        /// Truncates the timestep using nodes.
        /// </summary>
        /// <param name="sender">The sender (integration method).</param>
        /// <param name="args">The <see cref="TruncateEvaluateEventArgs" /> instance containing the event data.</param>
        /// <exception cref="SpiceSharp.CircuitException">Invalid order</exception>
        protected override void TruncateNodes(object sender, TruncateEvaluateEventArgs args)
        {
            // Get the state
            var    state = args.Simulation.RealState;
            double tol, diff, tmp;
            var    timetemp = double.PositiveInfinity;
            var    nodes    = args.Simulation.Variables;
            int    index;

            // In my opinion, the original Spice method is kind of bugged and can be much better...
            switch (Order)
            {
            case 1:
                for (var i = 0; i < nodes.Count; i++)
                {
                    var node = nodes[i];
                    if (node.UnknownType != VariableType.Voltage)
                    {
                        continue;
                    }
                    index = node.Index;

                    // Milne's estimate for the second-order derivative using a Forward Euler predictor and Backward Euler corrector
                    diff = state.Solution[index] - Prediction[index];

                    // Avoid division by zero
                    if (!diff.Equals(0.0))
                    {
                        tol      = Math.Max(Math.Abs(state.Solution[index]), Math.Abs(Prediction[index])) * LteRelTol + LteAbsTol;
                        tmp      = IntegrationStates[0].Delta * Math.Sqrt(Math.Abs(2.0 * TrTol * tol / diff));
                        timetemp = Math.Min(timetemp, tmp);
                    }
                }
                break;

            case 2:
                for (var i = 0; i < nodes.Count; i++)
                {
                    var node = nodes[i];
                    if (node.UnknownType != VariableType.Voltage)
                    {
                        continue;
                    }
                    index = node.Index;

                    // Milne's estimate for the third-order derivative using an Adams-Bashforth predictor and Trapezoidal corrector
                    diff = state.Solution[index] - Prediction[index];
                    var deriv = IntegrationStates[1].Delta / IntegrationStates[0].Delta;
                    deriv = diff * 4.0 / (1 + deriv * deriv);

                    // Avoid division by zero
                    if (!deriv.Equals(0.0))
                    {
                        tol      = Math.Max(Math.Abs(state.Solution[index]), Math.Abs(Prediction[index])) * LteRelTol + LteAbsTol;
                        tmp      = IntegrationStates[0].Delta * Math.Pow(Math.Abs(12.0 * TrTol * tol / deriv), 1.0 / 3.0);
                        timetemp = Math.Min(timetemp, tmp);
                    }
                }
                break;

            default:
                throw new CircuitException("Invalid order");
            }

            // Get the minimum timestep
            args.Delta = timetemp;
        }
 /// <summary>
 /// Truncates the timestep using nodes.
 /// </summary>
 /// <param name="sender">The sender (integration method).</param>
 /// <param name="args">The <see cref="TruncateEvaluateEventArgs"/> instance containing the event data.</param>
 protected abstract void TruncateNodes(object sender, TruncateEvaluateEventArgs args);
 /// <summary>
 /// Raises the <see cref="E:TruncateEvaluate" /> event.
 /// </summary>
 /// <param name="args">The <see cref="TruncateEvaluateEventArgs"/> instance containing the event data.</param>
 protected void OnTruncateEvaluate(TruncateEvaluateEventArgs args) => TruncateEvaluate?.Invoke(this, args);
 /// <summary>
 /// Raises the <see cref="E:TruncateNonConvergence" /> event.
 /// </summary>
 /// <param name="args">The <see cref="TruncateEvaluateEventArgs"/> instance containing the event data.</param>
 protected void OnTruncateNonConvergence(TruncateEvaluateEventArgs args) =>
 TruncateNonConvergence?.Invoke(this, args);