/// <summary> /// Accepts a solution at the current timepoint. /// </summary> public virtual void Accept() { // Store the accepted solution State.Solution.CopyTo(States.Value.Solution); // When just starting out, we want to copy all the states to the previous states. if (BaseTime.Equals(0.0)) { foreach (var istate in States) { istate.Delta = Parameters.MaxStep; States.Value.State.CopyTo(istate.State); } } // Accept all the registered states foreach (var state in RegisteredStates) { state.Accept(); } // Clear the breakpoints while (Time > Breakpoints.First) { Breakpoints.ClearBreakpoint(); } Break = false; }
/// <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> /// <exception cref="SpiceSharp.CircuitException">Timestep {0:e} is too small at time {1:e}".FormatString(newDelta, BaseTime)</exception> public override bool Evaluate(TimeSimulation simulation, out double newDelta) { // Spice 3f5 ignores the first timestep if (BaseTime.Equals(0.0)) { newDelta = IntegrationStates[0].Delta; return(true); } var result = base.Evaluate(simulation, out newDelta); // Limit the expansion of the timestep newDelta = Math.Min(Expansion * IntegrationStates[0].Delta, newDelta); // Limit the maximum timestep if (newDelta > MaxStep) { newDelta = MaxStep; } // If the timestep is consistently made smaller than the minimum timestep, throw an exception if (newDelta <= MinStep) { // If we already tried if (_oldDelta <= MinStep) { throw new CircuitException("Timestep {0:e} is too small at time {1:e}".FormatString(newDelta, BaseTime)); } newDelta = MinStep; } return(result); }
/// <summary> /// Continues the simulation. /// </summary> /// <param name="simulation">The time-based simulation</param> /// <param name="delta">The initial probing timestep.</param> public override void Continue(TimeSimulation simulation, ref double delta) { // Modify the timestep delta = Math.Min(delta, MaxStep); // Handle breakpoints if (Time.Equals(Breakpoints.First) || Breakpoints.First - Time <= MinStep) { // Cut integration order Order = 1; // Limit the next timestep var mt = Math.Min(_saveDelta, Breakpoints.Delta); delta = Math.Min(delta, 0.1 * mt); // Spice will divide the first timestep by 10 if (BaseTime.Equals(0.0)) { delta /= 10.0; } // Don't go below MinStep without reason delta = Math.Max(delta, 2.0 * MinStep); } else if (Time + delta >= Breakpoints.First) { Break = true; _saveDelta = delta; delta = Breakpoints.First - Time; } base.Continue(simulation, ref delta); }
/// <inheritdoc/> public void Accept() { if (BaseTime.Equals(0.0)) { foreach (var state in _states) { _states.Value.CopyTo(state); } } foreach (var state in _registeredStates) { state.Accept(); } }
/// <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; }
/// <inheritdoc/> public virtual void Prepare() { var delta = Math.Min(Delta, Parameters.MaxStep); // Breakpoints if (Time.Equals(Breakpoints.First) || Breakpoints.First - Time <= Parameters.MinStep) { // Cut integration order Order = 1; // Limit the next timestep var mt = Math.Min(_saveDelta, Breakpoints.Delta); delta = Math.Min(delta, 0.1 * mt); // Spice will divide the first timestep by 10 if (BaseTime.Equals(0.0)) { delta /= 10.0; } // Don't go below MinStep without reason delta = Math.Max(delta, 2.0 * Parameters.MinStep); } else if (Time + delta >= Breakpoints.First) { Break = true; _saveDelta = delta; delta = Breakpoints.First - Time; } // Start States.Accept(); BaseTime = Time; States.Value.Delta = delta; Delta = delta; }
/// <inheritdoc/> /// <exception cref="TimestepTooSmallException">Thrown when the timestep is too small.</exception> public virtual bool Evaluate(double maxTimestep) { double newDelta; maxTimestep.GreaterThan(nameof(maxTimestep), 0); // Ignore checks on the first timepoint if (BaseTime.Equals(0.0)) { if (maxTimestep < States.Value.Delta) { Delta = maxTimestep; } else { Delta = States.Value.Delta; } return(true); } else { newDelta = double.PositiveInfinity; // Truncate the timestep foreach (var truncatable in TruncatableStates) { newDelta = Math.Min(newDelta, truncatable.Truncate()); } if (newDelta <= 0.0) { throw new TimestepTooSmallException(newDelta, BaseTime); } if (newDelta > 0.9 * States.Value.Delta) { if (Order < MaxOrder) { // Let's see if we can increase the timestep by using a bigger integration order Order++; // Truncate the timestep using the higher order newDelta = double.PositiveInfinity; foreach (var truncatable in TruncatableStates) { newDelta = Math.Min(newDelta, truncatable.Truncate()); } if (newDelta <= 0.0) { throw new TimestepTooSmallException(newDelta, BaseTime); } // Is the higher (more expensive) order useful? if (newDelta <= 1.05 * States.Value.Delta) { Order--; } } } else { Delta = Math.Min(newDelta, maxTimestep); return(false); } } // Limit the expansion of the timestep newDelta = Math.Min(Parameters.MaximumExpansion * States.Value.Delta, newDelta); // Limit the maximum timestep if (newDelta > Parameters.MaxStep) { newDelta = Parameters.MaxStep; } if (newDelta > maxTimestep) { newDelta = maxTimestep; } // Check for timesteps that became too small if (newDelta <= Parameters.MinStep) { // Was the previously tried timestep already at the minimum? if (States.Value.Delta <= Parameters.MinStep) { throw new TimestepTooSmallException(newDelta, BaseTime); } // Else let's just try one more time with the minimum timestep newDelta = Parameters.MinStep; Order = 1; } Delta = newDelta; return(true); }