├── .gitattributes ├── FNN_100eph_0.005LR.mat ├── README.md ├── lstm_15-min_nnet.mat ├── lstm_45-min_nnet.mat ├── main.m ├── trainingVariables.mat ├── variables_15.mat └── variables_45.mat /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /FNN_100eph_0.005LR.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nesasio/Frequency-and-Power-Prediction-System/cc80e2d70da2a19f3a7412133ca82dcd529f6a4f/FNN_100eph_0.005LR.mat -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frequency and Power Prediction System 2 | Frequency and Power Prediction System is a forecasting tool made with [MATLAB](https://www.mathworks.com/products/matlab.html) to predict frequency of the electrical grid and the scheduled power generation of the power plant. 3 | The real time frequency data, scheduled power data and actual power data is extracted from [UP load despatch centre](https://www.upsldc.org/real-time-data). 4 | 5 | ![Image](https://github.com/Nesasio/Scheduled-Power-Prediction-System/assets/110229836/1580873a-3168-4ea5-afea-554567a9e657) 6 | *Prediction System User Interface* 7 | 8 | In context of a thermal power plant, scheduled power is the power generation which is to be maintained by the plant in accordance with the power demand, costs and various other factors. This scheduled power value for a particular time in a day is assigned to the power plant around 2 blocks in advance. A block in this context is a period of 15 minutes starting from 0000 hours, meaning the block number is 1 from 0000 to 0015 hours on any particular day. This prediction tool therefore, predicts the scheduled power value of the next block and the n+3rd block to aid in better power management of the power plant. 9 | 10 | The real time data updates every 15 minutes and this system takes a few data points before starting predictions. As soon as few data points are collected, the prediction will start and the system will plot the predicted values of the next block and the n+3rd block every 15 minutes. 11 | 12 | ## User Interface 13 | The user interface displays an interactive table which shows the values of frequency, power data and block number along with the predicted suggestion for the next block which updates in real time every 15 minutes. 14 | The real time graphs are also displayed which shows current as well as predicted values. Graphs can be cycled between power data and frequency data from the tab group. 15 | The current value and the immediate next prediction values are shown on top. -------------------------------------------------------------------------------- /lstm_15-min_nnet.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nesasio/Frequency-and-Power-Prediction-System/cc80e2d70da2a19f3a7412133ca82dcd529f6a4f/lstm_15-min_nnet.mat -------------------------------------------------------------------------------- /lstm_45-min_nnet.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nesasio/Frequency-and-Power-Prediction-System/cc80e2d70da2a19f3a7412133ca82dcd529f6a4f/lstm_45-min_nnet.mat -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | % Driver Code 2 | 3 | % --------------------------------------------------------------------------------- 4 | % Loading the system variables 5 | 6 | % Variables for 15 minutes prediction 7 | load variables_15.mat; 8 | 9 | % Variables for 45 minutes prediction 10 | load variables_45.mat; 11 | 12 | % Variables for frequency prediction 13 | load trainingVariables.mat 14 | 15 | % --------------------------------------------------------------------------------- 16 | 17 | powerUpperLimit = 1648; % Upper limit of the power supplied in MegaWatts 18 | history = 4; % Number of datapoints it takes to start the prediction 19 | dataTest = zeros(history, 3); 20 | numPred = 3; % Number of future predictions to be done in each step 21 | 22 | % Loading the trained Neural Networks 23 | load lstm_15-min_nnet.mat; 24 | load lstm_45-min_nnet.mat; 25 | load FNN_100eph_0.005LR.mat; 26 | 27 | tableRowLimit = 10; % Maximum number of rows the table will display 28 | valueCounter = 0; 29 | stepSize = 50; % Maximum deviation to be considered negligible 30 | runTime = 2*24*60*60; % Hours * 60 * 60 seconds 31 | intervalDuration = 15*60; % minutes * 60 seconds (Duration between collecting 2 values) 32 | 33 | % =================================================== 34 | % Creating User Interface 35 | 36 | % Setting up variables 37 | TimeStamp = []; 38 | Block = []; 39 | Frequency = []; 40 | PredictedFrequency = []; 41 | ScheduledGeneration = []; 42 | ActualGeneration = []; 43 | Prediction_15 = []; 44 | Prediction_45 = []; 45 | Prediction_S = []; 46 | 47 | % Variable Names 48 | varNames = ["Time Stamp", "Block", "Frequency", "Frequency Prediction", "Scheduled Generation", "Actual Generation", "(n+1) Prediction", "(n+3) Prediction", "Predicted Suggestion"]; 49 | 50 | % Creating UI 51 | f = uifigure("Name", "Prediction System", "Position", [20 20 1800 900]); 52 | tabgp = uitabgroup(f, 'Position', [10 380 1780 500]); 53 | tab1 = uitab(tabgp, 'Title', 'Frequency'); 54 | tab2 = uitab(tabgp, 'Title', 'Scheduled Power'); 55 | 56 | uit = uitable(f, 'Position',[10 10 1780 360]); 57 | 58 | % Scheduled Power Plot 59 | a = uiaxes(tab2, 'Position', [10 10 1760 350]); 60 | title(a, 'Power Data'); 61 | xlabel(a, 'Time Stamp', 'FontSize', 12); 62 | ylabel(a, 'Scheduled Power (MW)', 'FontSize', 12); 63 | grid(a, 'on'); 64 | 65 | % Frequency Plot 66 | af = uiaxes(tab1, 'Position', [10 10 1760 350]); 67 | title(af, 'Frequency Data'); 68 | xlabel(af, 'Time Stamp', 'FontSize', 12); 69 | ylabel(af, 'Frequency (Hz)', 'FontSize', 12); 70 | grid(af, 'on'); 71 | 72 | lbl1 = uilabel(tab2, 'Position', [20 380 500 50]); 73 | lbl1.Text = "Current SG:"; 74 | lbl1.Interpreter = "html"; 75 | 76 | lbl2 = uilabel(tab2, 'Position', [450 380 600 50]); 77 | lbl2.Text = " N+3rd Block Prediction:"; 78 | lbl2.Interpreter = "html"; 79 | 80 | lbl3 = uilabel(tab2, 'Position', [210 382 200 50]); 81 | lbl3.FontSize = 34; 82 | lbl3.FontColor = [0 0.5 0]; 83 | lbl3.FontWeight = 'bold'; 84 | lbl3.Text = "NaN"; 85 | 86 | lbl4 = uilabel(tab2, 'Position', [830 382 400 50]); 87 | lbl4.FontSize = 34; 88 | lbl4.FontColor = [1 0 0]; 89 | lbl4.FontWeight = 'bold'; 90 | lbl4.Text = "NaN"; 91 | 92 | lbl5 = uilabel(tab1, 'Position', [1170 380 600 50]); 93 | lbl5.Text = "Current Block:"; 94 | lbl5.Interpreter = "html"; 95 | 96 | lbl6 = uilabel(tab1, 'Position', [1520 382 200 50]); 97 | lbl6.FontSize = 34; 98 | lbl6.FontColor = [0 0 1]; 99 | lbl6.FontWeight = 'bold'; 100 | lbl6.Text = "NaN"; 101 | 102 | lbl7 = uilabel(tab1, 'Position', [20 380 500 50]); 103 | lbl7.Text = "Frequency:"; 104 | lbl7.Interpreter = "html"; 105 | 106 | lbl8 = uilabel(tab1, 'Position', [460 380 600 50]); 107 | lbl8.Text = "Next Block Prediction:"; 108 | lbl8.Interpreter = "html"; 109 | 110 | lbl9 = uilabel(tab1, 'Position', [220 382 200 50]); 111 | lbl9.FontSize = 34; 112 | lbl9.FontColor = [0 0.5 0]; 113 | lbl9.FontWeight = 'bold'; 114 | lbl9.Text = "NaN"; 115 | 116 | lbl10 = uilabel(tab1, 'Position', [810 382 200 50]); 117 | lbl10.FontSize = 34; 118 | lbl10.FontColor = [1 0 0]; 119 | lbl10.FontWeight = 'bold'; 120 | lbl10.Text = "NaN"; 121 | 122 | lbl11 = uilabel(tab2, 'Position', [1170 380 600 50]); 123 | lbl11.Text = "Current Block:"; 124 | lbl11.Interpreter = "html"; 125 | 126 | lbl11 = uilabel(tab2, 'Position', [1520 382 200 50]); 127 | lbl11.FontSize = 34; 128 | lbl11.FontColor = [0 0 1]; 129 | lbl11.FontWeight = 'bold'; 130 | lbl11.Text = "NaN"; 131 | 132 | 133 | % =================================================== 134 | 135 | % Setting the URLs to fetch data in real time 136 | urlF = 'https://www.upsldc.org/real-time-data?p_p_id=upgenerationsummary_WAR_UPSLDCDynamicDisplayportlet&p_p_lifecycle=2&p_p_state=normal&p_p_mode=view&p_p_resource_id=realtimedata&p_p_cacheability=cacheLevelPage&p_p_col_id=column-1&p_p_col_count=1&_upgenerationsummary_WAR_UPSLDCDynamicDisplayportlet_time=1680234586750&_upgenerationsummary_WAR_UPSLDCDynamicDisplayportlet_cmd=realtimedata'; 137 | urlP = 'https://www.upsldc.org/real-time-data?p_p_id=upgenerationsummary_WAR_UPSLDCDynamicDisplayportlet&p_p_lifecycle=2&p_p_state=normal&p_p_mode=view&p_p_resource_id=unitwisedata&p_p_cacheability=cacheLevelPage&p_p_col_id=column-1&p_p_col_count=1&_upgenerationsummary_WAR_UPSLDCDynamicDisplayportlet_cmd=unitwisedata'; 138 | options = weboptions('Timeout', 150); 139 | 140 | % ---------------------------------------------------------------------------------- 141 | 142 | % Initializing variables 143 | prevFreqData = 0; 144 | tempSG = 0; 145 | tempAG = 0; 146 | 147 | % Mainloop 148 | tic; % Starting stopwatch 149 | while toc < runTime 150 | 151 | % Reading web data 152 | urlDataF = webread(urlF, options); 153 | urlDataP = webread(urlP, options); 154 | 155 | % ---------------------------------------------------------------------- 156 | 157 | % Extracting Frequency 158 | pat1 = '{\"point_id\":\"MUPS.SCADA02.00102650\",\"point_desc\":\"NR FREQUENCY\",\"time_val\"'; 159 | pat2 = ',\"current_revision\":0,\"max_demand\":0.0,\"min_demand\":0.0}"}, {"daynamic_obj":"{\"point_id\":\"MUPS.SCADA02.00113671\"'; 160 | list1 = extractBetween(urlDataF, pat1, pat2); 161 | 162 | webDatetime = extractBetween(list1,':\"', '\",\"point_val'); 163 | freq = extractAfter(list1,'\",\"point_val\":'); 164 | 165 | pat3 = '"BARA\",\"unit_id\":207,\"unit_name\":\"Unit 3\",\"capacity\":610,\"scada_point_id\":\"MUPS.SCADA02.00053725\",\"point_val\":'; 166 | pat4 = ',\"discom_sg_wl\":0}"}, {"daynamic_obj":"{\"time_block\":0,\"gen_id\":0,\"actual_sch\":0,\"version\":0.0,\"gen_cat\":\"CGS\",\"gen_name\":\"BARA SUIL'; 167 | list2 = extractBetween(urlDataP, pat3, pat4); 168 | 169 | % Extracting Scheduled Generation 170 | pat5 = ',\"discom_sg_wl\":0}"}, {"daynamic_obj":"{\"time_block\":0,\"gen_id\":0,\"actual_sch\":'; 171 | pat6 = ',\"version\":0.0,\"gen_cat\":\"IPP\",\"unit_id\":0,\"unit_name\":\"Total\",\"capacity\":0,\"point_val\":'; 172 | sch = extractBetween(list2, pat5, pat6); 173 | 174 | % Extracting Actual Generation 175 | pat7 = ',\"subCatCount\":3,\"actual_sch_dc\":'; 176 | act = extractBetween(list2, pat6, pat7); 177 | 178 | % --------------------------------------------------------------------- 179 | 180 | % Current time stamp 181 | currentDateTime = datetime('now'); 182 | pred_15_Time = currentDateTime + seconds(intervalDuration); 183 | pred_45_Time = currentDateTime + 3*seconds(intervalDuration); 184 | 185 | % Minutes passed since midnight 186 | currentDate = dateshift(currentDateTime, 'start', 'day'); 187 | minutesPassed = minutes(currentDateTime - currentDate); 188 | 189 | % Current Block 190 | currentBlock = (int64(floor(minutesPassed/15)) + 1); 191 | blockText = sprintf('%d', currentBlock); 192 | lbl6.Text = blockText; 193 | lbl11.Text = blockText; 194 | 195 | % ----------------------------------------------------------------------- 196 | 197 | if ~isempty(freq) 198 | disp(webDatetime{1}); 199 | currFreq = str2double(freq{1}); % Current frequency value 200 | 201 | % ---------------------------------------------------------------- 202 | 203 | if (currFreq ~= prevFreqData) 204 | if isempty(sch) 205 | finalSch = tempSG; 206 | else 207 | finalSch = str2double(sch{1}); 208 | end 209 | 210 | if isempty(act) 211 | finalAct = tempAG; 212 | else 213 | finalAct = str2double(act{1}); 214 | end 215 | 216 | finalFreq = str2double(freq{1}); 217 | 218 | if valueCounter < history 219 | dataTest(valueCounter+1, :) = [finalFreq finalSch finalAct]; 220 | 221 | TimeStamp = [TimeStamp; currentDateTime]; 222 | Block = [Block; currentBlock]; 223 | Frequency = [Frequency; finalFreq]; 224 | PredictedFrequency = [PredictedFrequency; "NaN"]; 225 | ScheduledGeneration = [ScheduledGeneration; finalSch]; 226 | ActualGeneration = [ActualGeneration; int64(finalAct)]; 227 | Prediction_15 = [Prediction_15; "NaN"]; 228 | Prediction_45 = [Prediction_45; "NaN"]; 229 | Prediction_S = [Prediction_S; "NaN"]; 230 | 231 | % Creating table 232 | DataTable = table(TimeStamp, Block, Frequency, PredictedFrequency, ScheduledGeneration, ActualGeneration, Prediction_15, Prediction_45, Prediction_S, 'VariableNames', varNames); 233 | 234 | % Displaying the current prediction values on labels 235 | currentVal = sprintf('%d' + " MW", finalSch); 236 | lbl3.Text = currentVal; 237 | lbl4.Text = "Analysing..."; 238 | cf = sprintf('%.4f' + " Hz", finalFreq(end)); 239 | lbl9.Text = cf; 240 | lbl10.Text = "Analysing..."; 241 | 242 | else 243 | dataTest(1:end-1, :) = dataTest(2:end, :); 244 | dataTest(end, :) = [finalFreq finalSch finalAct]; 245 | 246 | X_15 = (dataTest(1:end, :) - muX_15')./sigmaX_15'; 247 | X_45 = (dataTest(1:end, :) - muX_45')./sigmaX_45'; 248 | X_f = (dataTest(1:end, :) - muX)./sigmaX; 249 | 250 | [net_15, Yp_15] = predictAndUpdateState(net_15, X_15', 'SequencePaddingDirection', 'left'); 251 | [net_45, Yp_45] = predictAndUpdateState(net_45, X_45', 'SequencePaddingDirection', 'left'); 252 | [net, Yp_f] = predictAndUpdateState(net, X_f', 'SequencePaddingDirection', 'left'); 253 | 254 | 255 | % ---------------------------------------------------------------------------- 256 | % Smoothing the preditions 257 | deviation = abs((sigmaY_15*Yp_15(end) + muY_15) - finalSch); 258 | if deviation < stepSize 259 | Yp_15(end) = dataTest(end, 2); 260 | else 261 | Yp_15(end) = sigmaY_15*Yp_15(end) + muY_15; 262 | end 263 | 264 | deviation = abs((sigmaY_45*Yp_45(end) + muY_45) - finalSch); 265 | if deviation < stepSize 266 | Yp_45(end) = dataTest(end, 2); 267 | else 268 | Yp_45(end) = sigmaY_45*Yp_45(end) + muY_15; 269 | end 270 | 271 | % ---------------------------------------------------------------------------- 272 | % Power upper limit 273 | if Yp_15(end) > powerUpperLimit 274 | Yp_15(end) = powerUpperLimit; 275 | end 276 | 277 | if Yp_45(end) > powerUpperLimit 278 | Yp_45(end) = powerUpperLimit; 279 | end 280 | 281 | Yp_f(end) = sigmaY*Yp_f(end) + muY; 282 | 283 | % Setting the current predicted value to temp variable 284 | tempSG = finalSch; 285 | tempAG = finalAct; 286 | 287 | % Plotting power the values on graph 288 | hold(a, 'on'); 289 | plot(a, pred_15_Time, Yp_15(end), 'rx', 'MarkerSize', 10, 'LineWidth', 3); 290 | plot(a, pred_45_Time, Yp_45(end), 'blackd', 'MarkerSize', 10, 'LineWidth', 3); 291 | a.XLim = [(currentDateTime - seconds(10 * intervalDuration)) (currentDateTime + seconds(10 * intervalDuration))]; 292 | hold(a, 'off'); 293 | 294 | % Plotting frequency the values on graph 295 | hold(af, 'on'); 296 | plot(af, pred_15_Time, Yp_f(end), 'rx','MarkerSize', 10, 'LineWidth', 3); 297 | af.XLim = [(currentDateTime - seconds(10 * intervalDuration)) (currentDateTime + seconds(10 * intervalDuration))]; 298 | hold(af, 'off'); 299 | 300 | % -------------------------------------------------------------------------- 301 | % Suggesting changes in power 302 | 303 | if ((Yp_45(end) - finalSch) > -20) && ((Yp_45(end) - finalSch) < 20) 304 | Prediction_S = [Prediction_S; "No Change"]; 305 | elseif (Yp_45(end) - finalSch) >= 20 306 | Prediction_S = [Prediction_S; "Increase"]; 307 | else 308 | Prediction_S = [Prediction_S; "Decrease"]; 309 | end 310 | 311 | TimeStamp = [TimeStamp; currentDateTime]; 312 | Block = [Block; currentBlock]; 313 | Frequency = [Frequency; finalFreq]; 314 | PredictedFrequency = [PredictedFrequency; (Yp_f)]; 315 | ScheduledGeneration = [ScheduledGeneration; finalSch]; 316 | ActualGeneration = [ActualGeneration; int64(finalAct)]; 317 | Prediction_15 = [Prediction_15; int64(Yp_15(end))]; 318 | Prediction_45 = [Prediction_45; int64(Yp_45(end))]; 319 | 320 | DataTable = table(TimeStamp, Block, Frequency, PredictedFrequency, ScheduledGeneration, ActualGeneration, Prediction_15, Prediction_45, Prediction_S, 'VariableNames', varNames); 321 | 322 | % Limiting the number of rows in the table 323 | if size(DataTable, 1) > tableRowLimit 324 | DataTable = DataTable(end - tableRowLimit: end, :); 325 | end 326 | 327 | % Displaying the values on the labels 328 | currentVal = sprintf('%d' + " MW", finalSch); 329 | lbl3.Text = currentVal; 330 | 331 | predictedVal = sprintf('%d', int64(Yp_45(end))); 332 | lbl4.Text = predictedVal + " MW (" + Prediction_S(end) + ")"; 333 | lbl9.Text = sprintf('%.4f' + " Hz", finalFreq); 334 | lbl10.Text = sprintf('%.4f' + " Hz", Yp_f); 335 | 336 | 337 | end 338 | 339 | % Plotting the power values on the graph 340 | hold(a, 'on'); 341 | plot(a, currentDateTime, finalSch, 'bo', 'MarkerSize', 10, 'LineWidth', 3); 342 | a.XLim = [(currentDateTime - seconds(10 * intervalDuration)) (currentDateTime + seconds(10 * intervalDuration))]; 343 | a.YLim = [500 2000]; 344 | hold(a, 'off'); 345 | 346 | % Plotting the frequency values on the graph 347 | hold(af, 'on'); 348 | plot(af, currentDateTime, finalFreq, 'bo', 'MarkerSize', 10, 'LineWidth', 3); 349 | af.XLim = [(currentDateTime - seconds(10 * intervalDuration)) (currentDateTime + seconds(10 * intervalDuration))]; 350 | af.YLim = [49.6 50.4]; 351 | hold(af, 'off'); 352 | 353 | % Updating the variables 354 | prevFreqData = currFreq; 355 | valueCounter = valueCounter + 1; 356 | 357 | % Printing values to table 358 | uit.Data = DataTable; 359 | 360 | % ------------------------------------------------------------------------------------------------- 361 | % Adding colours to the table rows 362 | 363 | [decRow] = find(strcmp(DataTable.("Predicted Suggestion"), 'Decrease')); 364 | [incRow] = find(strcmp(DataTable.("Predicted Suggestion"), 'Increase')); 365 | [ncRow] = find(strcmp(DataTable.("Predicted Suggestion"), 'No Change')); 366 | rc1 = uistyle("BackgroundColor", [1 0.6 0.6]); 367 | rc2 = uistyle("BackgroundColor", [0.6 1 0.6]); 368 | 369 | if ~isempty([decRow]) 370 | addStyle(uit, rc1, "row", [decRow]); 371 | end 372 | 373 | if ~isempty([incRow]) 374 | addStyle(uit, rc1, "row", [incRow]); 375 | end 376 | 377 | if ~isempty([ncRow]) 378 | addStyle(uit, rc2, "row", [ncRow]); 379 | end 380 | 381 | % ------------------------------------------------------------------------------------------------------ 382 | 383 | drawnow; 384 | 385 | end 386 | 387 | % ---------------------------------------------------------------- 388 | 389 | end 390 | 391 | % ----------------------------------------------------------------------- 392 | 393 | pause(intervalDuration) 394 | end -------------------------------------------------------------------------------- /trainingVariables.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nesasio/Frequency-and-Power-Prediction-System/cc80e2d70da2a19f3a7412133ca82dcd529f6a4f/trainingVariables.mat -------------------------------------------------------------------------------- /variables_15.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nesasio/Frequency-and-Power-Prediction-System/cc80e2d70da2a19f3a7412133ca82dcd529f6a4f/variables_15.mat -------------------------------------------------------------------------------- /variables_45.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nesasio/Frequency-and-Power-Prediction-System/cc80e2d70da2a19f3a7412133ca82dcd529f6a4f/variables_45.mat --------------------------------------------------------------------------------