// Wake Code - Matlab // Rasmus Christensen // Control and Automation, Aalborg University // N_turb = number of turbines. // X_turb = x-position of turbine. // Y_turb = y-position of turbine. #endregion internal static void ROTATE_corrd(out ILArray<double> out_x, out ILArray<double> out_y, ILArray<double> xTurb, ILArray<double> yTurb, double rotA) { #region "Used variables declaration" ILArray<double> x_out; ILArray<double> y_out; int i; #endregion x_out = zeros(1, length(xTurb)); // Initialization x-coordinates y_out = zeros(1, length(yTurb)); // Initialization y-coordinates rotA = rotA * pi / 180; // Conversion to radians for (i = 1; i <= length(xTurb); i++) { x_out._(i, '=', xTurb._(i) * cos(rotA) - xTurb._(i) * sin(rotA)); y_out._(i, '=', xTurb._(i) * sin(rotA) + yTurb._(i) * cos(rotA)); } if (min_(x_out) < 0) // Moves the x-points if these are negative. { x_out = x_out + 500 + abs(min_(x_out)); } if (min_(y_out) < 0) // Moves the y-points if these are negative. { y_out = y_out + 500 + abs(min_(y_out)); } out_x = x_out; out_y = y_out; }
// Wake Code - Matlab // Rasmus Christensen // Control and Automation, Aalborg University // N_turb = number of turbines. // X_turb = x-position of turbine. // Y_turb = y-position of turbine. #endregion internal static void ROTATE_corrd(out ILArray <double> out_x, out ILArray <double> out_y, ILArray <double> xTurb, ILArray <double> yTurb, double rotA) { #region "Used variables declaration" ILArray <double> x_out; ILArray <double> y_out; int i; #endregion x_out = zeros(1, length(xTurb)); // Initialization x-coordinates y_out = zeros(1, length(yTurb)); // Initialization y-coordinates rotA = rotA * pi / 180; // Conversion to radians for (i = 1; i <= length(xTurb); i++) { x_out._(i, '=', xTurb._(i) * cos(rotA) - xTurb._(i) * sin(rotA)); y_out._(i, '=', xTurb._(i) * sin(rotA) + yTurb._(i) * cos(rotA)); } if (min_(x_out) < 0) // Moves the x-points if these are negative. { x_out = x_out + 500 + abs(min_(x_out)); } if (min_(y_out) < 0) // Moves the y-points if these are negative. { y_out = y_out + 500 + abs(min_(y_out)); } out_x = x_out; out_y = y_out; }
//P_ref is a vector of power refenreces for tehe wind turbine with dimension 1xN //v_nac is a vector of wind speed at each wind turbine with dimension 1xN //P_demand is a scale of the wind farm power demand. //parm is a struct of wind turbine parameters e.g. NREL5MW #endregion internal static void powerDistributionControl(out ILArray <double> P_ref, out ILArray <double> P_a, double[] v_nac, double P_demand, WindTurbineParameters parm) { #region "Used variables declaration" double rho; ILArray <double> R; ILArray <double> rated; ILArray <double> Cp; int i; #endregion rho = parm.rho; //air density for each wind turbine(probably the same for all) R = parm.radius.C; //rotor radius for each wind turbine(NREL.r=63m) rated = parm.rated.C; //Rated power for each wind turbine(NREL.Prated=5MW) Cp = parm.Cp.C; // Max cp of the turbines for each wind turbine(NREL.Cp.max=0.45) P_a = zeros(parm.N, 1); P_ref = zeros(parm.N, 1); // Compute available power at each turbine for (i = 1; i <= parm.N; i++) { P_a._(i, '=', min_(__[rated._(i), (pi / 2) * rho * _p(R._(i), 2) * _p(v_nac[i - 1], 3) * Cp._(i)])); } var sum_P_a_ = sum_(P_a); //Distribute power according to availibility for (i = 1; i <= parm.N; i++) { if (P_demand < sum_P_a_) { P_ref._(i, '=', max_(__[0, min_(__[rated._(i), P_demand * P_a._(i) / sum_P_a_])])); } else { P_ref._(i, '=', P_a._(i)); } } }
//P_ref is a vector of power refenreces for tehe wind turbine with dimension 1xN //v_nac is a vector of wind speed at each wind turbine with dimension 1xN //P_demand is a scale of the wind farm power demand. //parm is a struct of wind turbine parameters e.g. NREL5MW #endregion internal static void powerDistributionControl(out ILArray<double> P_ref, out ILArray<double> P_a, double[] v_nac, double P_demand, WindTurbineParameters parm) { #region "Used variables declaration" double rho; ILArray<double> R; ILArray<double> rated; ILArray<double> Cp; int i; #endregion rho = parm.rho; //air density for each wind turbine(probably the same for all) R = parm.radius.C; //rotor radius for each wind turbine(NREL.r=63m) rated = parm.rated.C; //Rated power for each wind turbine(NREL.Prated=5MW) Cp = parm.Cp.C; // Max cp of the turbines for each wind turbine(NREL.Cp.max=0.45) P_a = zeros(parm.N, 1); P_ref = zeros(parm.N, 1); // Compute available power at each turbine for (i = 1; i <= parm.N; i++) { P_a._(i, '=', min_(__[ rated._(i), (pi / 2) * rho * _p(R._(i), 2) * _p(v_nac[i - 1], 3) * Cp._(i) ])); } var sum_P_a_ = sum_(P_a); //Distribute power according to availibility for (i = 1; i <= parm.N; i++) { if (P_demand < sum_P_a_) { P_ref._(i, '=', max_(__[ 0, min_(__[ rated._(i), P_demand * P_a._(i) / sum_P_a_ ]) ])); } else { P_ref._(i, '=', P_a._(i)); } } }
// Wake Code - Matlab // Rasmus Christensen // Control and Automation, Aalborg University #endregion internal static void Turb_centr_coord(out ILArray<int> output, int nTurb, int iMax, ILArray<double> x, ILArray<double> xTurb, int gridRes) { #region "Used variables declaration" ILArray<int> xxcTurb; int i; int ii; #endregion xxcTurb = zeros_(1, nTurb); for (i = 1; i <= nTurb; i++) { for (ii = 1; ii <= iMax - 1; ii++) { if (abs(x._(ii)) <= abs(xTurb._(i)) && abs(xTurb._(i)) < abs(x._(ii + 1))) { xxcTurb._(i, '=', ii * sign(xTurb._(i))); break; } } } output = xxcTurb; }
// Wake Code - Matlab // Rasmus Christensen // Control and Automation, Aalborg University #endregion internal static void Turb_centr_coord(out ILArray <int> output, int nTurb, int iMax, ILArray <double> x, ILArray <double> xTurb, int gridRes) { #region "Used variables declaration" ILArray <int> xxcTurb; int i; int ii; #endregion xxcTurb = zeros_(1, nTurb); for (i = 1; i <= nTurb; i++) { for (ii = 1; ii <= iMax - 1; ii++) { if (abs(x._(ii)) <= abs(xTurb._(i)) && abs(xTurb._(i)) < abs(x._(ii + 1))) { xxcTurb._(i, '=', ii * sign(xTurb._(i))); break; } } } output = xxcTurb; }
internal static void wakeCalculation(out ILArray <double> v_nac, ILArray <double> Ct, int i, ILArray <double> wind) { #region "Original function comments" //% v_nac = WAKECALCULATION(Ct,i,wind) //This function calculates the wake //Currently it is a very very simplified wake calculation. It just serves as //a placeholder for a correct wake calculation that will come later #endregion #region "Used variables declaration" ILArray <double> scaling; #endregion scaling = linspace(0.5, 0.9, length(Ct)); v_nac = scaling * wind._(i, 2); }
internal static void wakeCalculation(out ILArray<double> v_nac, ILArray<double> Ct, int i, ILArray<double> wind) { #region "Original function comments" //% v_nac = WAKECALCULATION(Ct,i,wind) //This function calculates the wake //Currently it is a very very simplified wake calculation. It just serves as //a placeholder for a correct wake calculation that will come later #endregion #region "Used variables declaration" ILArray<double> scaling; #endregion scaling = linspace(0.5, 0.9, length(Ct)); v_nac = scaling * wind._(i, 2); }
//% v_nac = WAKECALCULATION(Ct,i,wind) // RLC, Aalborg // The below is based on the .F90 code developed by ?, and will give a // better estimate of the actual wake the individual turbines experience. #endregion internal static void wakeCalculationsRLC(out ILArray <double> vNac, double dTurb, int nTurb, double kWake, ILArray <double> x, int gridX, int gridY, ILArray <double> yOrder, double dy, ILArray <int> xTurbC, ILArray <int> yTurbC, ILArray <double> Ct, ILArray <double> wField, double[] vHub, WindTurbineParameters parm, SimParm simParm) { #region "Used variables declaration" double[,] Velocity; int j; #endregion // Velocity Computation Compute_Vell(out Velocity, yOrder, xTurbC, yTurbC, x, wField, vHub, kWake, gridX, gridY, nTurb, dTurb, Ct, dy); // Extracting the individual Nacelle Wind Speeds from the wind velocity matrix. //Velocity = Velocity'; vNac = zeros(nTurb, 1); for (j = 1; j <= length(xTurbC); j++) { vNac._(j, '=', Velocity[yTurbC._(j) - 1, xTurbC._(j) - 1]); } }
//% v_nac = WAKECALCULATION(Ct,i,wind) // RLC, Aalborg // The below is based on the .F90 code developed by ?, and will give a // better estimate of the actual wake the individual turbines experience. #endregion internal static void wakeCalculationsRLC(out ILArray<double> vNac, double dTurb, int nTurb, double kWake, ILArray<double> x, int gridX, int gridY, ILArray<double> yOrder, double dy, ILArray<int> xTurbC, ILArray<int> yTurbC, ILArray<double> Ct, ILArray<double> wField, double[] vHub, WindTurbineParameters parm, SimParm simParm) { #region "Used variables declaration" double[,] Velocity; int j; #endregion // Velocity Computation Compute_Vell(out Velocity, yOrder, xTurbC, yTurbC, x, wField, vHub, kWake, gridX, gridY, nTurb, dTurb, Ct, dy); // Extracting the individual Nacelle Wind Speeds from the wind velocity matrix. //Velocity = Velocity'; vNac = zeros(nTurb, 1); for (j = 1; j <= length(xTurbC); j++) { vNac._(j, '=', Velocity[yTurbC._(j) - 1, xTurbC._(j) - 1]); } }
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // DESCRIPTION: // RLC - 8/9/2014, interpolation, for getting an accurate CP/CT value. The // reason for a standalone script, is that the built in MATLAB script // (interp) includes a lot of redundancy checks that are not necessary, and // not feasible for embedded solutions. // Based on the NREL5MW Turbine. // Uses Linear Polynomial Extrapolation, to get a value for CP, given a Beta // (Revolutional speed) and Lambda (Tip-Speed-Ratio) of the Turbine. // The interpolation is computed as: // y = y0 + (y1 - y0)*(x-x0)/(x1-x0) // // Beta is the revolutional entry. // Lambda is the TSR entry ratio. // turbineTable defines the table to lookup in. // negYes defines wether the CP value should be allowed to be negative. //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #endregion internal static void interpTable(out double interpValue, double Beta, double Lambda, ILArray <double> table, ILArray <double> turbineTableBeta, ILArray <double> turbineTableLambda, bool negYes) { #region "Used variables declaration" ILArray <double> turbineTable; int sizeBt; int sizeLa; int Bt0; int Bt1; int La0; int La1; ILArray <double> tableLookup; ILArray <double> lambdaIntervals; double betaIntervals; #endregion //% Setup, loads the table, and stores it. //persistent turbineTable tableLoad //if isempty(tableLoad) turbineTable = table.C; // tableLoad = 1; //end size(out sizeBt, out sizeLa, turbineTable); //% Index Interpolation // Finds the beta-point of the interpolation. min(out Bt0, abs(turbineTableBeta - Beta)); // Determines the index of the closest point. if (Beta > turbineTableBeta._(Bt0)) { if (Bt0 == sizeBt) //length(turbineTableBeta) { Bt1 = Bt0; Bt0 = Bt0 - 1; } else { Bt1 = Bt0 + 1; } } else { if (Bt0 == 1) { Bt1 = Bt0 + 1; } else { Bt1 = Bt0; Bt0 = Bt1 - 1; } } // Finds the Lambda-point of the interpolation. min(out La0, abs(turbineTableLambda - Lambda)); // Determines the index of the closest point. if (Lambda > turbineTableLambda._(La0)) { if (La0 == sizeLa) //length(turbineTableLambda) { La1 = La0; La0 = La1 - 1; } else { La1 = La0 + 1; } } else { if (La0 == 1) { La1 = La0 + 1; } else { La1 = La0; La0 = La1 - 1; } } //% Table Interpolation // Table lookup using indexes obtained previously: tableLookup = __[turbineTable._(Bt0, La0), turbineTable._(Bt0, La1), ';', turbineTable._(Bt1, La0), turbineTable._(Bt1, La1)]; // Interpolating, using the Lambda values first, then the Betas. lambdaIntervals = __[((tableLookup._(1, 2) - tableLookup._(1, 1)) / (turbineTableLambda._(La1) - turbineTableLambda._(La0))) * (Lambda - turbineTableLambda._(La0)) + tableLookup._(1, 1), ';', ((tableLookup._(2, 2) - tableLookup._(2, 1)) / (turbineTableLambda._(La1) - turbineTableLambda._(La0))) * (Lambda - turbineTableLambda._(La0)) + tableLookup._(1, 2)]; // Interpolation, using the Beta values (using the intervales computed above). betaIntervals = ((lambdaIntervals._(2) - lambdaIntervals._(1)) / (turbineTableBeta._(Bt1) - turbineTableBeta._(Bt0))) * (Beta - turbineTableBeta._(Bt0)) + lambdaIntervals._(1); //% Negativity Handling // If the negYes value is set as true in the system, the functino will only // give an absolute value of Ct and Cp - have to be modified so it does this // correctly. if (negYes) { interpValue = 0; } else { interpValue = betaIntervals; } }
// Wake Simulation // (C) Rasmus Christensen // Control and Automation, Aalborg University 2014 // Compute the velocity in front of each wind-turbine, with respect to the wind input. #endregion internal static void Compute_Vell(out double[,] vel_output, ILArray <double> yTurb, ILArray <int> xTurbC, ILArray <int> yTurbC, ILArray <double> x, ILArray <double> wField, double[] Uhub, double kWake, int iMax, int jMax, int nTurb, double dTurb, ILArray <double> Ct, double dy) { #region "Used variables declaration" double[,] vell_i; ILArray <double> shadow; double r0; double nk; int k; int J; double SS; double SS0; int i; double RR_i; double Dij; double Alpha_i; double Alpha_k; double Area; int ii; double rrt; double nj; int jjMin; int jjMax; #endregion //vell_i = wField.C; vell_i = new double[wField.Size[0], wField.Size[1]]; for (var i0 = 0; i0 < vell_i.GetLength(0); i0++) { for (var i1 = 0; i1 < vell_i.GetLength(1); i1++) { vell_i[i0, i1] = wField.GetValue(i0, i1); } } shadow = zeros(1, nTurb); r0 = 0.5 * dTurb; nk = 2 * ceil(dTurb / dy); for (k = 1; k <= nTurb; k++) { J = 0; SS = 0; SS0 = pi * r0 * r0; for (i = 1; i <= k - 1; i++) { RR_i = r0 + kWake * (x._(xTurbC._(k)) - x._(xTurbC._(i))); Dij = abs(yTurb._(i) - yTurb._(k)); if ((RR_i >= (r0 + Dij)) || (Dij <= dy)) { SS = SS + ((r0 * r0) / (RR_i * RR_i)); } else { if (RR_i >= (r0 + Dij) && Dij > dy) { J = J + 1; Alpha_i = acos((RR_i * RR_i) + (Dij * Dij) - (r0 * r0) / (2 * RR_i * Dij)); Alpha_k = acos(((r0 * r0) + (Dij * Dij) - (RR_i * RR_i)) / (2 * r0 * Dij)); AArea(out Area, RR_i, r0, Dij); shadow._(J, '=', (Alpha_i * (RR_i * RR_i) + Alpha_k * (r0 * r0)) - 2 * Area); SS = SS + ((shadow._(J)) / SS0) * ((r0 * r0) / (RR_i * RR_i)); } } } for (ii = xTurbC._(k); ii <= iMax; ii++) { rrt = r0 + kWake * (x._(ii) - x._(xTurbC._(k))); nj = ceil(rrt / dy); jjMin = floor_(max(1, yTurbC._(k) - nj)); jjMax = ceil_(min(jMax, yTurbC._(k) + nj)); for (var j = jjMin; j <= jjMax; j++) { if (((-vell_i[ii - 1, j - 1] + Uhub[k - 1]) > 0) && (ii > xTurbC._(k) + nk)) { vell_i[ii - 1, j - 1] = min(vell_i[ii - 2, j - 1], Uhub[k - 1] + Uhub[k - 1] * (sqrt(1 - Ct._(k)) - 1) * ((r0 * r0) / (rrt * rrt)) * (1 - (1 - sqrt(1 - Ct._(k))) * SS)); vell_i[ii - 1, j - 1] = max(0, vell_i[ii - 1, j - 1]); } else { vell_i[ii - 1, j - 1] = (Uhub[k - 1] + Uhub[k - 1] * (sqrt(1 - Ct._(k)) - 1) * (r0 / rrt) * (r0 / rrt)) * (1 - (1 - sqrt(1 - Ct._(k))) * SS); vell_i[ii - 1, j - 1] = max(0, vell_i[ii - 1, j - 1]); } } } } vel_output = vell_i; }
internal static void NowCastWFPFunc(out string outMethod, out ILArray<double> outTime, out ILArray<double> outX, out ILArray<double> outXhmsAll, out int outXhmsAllTimeOffset, out int outXhmsLLength, out int outXhmsUOffset, ILArray<double> Data, double TPredict = TPredictDef, string Method = MethodDef, int r = rDef, double Ts = TsDef) { #region "Original function comments" //Nowcasting with model based on total wind farm power // // NowCastWFPFunc(Data,TPredict,Method,r,Ts) // // Data : Total wind farm power // TPredict : Time for starting multi step prediction. If TPredict<1 it // is assumed a fraction of the end time (default 0.5) // Method : 'AR(1)' or 'Persistence' only first letter count (default 'a') // r : Decimation with a moving average of order r (default 1) // Ts : Sampling time (default 0.1) // // External input: None // Time-stamp: <2014-10-17 14:09:40 tk> // Version 1: Initial version // Torben Knudsen // Aalborg University, Dept. of Electronic Systems, Section of Automation // and Control // E-mail: [email protected] #endregion #region "Used variables declaration" int IMT; double q0; double TauLambdaLRel; double Lambda0; double TauLambdaInf; double TimeScaling; //string TitleStr; int NS; ILArray<double> T; ILArray<double> TimePlot; double NWT; double NomWFP; ILArray<double> PWF; double MinWFP; double TauLambdaL; double LambdaL; double LambdaInf; int TPS; ILArray<double> Theta; ILArray<double> Sigma; ILArray<double> Xh; ILArray<double> Lambda; ILArray<int> Time; ILArray<double> Res; int TPSEst; double A; double B; ILArray<double> xhms; ILArray<double> covxhms; double aux; int i; ILArray<double> q; double xh; double xt; double dt; ILArray<double> sigmaxhms; ILArray<double> ConIntAWFP; int NS10Min; int NSOneHour; #endregion //% setting up inputs //TsDef = 0.1; //rDef = 1; //MethodDef = "a"; //TPredictDef = 0.5; //if nargin < 5; Ts= []; end; //if nargin < 4; r= []; end; //if nargin < 3; Method= []; end; //if nargin < 2; TPredict= []; end; //if nargin < 1; error('Error TK: To few input arguments'); end; //if isempty(r); r= rDef; end; //if isempty(TPredict); TPredict= TPredictDef; end; //if isempty(Ts); Ts= TsDef; end; //if isempty(Method); Method= MethodDef; end; //% Parameters IMT = 1; // Include measurement time; q0 = 0; TauLambdaLRel = 0.1; // TauLambdaL= 10% of the samples Lambda0 = 0.5; // Initial Lambda TauLambdaInf= 600; // TauLambdaInf= 10min TimeScaling = 1.0 / 3600; // From seconds to hours //% Initialization // Use Offwind simulation data made by Rasmus 2014-10-14 // The format is: // [time sumPower sumRef sumAvai] as a matrix NS x 4. Power i MW // 48 WT are simulated. Data for individual WT are also found e.g. in // Power, P_ref, PA, beta etc if (strncmpi(Method, "a", 1)) { Method = "AR(1)"; } else { Method = "Persistence"; } //TitleStr = "Nowcasting with " + Method + " model based on total wind farm " //... // + "power, Offwind simulation"; Data = Data[_(':')]; NS = size(Data, 1); // Number of samples NS = max_(find(Data > 0)); // Number of samples; Data = Data[_(1, ':', NS)]; // Limit the data T = Ts * (_c(1, NS)).T; TimePlot = T * TimeScaling; // Time in hours NWT = 48; NomWFP = NWT * 5e6 * 1e-6; // Power in MW PWF = Data; // Total Power in MW if (r > 1) { DecimateWMA(out PWF, out _ILArray_double, PWF, r); NS = size(PWF, 1); // Number of samples Ts = Ts * r; T = Ts * (_c(1, NS)).T; TimePlot = T * TimeScaling; // Time in hours } MinWFP = 0; // For real WFs //% Definitions etc. // Calculate Lamba* from TauLambda* and dt TauLambdaL = TauLambdaLRel * (T._(end) - T._(1));// TauLambdaL= 10% of the samples LambdaL = exp(-Ts / TauLambdaL); LambdaInf = exp(-Ts / TauLambdaInf); //% Algorithm // Initialization // Prediction from time in TPredict // if TPredict is a fraction calculate TPredict if (TPredict < 1) { TPredict = round(TPredict * T._(end) / Ts) * Ts; } if (TPredict < TauLambdaInf) { warning(__[ "TK: Prediction time is so small that the estimator/predictor ", //... "might not have converged yet" ]); } TPS = min_(find(T >= TPredict)); // Use time from measurements // Multi step prediction if (strncmpi(Method, "a", 1)) { // ARX1 version; // Recursive parameter estimation RLSMARX1(out Theta, out Sigma, out Xh, out Lambda, out Time, out _ILArray_double, out _ILArray_double, out _ILArray_double, PWF, 1, LambdaInf); Res = __[ TimePlot[_(Time)], PWF[_(Time)], Xh, Sigma, Lambda ]; Res = __[ nan(Time._(1) - 1, size(Res, 2)), ';', Res ]; // Notice that length of Time is shorter than length of T if batch RLS // start is used so TPSEst < TPS in that case TPSEst = min_(find(_dbl(Time) * Ts >= TPredict)); // Use time from estimates // Parameter values must be taken for index TPSEst A = Theta._(TPSEst, 1); if (abs(A) > 1) { warning(__[ "TK: Unstable pole, max(abs(eig)): ", num2str(abs(A)) ]); } B = Theta._(TPSEst, 2); xhms = zeros(NS - TPS, 1); covxhms = zeros(NS - TPS, 1); xhms._(1, '=', Xh._(TPSEst + 1)); covxhms._(1, '=', Sigma._(TPSEst + 1)); aux = Sigma._(TPSEst + 1); for (i = 2; i <= NS - TPS; i++) { xhms._(i, '=', A * xhms._(i - 1) + B); aux = A * aux * A.T(); covxhms._(i, '=', covxhms._(i - 1) + aux); } // Prepend xhms with the present measurement so the plot clearly indicates // the time for the measurement } else { // Persistence version; // Initialization Lambda = Lambda0; q = q0; xh = PWF._(1); Res = __[ T._(1), PWF._(1), xh, q, Lambda0 ]; // Recursive estimation of incremental covariance for (i = 2; i <= NS; i++) { xt = PWF._(i) - xh; dt = T._(i) - T._(i - 1); q = _m(Lambda, '*', q) + (1 - Lambda) * _p(xt, 2) / dt; Res = __[ Res, ';', __[ T._(i), PWF._(i), xh, q, Lambda ] ]; Lambda = LambdaL * Lambda + (1 - LambdaL) * LambdaInf; xh = PWF._(i); } Res[_(':'), _(1)] = Res[_(':'), _(1)] * TimeScaling; // Persistence version; xhms = Res._(TPS + 1, 3) * ones(NS - TPS, 1); covxhms = Res._(TPS + 1, 4) * Ts * ((_c(1, NS - TPS)).T); } if (IMT != 0) { xhms = __[ PWF._(TPS), ';', xhms ]; covxhms = __[ 0, ';', covxhms ]; } sigmaxhms = sqrt(covxhms); ConIntAWFP = _m(xhms, '*', __[ 1, 1, 1 ]) + _m(sigmaxhms, '*', __[ -2, 0, 2 ]); ConIntAWFP = min(max(ConIntAWFP, MinWFP), NomWFP); // Limit confidence limits // Plot results // Plot actual as black solid and lower, prediction and upper confidence // limits as red, green and blue solid for 10 min, dashed for one hour and // dotted for the rest. //figure; NS10Min = min(NS - TPS - 1, round_(600.0 / Ts)); NSOneHour = min(NS - TPS - 1, round_(3600.0 / Ts)); //set(gcf, "defaultaxescolororder", ILMath.eye(3, 3)); outMethod = Method; outTime = TimePlot; outX = Res[_(':'), _(2)]; outXhmsAll = ConIntAWFP; outXhmsAllTimeOffset = (TPS - IMT); outXhmsLLength = (NS10Min + IMT); outXhmsUOffset = (NSOneHour + IMT); //plot( // TimePlot, Res[ILMath.full, 2 - 1], 'k',///... // TimePlot[ILMath.r(TPS + 1 - IMT - 1, TPS + NS10Min - 1)], ConIntAWFP[ILMath.r(1 - 1, NS10Min + IMT - 1), ILMath.full],//... // TimePlot[ILMath.r(TPS + NS10Min + 1 - 1, TPS + NSOneHour - 1)], ConIntAWFP[_a(NS10Min + 1, 1, NSOneHour) + IMT - 1, ILMath.full], "--",//... // TimePlot[ILMath.r(TPS + NSOneHour + 1 - 1, ILMath.end)], ConIntAWFP[ILMath.r(NSOneHour + 1 + IMT - 1, ILMath.end), ILMath.full], ':'); //title(TitleStr); //Legend= {'x' 'xhmsL' 'xhms' 'xhmsU'}; //legend(Legend); //grid('on'); }
private static void RLSMARX1(out ILArray<double> Theta, out ILArray<double> Sigma, out ILArray<double> Xh, out ILArray<double> Lambda, out ILArray<int> Time, out ILArray<double> AByTime, out ILArray<double> BByTime, out ILArray<double> SigmaByTime, ILArray<double> X, ILArray<double> U = null, ILArray<double> lambda = null, ILArray<double> theta0 = null, ILArray<double> sigma0 = null, int Plot = PlotDef) { #region "Original function comments" //RLS calculates recursive least squares parameter estimates corresponding // to the output input data X U and the model x(n+1)= A*x(n)+B*u(n)+e(n) // <=> x(n+1)'= [x(n)' u(n)']*[A'; B']+e(n) // Duble exponential forgetting can be used where lamba increases // exponentielly from lamba0 to lambda. // // [Theta,Sigma,Xh,Lambda,Time]= RLSMARX1(X,U,lambda,theta0,sigma0) // // X: Output Matrix with size number of samples * number of channels // U: Input Matrix with size number of samples * number of channels // zero scalar/matrix corresponds to no input, ones corresponds to estimating // a mean value parameter. (default ones) // lambda : Forgetting factor (default 0.99) if lambda= [lambda lambda0] // the forgetting factor increases exponentially from lambda0 // to lambda. // theta0 : Initial parameter estimate. Notice that theta0= [A0';B0']; // sigma0 : Initial covvariance estimate. // If no initial parameters are specifyed a offline/batch // estimate is used for the start. // Plot : If 1/true plot informative plots (default: false) // // External input: // Time-stamp: <2014-10-17 11:44:27 tk> // Version 1: 2014-10-01 app. // Version 2: 2014-10-02 12:53:07 tk Included LS/offline/batch startup // Version 3: 2014-10-07 13:57:54 tk Included additional output and // plotting // Torben Knudsen // Aalborg University, Dept. of Electronic Systems, Section of Automation // and Control // E-mail: [email protected] #endregion #region "Used variables declaration" double TauLambdaLRel; double lambdaInf; double lambda0; double IniNumSampFrac; int N; int n; int m; double TauLambdaL; double lambdaL; ILArray<double> sigma; ILArray<double> theta; int NIni; ILArray<double> XX; ILArray<double> YY; ILArray<double> R; ILArray<double> Epsilon; ILArray<double> xh; ILArray<double> Res; int i; ILArray<double> phi; ILArray<double> epsilon; #endregion //% setting up inputs //UDef = 1; //lambdaDef = 0.99; //theta0Def = 0; //sigma0Def = 0; //PlotDef = 0; //if (nargin < 6) { Plot = []; } if (sigma0 == null) { sigma0 = __[' ']; } if (theta0 == null) { theta0 = __[' ']; } if (lambda == null) { lambda = __[' ']; } if (U == null) { U = __[' ']; } //if (nargin < 1) { error('Error TK: To few input arguments'); } if (isempty(U)) { U = UDef; } //if (isempty(Plot)) { Plot = PlotDef; } if (isempty(lambda)) { lambda = lambdaDef; } if (isempty(theta0)) { theta0 = theta0Def; } if (isempty(sigma0)) { sigma0 = sigma0Def; } //% Parameters TauLambdaLRel = 0.1; // TauLambdaL= 10% of the samples lambdaInf = lambda._(1); if (length(lambda) > 1) // Initial lambda; { lambda0 = lambda._(2); } else { lambda0 = lambdaInf; } IniNumSampFrac = 0.1; // Number of samples to use for init //% Definitions etc. N = size(X, 1); n = size(X, 2); m = size(U, 2); // Calculate Lamba* from TauLambda* assuming Ts= 1 TauLambdaL = TauLambdaLRel * N; lambdaL = exp(-1.0 / TauLambdaL); if (all(U == 1)) { U = ones(N, 1); } //% Algorithm // Initialisation if (all(all(sigma0 == 0))) // No sigma0 specifyed { sigma = 0 * eye(n); } else { sigma = sigma0; } if (all(all(theta0 == 0))) // No sigma0 specifyed { theta = zeros(n + m, n); } else { theta = theta0; } // Start with a offline estimate if initial parameters are not specifyed if (all(__[ theta0, ';', sigma0 ] == 0)) { NIni = max(n + 1, ceil_(N * IniNumSampFrac)); XX = __[ X[_(1, ':', NIni - 1), _(':')], U[_(1, ':', NIni - 1), _(':')] ]; YY = X[_(_c_(1, NIni - 1) + 1), _(':')]; R = _m(XX.T, '*', XX); theta = _m(_m(inv(R), '*', (XX.T)), '*', YY); Epsilon = YY - _m(XX, '*', theta); sigma = _m(Epsilon.T, '*', Epsilon) / NIni; xh = _m(XX[_(end), _(':')], '*', theta); } else { NIni = 1; xh = X[_(1), _(':')]; R = 1e6 * eye(n); } lambda = lambda0; Res = __[ NIni, (theta[_(':')]).T, (sigma[_(':')]).T, xh, lambda ]; AByTime = zeros(n, n, N - NIni + 1); AByTime[_(':'), _(':'), _(1)] = (theta[_(1, ':', n), _(1, ':', n)]).T; BByTime = zeros(n, m, N - NIni + 1); BByTime[_(':'), _(':'), _(1)] = (theta[_(n + 1, ':', n + m), _(1, ':', n)]).T; SigmaByTime = zeros(n, n, N - NIni + 1); SigmaByTime[_(':'), _(':'), _(1)] = sigma; // Recursion // Model x(n+1)= A*x(n)+B*u(n)+e(n) <=> // x(n+1)'= [x(n)' u(n)']*[A'; B']+e(n) for (i = NIni + 1; i <= N; i++) { phi = (__[ X[_(i - 1), _(':')], U[_(i - 1), _(':')] ]).T; // phi(i) R = (lambda._Scalar()) * R + _m(phi, '*', (phi.T)); // R(i) xh = _m((phi.T), '*', theta); // xh(i) epsilon = X[_(i), _(':')] - xh; // epsilon(i) theta = theta + _m(_s(R, '\\', phi), '*', epsilon); // theta(i) sigma = _m(lambda, '*', sigma) + _m(_m((1 - lambda), '*', (epsilon.T)), '*', epsilon); Res = __[ Res, ';', __[ i, theta[_(':')].T, sigma[_(':')].T, xh, lambda ] ]; lambda = lambdaL * lambda + (1 - lambdaL) * lambdaInf; AByTime[_(':'), _(':'), _(i - NIni + 1)] = (theta[_(1, ':', n), _(1, ':', n)]).T; // Index start at 2 BByTime[_(':'), _(':'), _(i - NIni + 1)] = (theta[_(n + 1, ':', n + m), _(1, ':', n)]).T; SigmaByTime[_(':'), _(':'), _(i - NIni + 1)] = sigma; } // Res is organised like // i theta(:)' sigma(:)' xh lambda with the dimensions // 1 (n+m)*n n^2 n 1 Time = _int(Res[_(':'), _(1)]); Theta = Res[_(':'), _(1 + 1, ':', 1 + (n + m) * n)]; Sigma = Res[_(':'), _(1 + 1 + (n + m) * n, ':', 1 + (n + m) * n + _p_(n, 2))]; Xh = Res[_(':'), _(1 + 1 + (n + m) * n + _p_(n, 2), ':', 1 + (n + m) * n + _p_(n, 2) + n)]; Lambda = Res[_(':'), _(1 + 1 + (n + m) * n + _p_(n, 2) + n, ':', end)]; // Do informative plotting and output //if (Plot) //{ // figure; // plot([U X]); // title('Input and measurements'); // figure; // plot(Time,reshape(AByTime,n*n,N-NIni+1)'); // title('A parameters'); // figure; // plot(Time,reshape(BByTime,n*m,N-NIni+1)'); // title('B parameters'); // figure; // plot(Time,reshape(SigmaByTime,n*n,N-NIni+1)'); // title('Sigma parameters'); // figure; // plot(Time,Lambda); // title('Lambda'); // Epsilon= X(Time,:)-Xh; // Epsilon= Epsilon(100:end,:); // figure; // XCorrtkAll(Epsilon); // figure; // normplot(Epsilon); // gridtk('on',figures); // Lab= ['Final parameters A n*n B n*m Sigma n*n']; // RowLab= {''}; // ColLab= {''}; // Res= [AByTime(:,:,end) BByTime(:,:,end) SigmaByTime(:,:,end)]; // printmattk(Res,Lab); //} }
internal static void NowCastWFPFunc(out string outMethod, out ILArray <double> outTime, out ILArray <double> outX, out ILArray <double> outXhmsAll, out int outXhmsAllTimeOffset, out int outXhmsLLength, out int outXhmsUOffset, ILArray <double> Data, double TPredict = TPredictDef, string Method = MethodDef, int r = rDef, double Ts = TsDef) { #region "Original function comments" //Nowcasting with model based on total wind farm power // // NowCastWFPFunc(Data,TPredict,Method,r,Ts) // // Data : Total wind farm power // TPredict : Time for starting multi step prediction. If TPredict<1 it // is assumed a fraction of the end time (default 0.5) // Method : 'AR(1)' or 'Persistence' only first letter count (default 'a') // r : Decimation with a moving average of order r (default 1) // Ts : Sampling time (default 0.1) // // External input: None // Time-stamp: <2014-10-17 14:09:40 tk> // Version 1: Initial version // Torben Knudsen // Aalborg University, Dept. of Electronic Systems, Section of Automation // and Control // E-mail: [email protected] #endregion #region "Used variables declaration" int IMT; double q0; double TauLambdaLRel; double Lambda0; double TauLambdaInf; double TimeScaling; //string TitleStr; int NS; ILArray <double> T; ILArray <double> TimePlot; double NWT; double NomWFP; ILArray <double> PWF; double MinWFP; double TauLambdaL; double LambdaL; double LambdaInf; int TPS; ILArray <double> Theta; ILArray <double> Sigma; ILArray <double> Xh; ILArray <double> Lambda; ILArray <int> Time; ILArray <double> Res; int TPSEst; double A; double B; ILArray <double> xhms; ILArray <double> covxhms; double aux; int i; ILArray <double> q; double xh; double xt; double dt; ILArray <double> sigmaxhms; ILArray <double> ConIntAWFP; int NS10Min; int NSOneHour; #endregion //% setting up inputs //TsDef = 0.1; //rDef = 1; //MethodDef = "a"; //TPredictDef = 0.5; //if nargin < 5; Ts= []; end; //if nargin < 4; r= []; end; //if nargin < 3; Method= []; end; //if nargin < 2; TPredict= []; end; //if nargin < 1; error('Error TK: To few input arguments'); end; //if isempty(r); r= rDef; end; //if isempty(TPredict); TPredict= TPredictDef; end; //if isempty(Ts); Ts= TsDef; end; //if isempty(Method); Method= MethodDef; end; //% Parameters IMT = 1; // Include measurement time; q0 = 0; TauLambdaLRel = 0.1; // TauLambdaL= 10% of the samples Lambda0 = 0.5; // Initial Lambda TauLambdaInf = 600; // TauLambdaInf= 10min TimeScaling = 1.0 / 3600; // From seconds to hours //% Initialization // Use Offwind simulation data made by Rasmus 2014-10-14 // The format is: // [time sumPower sumRef sumAvai] as a matrix NS x 4. Power i MW // 48 WT are simulated. Data for individual WT are also found e.g. in // Power, P_ref, PA, beta etc if (strncmpi(Method, "a", 1)) { Method = "AR(1)"; } else { Method = "Persistence"; } //TitleStr = "Nowcasting with " + Method + " model based on total wind farm " //... // + "power, Offwind simulation"; Data = Data[_(':')]; NS = size(Data, 1); // Number of samples NS = max_(find(Data > 0)); // Number of samples; Data = Data[_(1, ':', NS)]; // Limit the data T = Ts * (_c(1, NS)).T; TimePlot = T * TimeScaling; // Time in hours NWT = 48; NomWFP = NWT * 5e6 * 1e-6; // Power in MW PWF = Data; // Total Power in MW if (r > 1) { DecimateWMA(out PWF, out _ILArray_double, PWF, r); NS = size(PWF, 1); // Number of samples Ts = Ts * r; T = Ts * (_c(1, NS)).T; TimePlot = T * TimeScaling; // Time in hours } MinWFP = 0; // For real WFs //% Definitions etc. // Calculate Lamba* from TauLambda* and dt TauLambdaL = TauLambdaLRel * (T._(end) - T._(1));// TauLambdaL= 10% of the samples LambdaL = exp(-Ts / TauLambdaL); LambdaInf = exp(-Ts / TauLambdaInf); //% Algorithm // Initialization // Prediction from time in TPredict // if TPredict is a fraction calculate TPredict if (TPredict < 1) { TPredict = round(TPredict * T._(end) / Ts) * Ts; } if (TPredict < TauLambdaInf) { warning(__["TK: Prediction time is so small that the estimator/predictor ", //... "might not have converged yet"]); } TPS = min_(find(T >= TPredict)); // Use time from measurements // Multi step prediction if (strncmpi(Method, "a", 1)) { // ARX1 version; // Recursive parameter estimation RLSMARX1(out Theta, out Sigma, out Xh, out Lambda, out Time, out _ILArray_double, out _ILArray_double, out _ILArray_double, PWF, 1, LambdaInf); Res = __[TimePlot[_(Time)], PWF[_(Time)], Xh, Sigma, Lambda]; Res = __[nan(Time._(1) - 1, size(Res, 2)), ';', Res]; // Notice that length of Time is shorter than length of T if batch RLS // start is used so TPSEst < TPS in that case TPSEst = min_(find(_dbl(Time) * Ts >= TPredict)); // Use time from estimates // Parameter values must be taken for index TPSEst A = Theta._(TPSEst, 1); if (abs(A) > 1) { warning(__["TK: Unstable pole, max(abs(eig)): ", num2str(abs(A))]); } B = Theta._(TPSEst, 2); xhms = zeros(NS - TPS, 1); covxhms = zeros(NS - TPS, 1); xhms._(1, '=', Xh._(TPSEst + 1)); covxhms._(1, '=', Sigma._(TPSEst + 1)); aux = Sigma._(TPSEst + 1); for (i = 2; i <= NS - TPS; i++) { xhms._(i, '=', A * xhms._(i - 1) + B); aux = A * aux * A.T(); covxhms._(i, '=', covxhms._(i - 1) + aux); } // Prepend xhms with the present measurement so the plot clearly indicates // the time for the measurement } else { // Persistence version; // Initialization Lambda = Lambda0; q = q0; xh = PWF._(1); Res = __[T._(1), PWF._(1), xh, q, Lambda0]; // Recursive estimation of incremental covariance for (i = 2; i <= NS; i++) { xt = PWF._(i) - xh; dt = T._(i) - T._(i - 1); q = _m(Lambda, '*', q) + (1 - Lambda) * _p(xt, 2) / dt; Res = __[Res, ';', __[T._(i), PWF._(i), xh, q, Lambda]]; Lambda = LambdaL * Lambda + (1 - LambdaL) * LambdaInf; xh = PWF._(i); } Res[_(':'), _(1)] = Res[_(':'), _(1)] * TimeScaling; // Persistence version; xhms = Res._(TPS + 1, 3) * ones(NS - TPS, 1); covxhms = Res._(TPS + 1, 4) * Ts * ((_c(1, NS - TPS)).T); } if (IMT != 0) { xhms = __[PWF._(TPS), ';', xhms]; covxhms = __[0, ';', covxhms]; } sigmaxhms = sqrt(covxhms); ConIntAWFP = _m(xhms, '*', __[1, 1, 1]) + _m(sigmaxhms, '*', __[-2, 0, 2]); ConIntAWFP = min(max(ConIntAWFP, MinWFP), NomWFP); // Limit confidence limits // Plot results // Plot actual as black solid and lower, prediction and upper confidence // limits as red, green and blue solid for 10 min, dashed for one hour and // dotted for the rest. //figure; NS10Min = min(NS - TPS - 1, round_(600.0 / Ts)); NSOneHour = min(NS - TPS - 1, round_(3600.0 / Ts)); //set(gcf, "defaultaxescolororder", ILMath.eye(3, 3)); outMethod = Method; outTime = TimePlot; outX = Res[_(':'), _(2)]; outXhmsAll = ConIntAWFP; outXhmsAllTimeOffset = (TPS - IMT); outXhmsLLength = (NS10Min + IMT); outXhmsUOffset = (NSOneHour + IMT); //plot( // TimePlot, Res[ILMath.full, 2 - 1], 'k',///... // TimePlot[ILMath.r(TPS + 1 - IMT - 1, TPS + NS10Min - 1)], ConIntAWFP[ILMath.r(1 - 1, NS10Min + IMT - 1), ILMath.full],//... // TimePlot[ILMath.r(TPS + NS10Min + 1 - 1, TPS + NSOneHour - 1)], ConIntAWFP[_a(NS10Min + 1, 1, NSOneHour) + IMT - 1, ILMath.full], "--",//... // TimePlot[ILMath.r(TPS + NSOneHour + 1 - 1, ILMath.end)], ConIntAWFP[ILMath.r(NSOneHour + 1 + IMT - 1, ILMath.end), ILMath.full], ':'); //title(TitleStr); //Legend= {'x' 'xhmsL' 'xhms' 'xhmsU'}; //legend(Legend); //grid('on'); }
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // DESCRIPTION: // RLC - 8/9/2014, interpolation, for getting an accurate CP/CT value. The // reason for a standalone script, is that the built in MATLAB script // (interp) includes a lot of redundancy checks that are not necessary, and // not feasible for embedded solutions. // Based on the NREL5MW Turbine. // Uses Linear Polynomial Extrapolation, to get a value for CP, given a Beta // (Revolutional speed) and Lambda (Tip-Speed-Ratio) of the Turbine. // The interpolation is computed as: // y = y0 + (y1 - y0)*(x-x0)/(x1-x0) // // Beta is the revolutional entry. // Lambda is the TSR entry ratio. // turbineTable defines the table to lookup in. // negYes defines wether the CP value should be allowed to be negative. //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #endregion internal static void interpTable(out double interpValue, double Beta, double Lambda, ILArray<double> table, ILArray<double> turbineTableBeta, ILArray<double> turbineTableLambda, bool negYes) { #region "Used variables declaration" ILArray<double> turbineTable; int sizeBt; int sizeLa; int Bt0; int Bt1; int La0; int La1; ILArray<double> tableLookup; ILArray<double> lambdaIntervals; double betaIntervals; #endregion //% Setup, loads the table, and stores it. //persistent turbineTable tableLoad //if isempty(tableLoad) turbineTable = table.C; // tableLoad = 1; //end size(out sizeBt, out sizeLa, turbineTable); //% Index Interpolation // Finds the beta-point of the interpolation. min(out Bt0, abs(turbineTableBeta - Beta)); // Determines the index of the closest point. if (Beta > turbineTableBeta._(Bt0)) { if (Bt0 == sizeBt) //length(turbineTableBeta) { Bt1 = Bt0; Bt0 = Bt0 - 1; } else { Bt1 = Bt0 + 1; } } else { if (Bt0 == 1) { Bt1 = Bt0 + 1; } else { Bt1 = Bt0; Bt0 = Bt1 - 1; } } // Finds the Lambda-point of the interpolation. min(out La0, abs(turbineTableLambda - Lambda)); // Determines the index of the closest point. if (Lambda > turbineTableLambda._(La0)) if (La0 == sizeLa) //length(turbineTableLambda) { La1 = La0; La0 = La1 - 1; } else { La1 = La0 + 1; } else { if (La0 == 1) { La1 = La0 + 1; } else { La1 = La0; La0 = La1 - 1; } } //% Table Interpolation // Table lookup using indexes obtained previously: tableLookup = __[ turbineTable._(Bt0, La0), turbineTable._(Bt0, La1), ';', turbineTable._(Bt1, La0), turbineTable._(Bt1, La1) ]; // Interpolating, using the Lambda values first, then the Betas. lambdaIntervals = __[ ( (tableLookup._(1, 2) - tableLookup._(1, 1)) / (turbineTableLambda._(La1) - turbineTableLambda._(La0)) ) * (Lambda - turbineTableLambda._(La0)) + tableLookup._(1, 1), ';', ( (tableLookup._(2, 2) - tableLookup._(2, 1)) / (turbineTableLambda._(La1) - turbineTableLambda._(La0)) ) * (Lambda - turbineTableLambda._(La0)) + tableLookup._(1, 2) ]; // Interpolation, using the Beta values (using the intervales computed above). betaIntervals = ( (lambdaIntervals._(2) - lambdaIntervals._(1)) / (turbineTableBeta._(Bt1) - turbineTableBeta._(Bt0)) ) * (Beta - turbineTableBeta._(Bt0)) + lambdaIntervals._(1); //% Negativity Handling // If the negYes value is set as true in the system, the functino will only // give an absolute value of Ct and Cp - have to be modified so it does this // correctly. if (negYes) { interpValue = 0; } else { interpValue = betaIntervals; } }
private static void RLSMARX1(out ILArray <double> Theta, out ILArray <double> Sigma, out ILArray <double> Xh, out ILArray <double> Lambda, out ILArray <int> Time, out ILArray <double> AByTime, out ILArray <double> BByTime, out ILArray <double> SigmaByTime, ILArray <double> X, ILArray <double> U = null, ILArray <double> lambda = null, ILArray <double> theta0 = null, ILArray <double> sigma0 = null, int Plot = PlotDef) { #region "Original function comments" //RLS calculates recursive least squares parameter estimates corresponding // to the output input data X U and the model x(n+1)= A*x(n)+B*u(n)+e(n) // <=> x(n+1)'= [x(n)' u(n)']*[A'; B']+e(n) // Duble exponential forgetting can be used where lamba increases // exponentielly from lamba0 to lambda. // // [Theta,Sigma,Xh,Lambda,Time]= RLSMARX1(X,U,lambda,theta0,sigma0) // // X: Output Matrix with size number of samples * number of channels // U: Input Matrix with size number of samples * number of channels // zero scalar/matrix corresponds to no input, ones corresponds to estimating // a mean value parameter. (default ones) // lambda : Forgetting factor (default 0.99) if lambda= [lambda lambda0] // the forgetting factor increases exponentially from lambda0 // to lambda. // theta0 : Initial parameter estimate. Notice that theta0= [A0';B0']; // sigma0 : Initial covvariance estimate. // If no initial parameters are specifyed a offline/batch // estimate is used for the start. // Plot : If 1/true plot informative plots (default: false) // // External input: // Time-stamp: <2014-10-17 11:44:27 tk> // Version 1: 2014-10-01 app. // Version 2: 2014-10-02 12:53:07 tk Included LS/offline/batch startup // Version 3: 2014-10-07 13:57:54 tk Included additional output and // plotting // Torben Knudsen // Aalborg University, Dept. of Electronic Systems, Section of Automation // and Control // E-mail: [email protected] #endregion #region "Used variables declaration" double TauLambdaLRel; double lambdaInf; double lambda0; double IniNumSampFrac; int N; int n; int m; double TauLambdaL; double lambdaL; ILArray <double> sigma; ILArray <double> theta; int NIni; ILArray <double> XX; ILArray <double> YY; ILArray <double> R; ILArray <double> Epsilon; ILArray <double> xh; ILArray <double> Res; int i; ILArray <double> phi; ILArray <double> epsilon; #endregion //% setting up inputs //UDef = 1; //lambdaDef = 0.99; //theta0Def = 0; //sigma0Def = 0; //PlotDef = 0; //if (nargin < 6) { Plot = []; } if (sigma0 == null) { sigma0 = __[' ']; } if (theta0 == null) { theta0 = __[' ']; } if (lambda == null) { lambda = __[' ']; } if (U == null) { U = __[' ']; } //if (nargin < 1) { error('Error TK: To few input arguments'); } if (isempty(U)) { U = UDef; } //if (isempty(Plot)) { Plot = PlotDef; } if (isempty(lambda)) { lambda = lambdaDef; } if (isempty(theta0)) { theta0 = theta0Def; } if (isempty(sigma0)) { sigma0 = sigma0Def; } //% Parameters TauLambdaLRel = 0.1; // TauLambdaL= 10% of the samples lambdaInf = lambda._(1); if (length(lambda) > 1) // Initial lambda; { lambda0 = lambda._(2); } else { lambda0 = lambdaInf; } IniNumSampFrac = 0.1; // Number of samples to use for init //% Definitions etc. N = size(X, 1); n = size(X, 2); m = size(U, 2); // Calculate Lamba* from TauLambda* assuming Ts= 1 TauLambdaL = TauLambdaLRel * N; lambdaL = exp(-1.0 / TauLambdaL); if (all(U == 1)) { U = ones(N, 1); } //% Algorithm // Initialisation if (all(all(sigma0 == 0))) // No sigma0 specifyed { sigma = 0 * eye(n); } else { sigma = sigma0; } if (all(all(theta0 == 0))) // No sigma0 specifyed { theta = zeros(n + m, n); } else { theta = theta0; } // Start with a offline estimate if initial parameters are not specifyed if (all(__[theta0, ';', sigma0] == 0)) { NIni = max(n + 1, ceil_(N * IniNumSampFrac)); XX = __[X[_(1, ':', NIni - 1), _(':')], U[_(1, ':', NIni - 1), _(':')]]; YY = X[_(_c_(1, NIni - 1) + 1), _(':')]; R = _m(XX.T, '*', XX); theta = _m(_m(inv(R), '*', (XX.T)), '*', YY); Epsilon = YY - _m(XX, '*', theta); sigma = _m(Epsilon.T, '*', Epsilon) / NIni; xh = _m(XX[_(end), _(':')], '*', theta); } else { NIni = 1; xh = X[_(1), _(':')]; R = 1e6 * eye(n); } lambda = lambda0; Res = __[NIni, (theta[_(':')]).T, (sigma[_(':')]).T, xh, lambda]; AByTime = zeros(n, n, N - NIni + 1); AByTime[_(':'), _(':'), _(1)] = (theta[_(1, ':', n), _(1, ':', n)]).T; BByTime = zeros(n, m, N - NIni + 1); BByTime[_(':'), _(':'), _(1)] = (theta[_(n + 1, ':', n + m), _(1, ':', n)]).T; SigmaByTime = zeros(n, n, N - NIni + 1); SigmaByTime[_(':'), _(':'), _(1)] = sigma; // Recursion // Model x(n+1)= A*x(n)+B*u(n)+e(n) <=> // x(n+1)'= [x(n)' u(n)']*[A'; B']+e(n) for (i = NIni + 1; i <= N; i++) { phi = (__[X[_(i - 1), _(':')], U[_(i - 1), _(':')]]).T; // phi(i) R = (lambda._Scalar()) * R + _m(phi, '*', (phi.T)); // R(i) xh = _m((phi.T), '*', theta); // xh(i) epsilon = X[_(i), _(':')] - xh; // epsilon(i) theta = theta + _m(_s(R, '\\', phi), '*', epsilon); // theta(i) sigma = _m(lambda, '*', sigma) + _m(_m((1 - lambda), '*', (epsilon.T)), '*', epsilon); Res = __[Res, ';', __[i, theta[_(':')].T, sigma[_(':')].T, xh, lambda]]; lambda = lambdaL * lambda + (1 - lambdaL) * lambdaInf; AByTime[_(':'), _(':'), _(i - NIni + 1)] = (theta[_(1, ':', n), _(1, ':', n)]).T; // Index start at 2 BByTime[_(':'), _(':'), _(i - NIni + 1)] = (theta[_(n + 1, ':', n + m), _(1, ':', n)]).T; SigmaByTime[_(':'), _(':'), _(i - NIni + 1)] = sigma; } // Res is organised like // i theta(:)' sigma(:)' xh lambda with the dimensions // 1 (n+m)*n n^2 n 1 Time = _int(Res[_(':'), _(1)]); Theta = Res[_(':'), _(1 + 1, ':', 1 + (n + m) * n)]; Sigma = Res[_(':'), _(1 + 1 + (n + m) * n, ':', 1 + (n + m) * n + _p_(n, 2))]; Xh = Res[_(':'), _(1 + 1 + (n + m) * n + _p_(n, 2), ':', 1 + (n + m) * n + _p_(n, 2) + n)]; Lambda = Res[_(':'), _(1 + 1 + (n + m) * n + _p_(n, 2) + n, ':', end)]; // Do informative plotting and output //if (Plot) //{ // figure; // plot([U X]); // title('Input and measurements'); // figure; // plot(Time,reshape(AByTime,n*n,N-NIni+1)'); // title('A parameters'); // figure; // plot(Time,reshape(BByTime,n*m,N-NIni+1)'); // title('B parameters'); // figure; // plot(Time,reshape(SigmaByTime,n*n,N-NIni+1)'); // title('Sigma parameters'); // figure; // plot(Time,Lambda); // title('Lambda'); // Epsilon= X(Time,:)-Xh; // Epsilon= Epsilon(100:end,:); // figure; // XCorrtkAll(Epsilon); // figure; // normplot(Epsilon); // gridtk('on',figures); // Lab= ['Final parameters A n*n B n*m Sigma n*n']; // RowLab= {''}; // ColLab= {''}; // Res= [AByTime(:,:,end) BByTime(:,:,end) SigmaByTime(:,:,end)]; // printmattk(Res,Lab); //} }
/// <summary> /// (i1,i2) = /// </summary> /// <param name="equalitySign">'='</param> public static void _(this ILArray <double> ilArray, int index1, int index2, char equalitySign, double value) { ilArray._ <double>(index1, index2, equalitySign, value); }
// Wake Simulation // (C) Rasmus Christensen // Control and Automation, Aalborg University 2014 // Compute the velocity in front of each wind-turbine, with respect to the wind input. #endregion internal static void Compute_Vell(out double[,] vel_output, ILArray<double> yTurb, ILArray<int> xTurbC, ILArray<int> yTurbC, ILArray<double> x, ILArray<double> wField, double[] Uhub, double kWake, int iMax, int jMax, int nTurb, double dTurb, ILArray<double> Ct, double dy) { #region "Used variables declaration" double[,] vell_i; ILArray<double> shadow; double r0; double nk; int k; int J; double SS; double SS0; int i; double RR_i; double Dij; double Alpha_i; double Alpha_k; double Area; int ii; double rrt; double nj; int jjMin; int jjMax; #endregion //vell_i = wField.C; vell_i = new double[wField.Size[0], wField.Size[1]]; for (var i0 = 0; i0 < vell_i.GetLength(0); i0++) { for (var i1 = 0; i1 < vell_i.GetLength(1); i1++) { vell_i[i0, i1] = wField.GetValue(i0, i1); } } shadow = zeros(1, nTurb); r0 = 0.5 * dTurb; nk = 2 * ceil(dTurb / dy); for (k = 1; k <= nTurb; k++) { J = 0; SS = 0; SS0 = pi * r0 * r0; for (i = 1; i <= k - 1; i++) { RR_i = r0 + kWake * (x._(xTurbC._(k)) - x._(xTurbC._(i))); Dij = abs(yTurb._(i) - yTurb._(k)); if ((RR_i >= (r0 + Dij)) || (Dij <= dy)) { SS = SS + ((r0 * r0) / (RR_i * RR_i)); } else { if (RR_i >= (r0 + Dij) && Dij > dy) { J = J + 1; Alpha_i = acos((RR_i * RR_i) + (Dij * Dij) - (r0 * r0) / (2 * RR_i * Dij)); Alpha_k = acos(((r0 * r0) + (Dij * Dij) - (RR_i * RR_i)) / (2 * r0 * Dij)); AArea(out Area, RR_i, r0, Dij); shadow._(J, '=', (Alpha_i * (RR_i * RR_i) + Alpha_k * (r0 * r0)) - 2 * Area); SS = SS + ((shadow._(J)) / SS0) * ((r0 * r0) / (RR_i * RR_i)); } } } for (ii = xTurbC._(k); ii <= iMax; ii++) { rrt = r0 + kWake * (x._(ii) - x._(xTurbC._(k))); nj = ceil(rrt / dy); jjMin = floor_(max(1, yTurbC._(k) - nj)); jjMax = ceil_(min(jMax, yTurbC._(k) + nj)); for (var j = jjMin; j <= jjMax; j++) { if (((-vell_i[ii - 1, j - 1] + Uhub[k - 1]) > 0) && (ii > xTurbC._(k) + nk)) { vell_i[ii - 1, j - 1] = min(vell_i[ii - 2, j - 1], Uhub[k - 1] + Uhub[k - 1] * (sqrt(1 - Ct._(k)) - 1) * ((r0 * r0) / (rrt * rrt)) * (1 - (1 - sqrt(1 - Ct._(k))) * SS)); vell_i[ii - 1, j - 1] = max(0, vell_i[ii - 1, j - 1]); } else { vell_i[ii - 1, j - 1] = (Uhub[k - 1] + Uhub[k - 1] * (sqrt(1 - Ct._(k)) - 1) * (r0 / rrt) * (r0 / rrt)) * (1 - (1 - sqrt(1 - Ct._(k))) * SS); vell_i[ii - 1, j - 1] = max(0, vell_i[ii - 1, j - 1]); } } } } vel_output = vell_i; }
/// <summary> /// = (i) /// </summary> public static int _(this ILArray <int> ilArray, int index) { return(ilArray._ <int>(index)); }
/// <summary> /// (i) = /// </summary> /// <param name="equalitySign">'='</param> public static void _(this ILArray <int> ilArray, int index, char equalitySign, int value) { ilArray._ <int>(index, equalitySign, value); }
/// <summary> /// = (i) /// </summary> public static double _(this ILArray <double> ilArray, int index) { return(ilArray._ <double>(index)); }