├── .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 | 
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
--------------------------------------------------------------------------------