%$Id: tr_step-control.tex,v 21.13 2002/03/25 05:37:03 al Exp $
% man Tech tr_step-control .
% Copyright (C) 2001 Albert Davis
% Author: Albert Davis <aldavis@ieee.org>
%
% This file is part of "Gnucap", the Gnu Circuit Analysis Package
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2, or (at your option)
% any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
% 02111-1307, USA.
%------------------------------------------------------------------------
\subsection{Step control}

\subsubsection{The basic algorithm}

The basis of it is in the files ``s\_tr\_swp.cc'' and ``s\_tr\_rev.cc''.

The function TRANSIENT::review sets two variables: ``approxtime'' and
``control''.

The variable ``approxtime'' is a suggestion of what the next time
should be.  Note that this is a time, not a difference.  Also note
that the simulator may override this suggestion.  Another ``control''
is an enum that shows how the time was selected.  You can probe
control(0) to find this code, or control(1) to see how many steps (not
iterations) it calculated internally.

This time may be in the future, past, or again at the present time,
depending on conditions.  A time in the future means all is well, and
the simulation can proceed as expected.  A time in the past indicates
something is wrong, such as convergence failure, excessive truncation
error, or a missed event.  In this case, the step is rejected, and
time backed up.  A repeat at the present time usually means a latency
check failed.  A portion of the circuit that was thought to be latent
was found to be active.  This usually indicates a model problem.

First, it attempts to suggest a time ``rtime'' based on iteration count
and options.

There are several ``options'' that control the stepping:

\begin{itemize}

\item iterations $>$ itl4 ... reduce by option "trstepshrink".

\item iterations $>$ itl3 ... suggest the same step as last time.

\item else (iterations $<=$ itl3) ... increase step size.  Try the
max as per userstepsize/skip limit to larger of (rdt*trstepgrow) where
``rdt'' is the old ``review'' estimate or (oldstep*trstepgrow) where
oldstep is what was actually used last time and trstepgrow is an
option, from the options command.

\end{itemize}

Second it makes another suggestion ``tetime'' based on truncation
error, etc.  It does this by calling the ``review'' function for all
components, and taking the minimum.  Any component can suggest a time
for its next evaluation with its review function.  Most components
return a very large number, letting the capacitors and inductors
dominate, but it is not required for it to be so.  This time should be
in the future, but errors could produce a time in the past.

Then, the earliest time of the above two methods is selected.  A time
in the past means to reject the most recent time step and back up, but
note that this time is only a suggestion that may not be used.

The function ``TRANSIENT::sweep'' essentially processes the loop ``for
(first(); notpastend; next())''.  The function ``TRANSIENT::next()''
actually advances (hopefully) to the next step.  It may go backwards.

The actual time step depends on the suggestion by the review function
(approxtime), the event queue (which includes what Spice calls
``breakpoints''), the user step size (nexttick), and some tricks to
minimize changes.

Some considerations ...
\begin{itemize}

\item Changing the step size is an expensive operation, because it
usually forces a full LU decomposition and matrix reload.  If the step
can be kept constant, changes are limited to the right-side,
eliminating the need for the full evaluation and LU.

\item The simulator will place a time step exactly at any step for
which the user has requested output, or Fourier analysis needs a
point, or at any event from the event queue.

\end{itemize}

So, here it is ...

Assume we want it at the time the user requested.  If the event queue
says to do it sooner, take it, else take the user time.  Note that
this time is needed exactly, either now or later.  If the
``approxtime'' is sooner than the exact time, inject a time step as
follows...  if the time step is less than half of the time to when an
exact time is needed, take the approxtime, else take half of the
exact interval, in hopes that the next step will use up the other
half.

After that, there are some checks ....

``Very backward time step'' means that the suggested new step is
earlier than the PREVIOUS step, meaning that both the current step and
its predecessor are rejected, thus it should back up two steps.  Since
it can back up only one step, it rejects the most recent step and
tries again at the minimum step size.  This usually means there is a
bug in the software.

``Backwards time step'' means to reject the most recent step, but the
one before that is ok.  It will reject the step and try again at the
smaller interval.  This happens fairly often, usually due to slow
convergence.

``Zero time step'' means that the new time is the same as the previous
time, which usually means there is a bug in the software.  Something
is requesting a re-evaluation at the same time.

The combination of ``zero time step'' and ``very backward time step''
means that the re-evaluation didn't work.

Now, accept the new time and proceed.

\subsubsection{The ``review'' function}

Every component can have a ``review'' function, in which it can
determine whether to accept or reject the solution.  It will accept by
suggesting a time in the future, or reject by suggesting a time in the
past.  It returns the suggested time.  It can call new\_event to
request an exact time.

For capacitors and inductors, the review function attempts to estimate
truncation error using a divided difference method, and it suggests a
time for the next solution that will result in meeting the error
requirement.  Occasionally, it will discover that the step just
computed fails to meet the requirement, so it will reject it.

Truncation error is related to the third derivative of charge or flux.
Since current is the first derivative of charge, it would seem that
second derivative of current should produce the same results faster.
In practice, the current based method tends to estimate high leading
to smaller steps, and the charge based method tends to estimate low,
leading to larger steps.  The conservative approach would suggest
using the current based method, but that sometimes led to unreasonably
small steps ans slow simulations, so I chose (as Spice did) the other
method.  Either method is ok when the step size used is close to being
reasonable, but when the trial step is unreasonably large, either
approach gives a very poor estimate.  Taking a step much too small
will make the simulator run much slower, as it takes many steps, then
the step size is allowed to grow slowly.  This is slower both because
of the many unnecessary steps, and because of many adjustments.
Taking a step that is much too large will result in a choice that is
better than the first trial, which will make a better estimate and be
rejected.  It is rare to get more than one rejection based on
truncation error.

\subsubsection{Conclusion}

Gnucap will usually do more time steps than Spice will, due to 2 factors.
Gnucap will force calculations at print points and fourier points, and
can reject a bad step.  It is usually a little more, but could be as
much as twice as many steps.
%------------------------------------------------------------------------
%------------------------------------------------------------------------
