Energy-management-system-program-framework / MicrogridSys_ODEAE_expIterSolver.m
HanyangHe95's picture
Upload 20 files
9677843 verified
function [Xnext,Vs,Vinv,S_busInjVec,S_inv_Batt,S_inv_PV,fval,exitflag] = ...
MicrogridSys_ODEAE_expIterSolver(X,Vs,Vinv,Ref,S_slack_ini,S_load_vec,S_DieselG_vec, ...
V_DC,Xm_g,Zmv_g,Y_Bus,controlParams,K_Vdp,K_fdp, ...
BusOfBattery,BusOfPV,dt,Niter)
% ---------- persistent caches for speed ----------
% persistent Jpat Nbus_cached
Nbus = size(Y_Bus,1);
% Build / refresh Jacobian sparsity pattern when network size changes
% if isempty(Jpat) || isempty(Nbus_cached) || Nbus_cached ~= Nbus
% Jpat = buildJacPattern(Y_Bus); % see local function below
% Nbus_cached = Nbus;
% end
% Use sparse for network matrices
Y_Bus = sparse(Y_Bus);
BatteryNum = numel(BusOfBattery);
SolarNum = numel(BusOfPV);
% Initialize iterates
Vs_iter = Vs;
Vinv_iter = Vinv;
X_iter = X;
% Constant used in inverter voltage reconstruction
M = (sqrt(3)/sqrt(2))*0.5*V_DC;
for m = 1:Niter
% ------ Build sparse inverter shunt admittances ------
% allocate diagonal sparse matrices: number of nonzeros equals #devices
Yinv_Batt = spalloc(Nbus,Nbus,BatteryNum);
Yinv_PV = spalloc(Nbus,Nbus,SolarNum);
VinvBatt_bus = zeros(Nbus,1);
VinvPV_bus = zeros(Nbus,1);
idx_X = 0;
idx_ref = 0;
idx_Vin = 0;
idx_Xm = 0;
if BatteryNum > 0
for bb = 1:BatteryNum
% 1) ODE step for GFM battery
[Inc1, ~, Edq1] = ode4_singleStep( X_iter(idx_X+1:idx_X+6), dt, ...
@PID_Droopcontroller_DynModel, ...
Ref(idx_ref+1:idx_ref+4), ...
[Vinv_iter(idx_Vin+1); Vs_iter(BusOfBattery(bb))], ...
Xm_g(idx_Xm+1), Zmv_g(idx_Xm+1), controlParams(1:10), K_Vdp, K_fdp );
Xtemp = X_iter(idx_X+1:idx_X+6) + Inc1*dt;
X_iter(idx_X+1:idx_X+6,1) = Xtemp;
% 2) Compute new Vinv from E_dq
th = Xtemp(2);
Ex1 = Edq1(1)*cos(th) - Edq1(2)*sin(th);
Ey1 = Edq1(1)*sin(th) + Edq1(2)*cos(th);
Vinv_iter(idx_Vin+1) = M*(Ex1 + 1j*Ey1);
% 3) Update shunt admittance and terminal voltage
ii = BusOfBattery(bb);
Yinv_Batt(ii,ii) = Yinv_Batt(ii,ii) + 1/(1j*Xm_g(idx_Xm+1)+Zmv_g(idx_Xm+1));
VinvBatt_bus(ii) = Vinv_iter(idx_Vin+1);
idx_X = idx_X + 6;
idx_ref= idx_ref+ 4;
idx_Xm = idx_Xm + 1;
idx_Vin= idx_Vin+ 1;
end
else
VinvBatt_bus = Vs_iter; % keeps logic unchanged
end
if SolarNum > 0
for ss = 1:SolarNum
% 1) ODE step for PQ PV
[Inc2, ~, Edq2] = ode4_singleStep( X_iter(idx_X+1:idx_X+6), dt, ...
@PID_PQcontroller_DynModel, ...
Ref(idx_ref+1:idx_ref+2), ...
[Vinv_iter(idx_Vin+1); Vs_iter(BusOfPV(ss))], ...
Xm_g(idx_Xm+1), Zmv_g(idx_Xm+1), controlParams(11:20) );
Xtemp = X_iter(idx_X+1:idx_X+6) + Inc2*dt;
X_iter(idx_X+1:idx_X+6,1) = Xtemp;
% 2) Compute new Vinv from E_dq
th = Xtemp(2);
Ex2 = Edq2(1)*cos(th) - Edq2(2)*sin(th);
Ey2 = Edq2(1)*sin(th) + Edq2(2)*cos(th);
Vinv_iter(idx_Vin+1) = M*(Ex2 + 1j*Ey2);
% 3) Update shunt admittance and terminal voltage
jj = BusOfPV(ss);
Yinv_PV(jj,jj) = Yinv_PV(jj,jj) + 1/(1j*Xm_g(idx_Xm+1)+Zmv_g(idx_Xm+1));
VinvPV_bus(jj) = Vinv_iter(idx_Vin+1);
idx_X = idx_X + 6;
idx_ref= idx_ref+ 2;
idx_Xm = idx_Xm + 1;
idx_Vin= idx_Vin+ 1;
end
else
VinvPV_bus = Vs_iter;
end
% ------ Power-flow solve (FSOLVE) ------
% Unknown vector: [Pslack; Re(V2..VN); Qslack; Im(V2..VN)]
Vs_init = Vs_iter(2:end);
Vinf = 1+0j;
z0 = [ real(S_slack_ini);
real(Vs_init);
imag(S_slack_ini);
imag(Vs_init) ];
% opts = optimoptions('fsolve', ...
% 'Display','off');
opts = optimoptions('fsolve', ...
'Display','off',...
'Algorithm','levenberg-marquardt');
% opts = optimoptions('fsolve', ...
% 'Display','off', ...
% 'Algorithm','levenberg-marquardt', ...
% 'JacobPattern', Jpat, ... % sparsity pattern
% 'FiniteDifferenceType','forward', ... % faster FD
% 'ScaleProblem','jacobian', ...
% 'FunctionTolerance',1e-9, ...
% 'StepTolerance',1e-9, ...
% 'MaxIterations', 100);
[z_sol, fval, exitflag] = fsolve(@(z) complex_power_flow_eq( ...
z, Vinf, Y_Bus, Yinv_Batt, Yinv_PV, VinvBatt_bus, VinvPV_bus, ...
S_load_vec, S_DieselG_vec), z0, opts);
% Unpack: z = [Pr; Qi]
n = numel(z_sol)/2;
Pr = z_sol(1:n);
Qi = z_sol(n+1:end);
VsR = [1; Pr(2:end)];
VsI = [0; Qi(2:end)];
Vs_iter = VsR + 1j*VsI;
% keep warm start for next call
if exitflag > 0
z_last = z_sol;
else
% if failed, reset warm start to avoid poisoning next step
z_last = [];
end
end
% ------ outputs ------
Xnext = X_iter;
Vs = Vs_iter;
Vinv = Vinv_iter;
S_busInjVec = Vs .* conj(Y_Bus*Vs);
S_inv_Batt = Vs .* conj(Yinv_Batt * (VinvBatt_bus - Vs));
S_inv_PV = Vs .* conj(Yinv_PV * (VinvPV_bus - Vs));
% ================= local functions =================
function F = complex_power_flow_eq(decision_real_imag, Vinf, Ybus, Yinv_Batt,Yinv_PV, ...
VinvBatt_bus, VinvPV_bus, S_load, S_DieselG)
n = numel(decision_real_imag)/2;
Real = decision_real_imag(1:n);
Imag = decision_real_imag(n+1:end);
Vs_real = [real(Vinf); Real(2:end)];
Vs_imag = [imag(Vinf); Imag(2:end)];
Vbus = Vs_real + 1i*Vs_imag;
% Slack complex power
Psl = Real(1); Qsl = Imag(1);
S_sys = zeros(size(S_load));
S_sys(1,1) = Psl + 1j*Qsl;
% Inverter injections
S_inv = Vbus .* conj(Yinv_Batt * (VinvBatt_bus - Vbus)) + ...
Vbus .* conj(Yinv_PV * (VinvPV_bus - Vbus));
left = Vbus .* conj(Ybus * Vbus);
right = S_inv + S_sys + S_load + S_DieselG;
F = [ real(left - right); imag(left - right) ];
end
function [IncRate,dotX,Edq] = ode4_singleStep(Xin, h, f_dyn, varargin)
X1 = Xin;
[Edq,k1] = f_dyn(varargin{:}, X1);
X2 = Xin + 0.5*h*k1;
[~,k2] = f_dyn(varargin{:}, X2);
X3 = Xin + 0.5*h*k2;
[~,k3] = f_dyn(varargin{:}, X3);
X4 = Xin + h*k3;
[~,k4] = f_dyn(varargin{:}, X4);
IncRate = (k1 + 2*k2 + 2*k3 + k4)/6;
dotX = k1;
end
% function Jp = buildJacPattern(Ybus_full)
% % Jacobian sparsity for PF residual wrt variables z = [Pr;Qi]
% % We treat unknowns as [Pslack; Re(V2..VN); Qslack; Im(V2..VN)]
% N = size(Ybus_full,1);
% nb = N-1;
% A = spones((Ybus_full ~= 0) | (Ybus_full.' ~= 0)); % adjacency including self
%
% % Rows: N real mismatches + N imag mismatches
% % Cols: 1 + nb + 1 + nb = 2N unknowns
% Jp = spalloc(2*N, 2*N, 10*N); % rough nnz guess
%
% % Real mismatches depend on Pslack (1), Re(V2..VN), Im(V2..VN)
% Jp(1:N, 1) = 1; % wrt Pslack
% Jp(1:N, 2:(1+nb)) = A(:,2:end); % wrt Re(V2..VN)
% Jp(1:N, (3+nb):end) = A(:,2:end); % wrt Im(V2..VN)
%
% % Imag mismatches depend on Qslack, Re/Im of neighbors
% Jp((N+1):end, (2+nb)) = 1; % wrt Qslack
% Jp((N+1):end, 2:(1+nb)) = A(:,2:end);
% Jp((N+1):end, (3+nb):end) = A(:,2:end);
% end
end