# -*- coding: iso-8859-1 -*-
# trap.py
# Trap DF (with a second order prediction)
# Copyright 2006 Giuseppe Venturini
# This file is part of the ahkab simulator.
#
# Ahkab 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, version 2 of the License.
#
# Ahkab 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 v2
# along with ahkab. If not, see <http://www.gnu.org/licenses/>.
"""
This file implements the Trapezoidal (TRAP) Differentiation Formula (DF) and a
second order prediction formula.
Module reference
----------------
"""
from __future__ import (unicode_literals, absolute_import,
division, print_function)
import numpy as np
from numpy.linalg import inv
from .py3compat import range_type
order = 2
[docs]def is_implicit():
"""Is this Differentiation Formula (DF) implicit?
**Returns:**
isit : boolean
In this case, that's ``True``.
"""
return True
[docs]def get_required_values():
"""Get info regarding what values are needed by the DF
**Returns:**
tpl : tuple of tuples
A tuple of two tuples.
* The first tuple indicates what past values of the unknown are needed
for the DF.
* The second tuple indicates what past values of the unknown are needed
for the prediction method.
In particular, each of the sub-tuples is built this way:
::
(max_order_of_x, max_order_of_dx)
Where both the values are either ``int``, or ``None``.
If ``max_order_of_x`` is set to an integer value :math:`k`, the DF needs
all the :math:`x_{n-i}` values of x, for all :math:`0 \\le i \\le k`. In
the previous text, :math:`x_{n-i}` is the value the :math:`x` array
assumed :math:`i` steps before the one we are considering for the
derivative.
Similar considerations apply to ``max_order_of_dx``, but regard rather
:math:`dx_n/dt` instead of :math:`x_n`.
If any of the values is set to ``None``, it is to be assume that no
value is required.
The first array has to be used if no prediction is required, the second are the values needed for prediction.
"""
# we need x(n) and dx(n)/dt
return ((0, 0), (2, None))
[docs]def has_ff():
"""Has the method a Forward Formula for prediction?
**Returns:**
doesit : bool
In this particular case, this function always returns ``True``.
"""
return True
[docs]def get_df(pv_array, suggested_step, predict=True):
"""Get the coefficients for DF and prediction formula
**Parameters:**
pv_array : sequence of sequences
Each element of ``pv_array`` must be of the form:
::
(time, x, derivate(x))
In particular, the :math:`k` element of `pv_array` contains the values
of:
* :math:`t_{n-k}` (the time),
* :math:`x_{n-k}`,
* :math:`dx_{n-k}/dt`
evaluated :math:`k` time steps before the current one, labeled
:math:`n+1`.
How many samples are necessary is given by
:func:`ahkab.trap.get_required_values`.
Values that are not needed may be set to ``None``, as they will be
disregarded.
suggested_step : float
The step that will be used for the current iteration, provided the error
will be deemed acceptable.
predict : bool, optional
Whether a prediction for :math:`x_n` is needed as well or not. Defaults
to ``True``.
**Returns:**
ret : tuple
The return value has the form:
::
(C1, C0, x_lte_coeff, predict_x, predict_lte_coeff)
The derivative may be written as:
.. math::
d(x(n+1))/dt = C1 x(n+1) + C0
`x_lte_coeff` is the coefficient of the Local Truncation Error,
`predict_x` is the predicted value for :math:`x` and `predict_lte_coeff`
is the LTE coefficient for the prediction.
:raises ValueError: if the `pv_array` is malformed.
"""
# our method needs x(n) dx(n)/dt
if len(pv_array[0]) != 3:
raise ValueError('trap.get_df got a pv_array of wrong dimensions')
if pv_array[0][1] is None or pv_array[0][2] is None:
raise ValueError('trap.get_df got a pv_array that does not contain'
' required values')
if predict and (len(pv_array) < 2 or pv_array[0][1] is None or
pv_array[1][1] is None or pv_array[2][1] is None):
raise ValueError('trap.get_df got predict=True but pv_array that does'
' not contain the required values')
C1 = 2.0 / suggested_step
C0 = -1.0 * (2.0 / suggested_step * pv_array[0][1] + pv_array[0][2])
x_lte_coeff = 1.0 / 12 * suggested_step ** 3
if predict and len(pv_array) > 2 and pv_array[0][1] is not None and \
pv_array[1][1] is not None and pv_array[2][1] is not None:
A = np.zeros((len(pv_array[0]), len(pv_array[0])))
A[0, :] = 0
A[:, 0] = 1
for row in range_type(1, A.shape[0]):
for col in range_type(1, A.shape[0]):
A[row, col] = (pv_array[row][0] - pv_array[0][0]) ** col
Ainv = inv(A)
predict_x = np.zeros(pv_array[0][1].shape)
z = np.zeros((3, 1))
for var in range_type(pv_array[0][1].shape[0]):
for index in range(z.shape[0]):
z[index, 0] = pv_array[index][1][var, 0]
alpha = np.dot(Ainv, z)
predict_x[var, 0] = alpha[2, 0] * suggested_step**2 + \
alpha[1, 0] * suggested_step + alpha[0, 0]
predict_lte_coeff = -1.0 / 6.0 * suggested_step * \
(pv_array[0][0] + suggested_step - pv_array[1][0]) * \
(pv_array[0][0] + suggested_step - pv_array[2][0])
else:
predict_x, predict_lte_coeff = (None, None)
return C1, C0, x_lte_coeff, predict_x, predict_lte_coeff