-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgear.py
191 lines (151 loc) · 6.59 KB
/
gear.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# -*- coding: iso-8859-1 -*-
# gear.py
# Gear Linear Multi-Step (LMS) DF
# 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 should be conforming to a standard
""" About the method:
This is an implicit method, it means that to compute dx(n+1)/dt the value of x in (n+1) is required.
We don't know it, since it's our objective).
This method, as all other implicit methods, allows us to write the derivative as:
dx(n+1)/dt = x_coeff * x(n+1) + const (ii)
The get_df method returns those two vectors.
Gear's LMS interpolates the solution in a number of points equal to its order. Since it's a implicit
method, one of these is x(n+1). The values x(n), x(n-1)... x(n-(order+2)) need to be supplied to the method.
We can write x(t) as:
x(t) = a0 + a1*(t(n+1) - t ) + a2*( t(n+1) - t )^2 + ... (i)
The equation has <order> a coefficients, which we need to determine.
For this reason, we write a system of "order" equations in this way:
x(n+1) = a0 + a1*(t(n+1) - t(n+1)) + a2*(t(n+1) - t(n+1))^2 + ...
x(n) = a0 + a1*(t(n+1) - t(n) ) + a2*( t(n+1) - t(n) )^2 + ...
x(n-1) = a0 + a1*(t(n+1) - t(n-1)) + a2*(t(n+1) - t(n-1))^2 + ...
Which may be rewritten as:
z = A * a
z is the vector of known values of x
A is a time dependant matrix
a is a vector made of the a* coeffiecients
We don't need to explicit ALL of the a* coeffiecients. What we are really looking for is the derivative of x
in t(n+1), dx(n+1)/dt in short.
If we differentiate the relation (i):
dx(t)/dt = -a1 - 2*a2*( t(n+1) - t ) - 3*a3*( t(n+1) - t )^2 ...
Which evaluated in t = t(n+1) gives:
dx(n+1)/dt = -1 * a1
Our objective is then a1.
From the previsious system we write:
a = A^-1 * z
a1 is a[1,0], which may be extracted in this way:
et = [0 1 0 0 0 0 ...] (order elements)
a1 = et * a = et * A^-1 * z
Because of the associative prperty of matrix multiplication, we can write:
P = et * A^-1
a1 = P[1, :] * z
But, we don't know z[0,0] = x(n+1), we can split the above relation:
a1 = P[1, 0] * x(n+1) + P[1, 1:] * z[1:, 0]
We arrived to the relation written above (ii)
dx(n+1)/dt = x_coeff * x(n+1) + const = -1*a1
So:
x_coeff = -1 * P[1, 0]
const = -1 * P[1, 1:] * z[1:, 0]
This module uses a faster way to compute the values that doesn't require to invert the matrix.
Anyway, from a theorical point of view, the above applies.
"""
import numpy, sys
import utilities, printing
order = None
#FAST = True
def is_implicit():
return True
def has_ff():
return True
def get_required_values():
"""This returns two python arrays built this way:
[ max_order_of_x, max_order_of_dx ]
Where:
Both the values are int, or None
if max_order_of_x is set to k, the df method needs all the x(n-i) values of x,
where i<=k (the value the function assumed i+1 steps before the one we will ask for the derivative).
The same applies to max_order_of_dx, but regards dx(n)/dt
None means 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.
"""
# notice that: it returns the same values, it should be order-1 if no prediction is required BUT
# we build the required values in a (hopefully) fast way, that requires one point more.
return ((order, None), (order, None))
def get_df(pv_array, suggested_step, predict=False):
"""The array must be built in this way:
It has to be an array of arrays. Each of them has the following structure:
[time, numpy_matrix, numpy_matrix]
Hence the pv_array[k] element is made of:
_ time is the time in which the solution is valid: t(n-k)
_ The first numpy_matrix is x(n-k)
_ The second is d(x(n-k))/dt
Values that are not needed may be set to None and they will be disregarded.
if predict == True, it needs one more point to give a prediction
of x at the suggested step.
Returns: None if the incorrect values were given, or quits.
Otherwise returns an array:
_ the [0] element is the numpy matrix of coeffiecients (Nx1) of x(n+1)
_ the [1] element is the numpy matrix of constant terms (Nx1) of x(n+1)
The derivative may be written as:
d(x(n+1))/dt = ret[0]*x(n+1) + ret[1]"""
if order is None:
printing.print_general_error("You must set Gear's order before using it! e.g. gear.order = 5")
sys.exit(1)
s = []
s.append(0)
for index in range(1, order + 2):
s.append(suggested_step + pv_array[0][0] - pv_array[index - 1][0])
# build e[k, i]
e = numpy.mat(numpy.zeros((order + 2, order + 2)))
for k_index in range(1, order + 2):
for i_index in range(1, order + 2):
if i_index == k_index:
e[k_index, i_index] = 1
else:
e[k_index, i_index] = s[i_index] / (s[i_index] -s[k_index])
alpha = numpy.mat(numpy.zeros((1, order + 2)))
for k_index in range(1, order + 2):
alpha[0, k_index] = 1.0
for j_index in range(order + 1):
alpha[0, k_index] = alpha[0, k_index] * e[k_index, j_index+1]
#build gamma
gamma = numpy.mat(numpy.zeros((1, order +1)))
for k_index in range(1, order + 1):
gamma[0, k_index] = alpha[0, k_index] * ((1.0/s[order+1]) - (1.0/s[k_index]))
gamma[0, 0] = 0
for index in range(1, order + 1):
gamma[0, 0] = gamma[0, 0] - gamma[0, index]
# values to be returned
C1 = gamma[0, 0]
C0 = numpy.mat(numpy.zeros(pv_array[0][1].shape))
for index in range(order):
C0 = C0 + gamma[0, index + 1] * pv_array[index][1]
x_lte_coeff = 0
for k_index in range(1, order+1):
x_lte_coeff = x_lte_coeff + (s[k_index] ** (order + 1)) * (-1.0 * gamma[0, k_index] / gamma[0, 0])
x_lte_coeff = ((-1.0)**(order + 1)) *(1.0/utilities.fact(order + 1)) * x_lte_coeff
if predict:
predict_x = numpy.mat(numpy.zeros(pv_array[0][1].shape))
for index in range(1, order + 2): #order
predict_x = predict_x + alpha[0, index] * pv_array[index - 1][1]
predict_lte_coeff = -1.0/(utilities.fact(order + 1))
for index in range(1, order + 2):
predict_lte_coeff = predict_lte_coeff * s[index]
#print predict_lte_coeff
#print x_lte_coeff
else:
predict_x = None
predict_lte_coeff = None
return [C1, C0, x_lte_coeff, predict_x, predict_lte_coeff]