Я пытаюсь преобразовать свою программу из MATLAB в Python. Кажется, все работает нормально, пока я не попытаюсь инвертировать одну из моих матриц (22x22). Я получаю разные значения для обратной матрицы. В MATLAB я использовал функцию inv(J)
а также A\b
для инвертирования матрицы, тогда как в python я использовал JI
а также np.linalg.inv(J)
. Хотя обе функции в python возвращают один и тот же результат, они отличаются от того, что я получаю в MATLAB. Даже детерминантные значения различны для обоих случаев. Пока MATLAB возвращает значение порядка 10 ^ 23, python возвращает значение 10 ^ 20. Возможно ли, чтобы матрица имела более одного обратного? Или это имеет какое-либо отношение к точности расчета?
В этом потоке есть аналогичное обсуждение: несоответствия между матрицей Matlab и Numpy обратными. Тем не менее, я не нашел окончательного ответа в этой дискуссии, и поэтому я отправляю его снова.
Редактировать: Матрица не очень хорошо кондиционирована.
Вот программа python:
import numpy as np
import pandas as pd
import cmath
import math
from decimal import *
from pandas import DataFrame
ldata = '''\
1 2 1 1 1 0 0.01938 0.05917 0.0528 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1 5 1 1 1 0 0.05403 0.22304 0.0492 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2 3 1 1 1 0 0.04699 0.19797 0.0438 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2 4 1 1 1 0 0.05811 0.17632 0.0340 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2 5 1 1 1 0 0.05695 0.17388 0.0346 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
3 4 1 1 1 0 0.06701 0.17103 0.0128 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
4 5 1 1 1 0 0.01335 0.04211 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
4 7 1 1 1 0 0.0 0.20912 0.0 0 0 0 0 0 0.978 0.0 0.0 0.0 0.0 0.0 0.0
4 9 1 1 1 0 0.0 0.55618 0.0 0 0 0 0 0 0.969 0.0 0.0 0.0 0.0 0.0 0.0
5 6 1 1 1 0 0.0 0.25202 0.0 0 0 0 0 0 0.932 0.0 0.0 0.0 0.0 0.0 0.0
6 11 1 1 1 0 0.09498 0.19890 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
6 12 1 1 1 0 0.12291 0.25581 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
6 13 1 1 1 0 0.06615 0.13027 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
7 8 1 1 1 0 0.0 0.17615 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
7 9 1 1 1 0 0.0 0.11001 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
9 10 1 1 1 0 0.03181 0.08450 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
9 14 1 1 1 0 0.12711 0.27038 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
10 11 1 1 1 0 0.08205 0.19207 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
12 13 1 1 1 0 0.22092 0.19988 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
13 14 1 1 1 0 0.17093 0.34802 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
'''
line = pd.compat.StringIO(ldata)
df = pd.read_csv(line, sep='\s+', header=None)
linedata = df.values
nbus = 14
fb = (linedata[:,0]) #from bus
fb = fb.astype(int)
tb = (linedata[:,1]) #to bus
tb = tb.astype(int)
r = linedata[:,6] #resistence
x = linedata[:,7] #reactance
b = (linedata[:,8])/2 #b/2
b = 1j*b
a = linedata[:,14] #transformer ratio
#change the 0s to 1s for the transformer tap changing ratio
for i in range(len(a)):
if a[i] == 0:
a[i] = 1
z = r + 1j*x
y = 1/z
nb = int(max(max(fb),max(tb)))
#print(max(tb))
nl = len(fb)
#print(nl)
Y = np.zeros((nb,nb))
Y = Y + 1j*Y
#forming the off-diagonal elements
for k in range(nl):
Y[fb[k]-1,tb[k]-1] = (Y[fb[k]-1,tb[k]-1]) - (y[k]/a[k])
Y[tb[k]-1,fb[k]-1] = Y[fb[k]-1,tb[k]-1]
#forming the diagonal elements
for n in range(nl):
Y[fb[n]-1,fb[n]-1] = Y[fb[n]-1,fb[n]-1]+(y[n]/(a[n]*a[n]))+b[n]
#elif tb[n]==m:
Y[tb[n]-1,tb[n]-1]=Y[tb[n]-1,tb[n]-1]+((y[n])+b[n])
#print(Y)
bdata = '''\
1 Bus 1 HV 1 1 3 1.060 0.0 0.0 0.0 0 0 0.0 1.060 0.0 0.0 0.0 0.0 0
2 Bus 2 HV 1 1 2 1.045 -4.98 21.7 12.7 40.0 42.4 0.0 1.045 50.0 -40.0 0.0 0.0 0
3 Bus 3 HV 1 1 2 1.010 -12.72 94.2 19.0 0.0 23.4 0.0 1.010 40.0 0.0 0.0 0.0 0
4 Bus 4 HV 1 1 0 1.019 -10.33 47.8 -3.9 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0
5 Bus 5 HV 1 1 0 1.020 -8.78 7.6 1.6 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0
6 Bus 6 LV 1 1 2 1.070 -14.22 11.2 7.5 0.0 12.2 0.0 1.070 24.0 -6.0 0.0 0.0 0
7 Bus 7 ZV 1 1 0 1.062 -13.37 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0
8 Bus 8 TV 1 1 2 1.090 -13.36 0.0 0.0 0.0 17.4 0.0 1.090 24.0 -6.0 0.0 0.0 0
9 Bus 9 LV 1 1 0 1.056 -14.94 29.5 16.6 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.19 0
10 Bus 10 LV 1 1 0 1.051 -15.10 9.0 5.8 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0
11 Bus 11 LV 1 1 0 1.057 -14.79 3.5 1.8 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0
12 Bus 12 LV 1 1 0 1.055 -15.07 6.1 1.6 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0
13 Bus 13 LV 1 1 0 1.050 -15.16 13.5 5.8 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0
14 Bus 14 LV 1 1 0 1.036 -16.04 14.9 5.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0
'''
busd = pd.compat.StringIO(bdata)
dfbus = pd.read_csv(busd, sep='\s+', header=None)
dfbus = dfbus.loc[:,dfbus.dtypes != 'object']
busdata = dfbus.values
#print(busdata)
BMva = 100
bus = busdata[:,1]
type = busdata[:,4]
for i in range(len(type)):
if type[i] == 3:
type[i] = 1
elif type[i] == 0:
type[i] = 3
V = busdata[:,12]
for i in range(len(V)):
if V[i] == 0:
V[i] = 1
delta = busdata[:,15]
Pg = busdata[:,9]/BMva
Qg = busdata[:,10]/BMva
Pl = busdata[:,7]/BMva
Ql = busdata[:,8]/BMva
Qmin = busdata[:,14]/BMva
Qmax = busdata[:,13]/BMva
P = Pg - Pl
Q = Qg - Ql
Psp = P
Qsp = Q
#print(Qsp)
G = Y.real
B = Y.imag
pq = np.where(type == 3)
npq = len(pq[0])
pv = np.where(type < 3)
npv = len(pv[0])
Tol = 1
Iter = 1
while Tol > 1e-5:
P = np.zeros(nbus)
Q = np.zeros(nbus)
#Calculate P and Q
for i in range(nbus):
for k in range(nbus):
P[i] = P[i] + V[i]*V[k]*(G[i,k]*np.cos(delta[i]-delta[k]) + B[i,k]*np.sin(delta[i]-delta[k]))
Q[i] = Q[i] + V[i]*V[k]*(G[i,k]*np.sin(delta[i]-delta[k]) - B[i,k]*np.cos(delta[i]-delta[k]))
#Calculate the change from the specified value
dPa = Psp - P
dQa = Qsp - Q
k = 0
dQ = np.zeros(npq)
for i in range(nbus):
if type[i] == 3:
dQ[k] = dQa[i]
k = k + 1
dP = dPa[1:nbus]
#print(dP)
M = np.concatenate((dP,dQ))
#Jacobian Matrix formation
#J1-Derivative of real power injections with angles
J1 = np.zeros((nbus-1,nbus-1))
for i in range(nbus-1):
m = i + 1
for k in range(nbus-1):
n = k + 1
if n == m:
for n in range(nbus):
J1[i,k] = J1[i,k] + V[m]*V[n]*(-G[m,n]*np.sin(delta[m]-delta[n])+B[m,n]*np.cos(delta[m]-delta[n]))
J1[i,k] = J1[i,k] - (V[m]**2)*B[m,m]
else:
J1[i,k] = V[m]*V[n]*(G[m,n]*np.sin(delta[m]-delta[n])-B[m,n]*np.cos(delta[m]-delta[n]))
#J2 = Derivative of real power injections with V
J2 = np.zeros((nbus-1,npq))
for i in range(nbus-1):
m = i +1
for k in range(npq):
n = pq[0][k]
#print(n)
if n == m:
for n in range(nbus):
J2[i,k] = J2[i,k] + V[n]*(G[m,n]*np.cos(delta[m]-delta[n])+B[m,n]*np.sin(delta[m]-delta[n]))
J2[i,k] = J2[i,k] - V[m]*G[m,m]
else:
J2[i,k] = V[m]*(G[m,n]*np.cos(delta[m]-delta[n])+B[m,n]*np.sin(delta[m]-delta[n]))
#J3 = derivative of Reactive Power Injections with Angles
J3 = np.zeros((npq,nbus-1))
for i in range(npq):
m = pq[0][i]
for k in range(nbus-1):
n = k + 1
if n == m:
for n in range(nbus):
J3[i,k] = J3[i,k] + V[m]*V[n]*(G[m,n]*np.cos(delta[m]-delta[n])+B[m,n]*np.sin(delta[m]-delta[n]))
J3[i,k] = J3[i,k] - (V[m]**2)*G[m,m]
else:
J3[i,k] = V[m]*V[n]*(-G[m,n]*np.cos(delta[m]-delta[n])-B[m,n]*np.sin(delta[m]-delta[n]))
#J4 = Derivative of Reactive Power Injections with V
J4 = np.zeros((npq,npq))
for i in range(npq):
m = pq[0][i]
for k in range(npq):
n = pq[0][k]
if n == m:
for n in range(nbus):
J4[i,k] = J4[i,k] + V[n]*(G[m,n]*np.sin(delta[m]-delta[n])-B[m,n]*np.cos(delta[m]-delta[n]))
J4[i,k] = J4[i,k] - V[m]*B[m,m]
else:
J4[i,k] = V[m]*(G[m,n]*np.sin(delta[m]-delta[n])-B[m,n]*np.cos(delta[m]-delta[n]))
Jtemp1 = np.concatenate((J1,J3))
Jtemp2 = np.concatenate((J2,J4))
J = np.concatenate((Jtemp1,Jtemp2),axis=1)
#Jinvc = getMatrixInverse(J)
X = np.linalg.solve(J,M)
print(X)
dTh = X[0,0:nbus-1]
#print(dTh)
dV = X[0,nbus-1:2*npq+npv-1]
print(dV[0,8])
delta[1:nbus] = dTh + delta[1:nbus]
k = 0
for i in range(1,nbus):
if type[i] == 3:
V[i] = dV[0,k] + V[i]
k = k + 1
Iter = Iter + 1
Tol = max(abs(M))
print(V)
Это код MATLAB:
nbus = 14; % IEEE-14, IEEE-30, IEEE-57..
Y = ybusppg(nbus); % Calling ybusppg.m to get Y-Bus Matrix..
busd = busdatas(nbus); % Calling busdatas..
BMva = 100; % Base MVA..
bus = busd(:,1); % Bus Number..
type = busd(:,2); % Type of Bus 1-Slack, 2-PV, 3-PQ..
V = busd(:,3); % Specified Voltage..
del = busd(:,4); % Voltage Angle..
Pg = busd(:,5)/BMva; % PGi..
Qg = busd(:,6)/BMva; % QGi..
Pl = busd(:,7)/BMva; % PLi..
Ql = busd(:,8)/BMva; % QLi..
Qmin = busd(:,9)/BMva; % Minimum Reactive Power Limit..
Qmax = busd(:,10)/BMva; % Maximum Reactive Power Limit..
P = Pg - Pl; % Pi = PGi - PLi..
Q = Qg - Ql; % Qi = QGi - QLi..
Psp = P; % P Specified..
Qsp = Q; % Q Specified..
G = real(Y); % Conductance matrix..
B = imag(Y); % Susceptance matrix..
pv = find(type == 2 | type == 1); % PV Buses..
pq = find(type == 3); % PQ Buses..
npv = length(pv); % No. of PV buses..
npq = length(pq); % No. of PQ buses..
Tol = 1;
Iter = 1;
while (Tol > 1e-5) % Iteration starting..
P = zeros(nbus,1);
Q = zeros(nbus,1);
% Calculate P and Q
for i = 1:nbus
for k = 1:nbus
P(i) = P(i) + V(i)* V(k)*(G(i,k)*cos(del(i)-del(k)) + B(i,k)*sin(del(i)-del(k)));
Q(i) = Q(i) + V(i)* V(k)*(G(i,k)*sin(del(i)-del(k)) - B(i,k)*cos(del(i)-del(k)));
end
end
% Checking Q-limit violations..
if Iter <= 7 && Iter > 2 % Only checked up to 7th iterations..
for n = 2:nbus
if type(n) == 2
QG = Q(n)+Ql(n);
if QG < Qmin(n)
V(n) = V(n) + 0.01;
elseif QG > Qmax(n)
V(n) = V(n) - 0.01;
end
end
end
end
% Calculate change from specified value
dPa = Psp-P;
dQa = Qsp-Q;
k = 1;
dQ = zeros(npq,1);
for i = 1:nbus
if type(i) == 3
dQ(k,1) = dQa(i);
k = k+1;
end
end
dP = dPa(2:nbus);
M = [dP; dQ]; % Mismatch Vector
% Jacobian
% J1 - Derivative of Real Power Injections with Angles..
J1 = zeros(nbus-1,nbus-1);
for i = 1:(nbus-1)
m = i+1;
for k = 1:(nbus-1)
n = k+1;
if n == m
for n = 1:nbus
J1(i,k) = J1(i,k) + V(m)* V(n)*(-G(m,n)*sin(del(m)-del(n)) + B(m,n)*cos(del(m)-del(n)));
end
J1(i,k) = J1(i,k) - V(m)^2*B(m,m);
else
J1(i,k) = V(m)* V(n)*(G(m,n)*sin(del(m)-del(n)) - B(m,n)*cos(del(m)-del(n)));
end
end
end
% J2 - Derivative of Real Power Injections with V..
J2 = zeros(nbus-1,npq);
for i = 1:(nbus-1)
m = i+1;
for k = 1:npq
n = pq(k);
if n == m
for n = 1:nbus
J2(i,k) = J2(i,k) + V(n)*(G(m,n)*cos(del(m)-del(n)) + B(m,n)*sin(del(m)-del(n)));
end
J2(i,k) = J2(i,k) + V(m)*G(m,m);
else
J2(i,k) = V(m)*(G(m,n)*cos(del(m)-del(n)) + B(m,n)*sin(del(m)-del(n)));
end
end
end
% J3 - Derivative of Reactive Power Injections with Angles..
J3 = zeros(npq,nbus-1);
for i = 1:npq
m = pq(i);
for k = 1:(nbus-1)
n = k+1;
if n == m
for n = 1:nbus
J3(i,k) = J3(i,k) + V(m)* V(n)*(G(m,n)*cos(del(m)-del(n)) + B(m,n)*sin(del(m)-del(n)));
end
J3(i,k) = J3(i,k) - V(m)^2*G(m,m);
else
J3(i,k) = V(m)* V(n)*(-G(m,n)*cos(del(m)-del(n)) - B(m,n)*sin(del(m)-del(n)));
end
end
end
% J4 - Derivative of Reactive Power Injections with V..
J4 = zeros(npq,npq);
for i = 1:npq
m = pq(i);
for k = 1:npq
n = pq(k);
if n == m
for n = 1:nbus
J4(i,k) = J4(i,k) + V(n)*(G(m,n)*sin(del(m)-del(n)) - B(m,n)*cos(del(m)-del(n)));
end
J4(i,k) = J4(i,k) - V(m)*B(m,m);
else
J4(i,k) = V(m)*(G(m,n)*sin(del(m)-del(n)) - B(m,n)*cos(del(m)-del(n)));
end
end
end
J = [J1 J2; J3 J4]; % Jacobian Matrix..
condJ = cond(J)
detJ = det(J);
X = J\M;% Correction Vector
dTh = X(1:nbus-1); % Change in Voltage Angle..
dV = X(nbus:end); % Change in Voltage Magnitude..
% Updating State Vectors..
del(2:nbus) = dTh + del(2:nbus); % Voltage Angle..
k = 1;
for i = 2:nbus
if type(i) == 3
V(i) = dV(k) + V(i); % Voltage Magnitude..
k = k+1;
end
end
Iter = Iter + 1;
Tol = max(abs(M)); % Tolerance..
end
Функция ybusppg:
function Y = ybusppg(num) % Returns Y
linedata = linedatas(num); % Calling Linedatas...
fb = linedata(:,1); % From bus number...
tb = linedata(:,2); % To bus number...
r = linedata(:,3); % Resistance, R...
x = linedata(:,4); % Reactance, X...
b = linedata(:,5); % Ground Admittance, B/2...
a = linedata(:,6); % Tap setting value..
z = r + i*x; % z matrix...
y = 1./z; % To get inverse of each element...
b = i*b; % Make B imaginary...
nb = max(max(fb),max(tb)); % No. of buses...
nl = length(fb); % No. of branches...
Y = zeros(nb,nb); % Initialise YBus...
% Formation of the Off Diagonal Elements...
for k = 1:nl
Y(fb(k),tb(k)) = Y(fb(k),tb(k)) - y(k)/a(k);
Y(tb(k),fb(k)) = Y(fb(k),tb(k));
end
% Formation of Diagonal Elements....
for m = 1:nb
for n = 1:nl
if fb(n) == m
Y(m,m) = Y(m,m) + y(n)/(a(n)^2) + b(n);
elseif tb(n) == m
Y(m,m) = Y(m,m) + y(n) + b(n);
end
end
end
%Y; % Bus Admittance Matrix
%Z = inv(Y);
Функция linedata:
function linedt = linedatas(num)
% | From | To | R | X | B/2 | X'mer |
% | Bus | Bus | pu | pu | pu | TAP (a) |
linedat14 = [1 2 0.01938 0.05917 0.0264 1
1 5 0.05403 0.22304 0.0246 1
2 3 0.04699 0.19797 0.0219 1
2 4 0.05811 0.17632 0.0170 1
2 5 0.05695 0.17388 0.0173 1
3 4 0.06701 0.17103 0.0064 1
4 5 0.01335 0.04211 0.0 1
4 7 0.0 0.20912 0.0 0.978
4 9 0.0 0.55618 0.0 0.969
5 6 0.0 0.25202 0.0 0.932
6 11 0.09498 0.19890 0.0 1
6 12 0.12291 0.25581 0.0 1
6 13 0.06615 0.13027 0.0 1
7 8 0.0 0.17615 0.0 1
7 9 0.0 0.11001 0.0 1
9 10 0.03181 0.08450 0.0 1
9 14 0.12711 0.27038 0.0 1
10 11 0.08205 0.19207 0.0 1
12 13 0.22092 0.19988 0.0 1
13 14 0.17093 0.34802 0.0 1 ];
switch num
case 14
linedt = linedat14;
end
Функция данных шины:
function busdt = busdatas(num)
% Type....
% 1 - Slack Bus..
% 2 - PV Bus..
% 3 - PQ Bus..
% |Bus | Type | Vsp | theta | PGi | QGi | PLi | QLi | Qmin | Qmax |
busdat14 = [1 1 1.060 0 0 0 0 0 0 0;
2 2 1.045 0 40 42.4 21.7 12.7 -40 50;
3 2 1.010 0 0 23.4 94.2 19.0 0 40;
4 3 1.0 0 0 0 47.8 -3.9 0 0;
5 3 1.0 0 0 0 7.6 1.6 0 0;
6 2 1.070 0 0 12.2 11.2 7.5 -6 24;
7 3 1.0 0 0 0 0.0 0.0 0 0;
8 2 1.090 0 0 17.4 0.0 0.0 -6 24;
9 3 1.0 0 0 0 29.5 16.6 0 0;
10 3 1.0 0 0 0 9.0 5.8 0 0;
11 3 1.0 0 0 0 3.5 1.8 0 0;
12 3 1.0 0 0 0 6.1 1.6 0 0;
13 3 1.0 0 0 0 13.5 5.8 0 0;
14 3 1.0 0 0 0 14.9 5.0 0 0;];
switch num
case 14
busdt = busdat14;
end
Никакая матрица не может иметь более одного обратного, но она не может иметь обратного, математически.
Но все вычисления на компьютере выполняются с конечными представлениями с плавающей точкой и различными неточностями, которые могут возникать на каждом шаге вычисления.
Результат численного инвертирования матрицы зависит от того, насколько "хорошо обусловлена" матрица. Даже если матрица может быть инвертирована, если она плохо кондиционирована, то небольшие изменения в входе могут вызвать большие изменения в выходе. Так как значения в матрице представлены конечными представлениями с плавающей запятой, просто несколько разных внутренних представлений могут вызвать большие различия. Кроме того, поскольку все вычисления выполняются конечными представлениями с плавающей точкой, детали используемого алгоритма могут вызывать различия с плохо обусловленной матрицей.
Немного более подробный ответ: когда матрица плохо приспособлена, это означает, что у нее есть очень маленькие собственные значения. Пусть используется сингулярная декомпозиция значений, если ваша матрица X=L @S @RT
, тогда обратное значение Xi=R @np.sqrt(S) @LT
. Теперь средняя часть инвертирует собственные значения, и если есть очень маленькие, ошибка с плавающей запятой может иметь огромную разницу в их обратном и, следовательно, разницу, которую вы видите там.
Но имеет ли это значение? Нет, если ваша матрица не удовлетворена, то инверсия, которую вы вычисляете, ненадежна, вы можете проверить это, умножив ее на исходную матрицу и увидеть, что не каждый элемент близок к матрице идентичности.
Каково решение? 1. Используйте предварительный кондиционер 2. Если вы видите большое изменение в собственных значениях исходной матрицы, просто выбросьте эти несущественные собственные значения и собственные векторы, восстановите свою матрицу, а затем ваш обратный должен быть одинаковым независимо от платформы/пакета/ОС, которые вы используются. Обратите внимание, что первый подход в основном делает то же самое, что и второе, просто разные математические шаги.
inv
- плохой выбор. Просто используйтеA\b
вместоinv(A)*b
. Это происходит потому, что у вас плохо обусловленная матрица.