├── LICENSE ├── Montecarlo_nsq_single ├── case24_failrate.m ├── failprob.m ├── mc_sampling.m ├── mc_simulation.m └── nsqMain.m ├── Montecarlo_seq ├── anloducurve.m ├── calnlc.m ├── case24_loadprofile.m ├── dispaload.m ├── seqMain.m ├── seq_mcsampling.m ├── seq_mcsimulation.m └── seqmeantime.m └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tianyang Zhao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Montecarlo_nsq_single/case24_failrate.m: -------------------------------------------------------------------------------- 1 | function failrate = case24_failrate 2 | 3 | %% ---------------- ------------------Generation data-------------------------%% 4 | % Generator MTTF (hours) 5 | failrate.genmttf = [ 450 450 1960 1960 450 ... 6 | 450 1960 1960 1200 1200 ... 7 | 1200 950 950 950 10000 ... %10000 bus 14 synchronous compensator 8 | 2940 2940 2940 2940 2940 ... 9 | 960 960 1100 1100 1980 ... 10 | 1980 1980 1980 1980 1980 ... 11 | 960 960 1150 ]; 12 | 13 | % Generator MTTR (hours) 14 | failrate.genmttr = [ 50 50 40 40 50 ... 15 | 50 40 40 50 50 ... 16 | 50 50 50 50 0.1 ... %0.1 bus 14 synchronous compensator 17 | 60 60 60 60 60 ... 18 | 40 40 150 150 20 ... 19 | 20 20 20 20 20 ... 20 | 40 40 100 ]; 21 | 22 | % Generator force outage rate (1/yr) 23 | % failrate.genrate = failrate.genmttr ./ (failrate.genmttf + failrate.genmttr); 24 | 25 | % Generator scheduled maintainance weeks (weeks/yr) 26 | 27 | failrate.genweeks = [ 2 2 3 3 2 ... 28 | 2 3 3 3 3 ... 29 | 3 4 4 4 0.1 ... %0.1 bus 14 synchronous compensator 30 | 2 2 2 2 2 ... 31 | 4 4 6 6 2 ... 32 | 2 2 2 2 2 ... 33 | 4 4 5 ]; 34 | 35 | %% ---------------------------------Branch data-------------------------%% 36 | % Branch force outage rate (1/yr) 37 | failrate.brlambda = [ 0.24 0.51 0.33 0.39 0.48 0.38 ... 38 | 0.02 0.36 0.34 0.33 0.30 0.44 ... 39 | 0.44 0.02 0.02 0.02 0.02 0.40 ... 40 | 0.39 0.40 0.52 0.49 0.38 0.33 ... 41 | 0.41 0.41 0.41 0.35 0.34 0.32 ... 42 | 0.54 0.35 0.35 0.38 0.38 0.34 ... 43 | 0.34 0.45 ]; 44 | 45 | % Branch outage durations (hours) 46 | failrate.brdur = [ 16 10 10 10 10 768 10 10 35 10 10 10 ... 47 | 10 768 768 768 768 11 11 11 11 11 11 11 ... 48 | 11 11 11 11 11 11 11 11 11 11 11 11 ... 49 | 11 11]; 50 | 51 | % Branch repair rate (1/yr) 52 | % failrate.brmiu = 8760./failrate.brdur; 53 | %%----------------------------------End----------------------------------%% 54 | -------------------------------------------------------------------------------- /Montecarlo_nsq_single/failprob.m: -------------------------------------------------------------------------------- 1 | function totalprob = failprob() 2 | 3 | failrate = case24_failrate; 4 | %%----------------------------------Generator data----------------------------------%% 5 | % Compute the failure probability of generators 6 | 7 | probgen = failrate.genmttr ./ (failrate.genmttf + failrate.genmttr); 8 | 9 | %{ 10 | % when taking considerations of planned maintainances 11 | % componet of maintainance 12 | weekhours = 7*24; 13 | 14 | mttrp = failrate.genweeks * weekhours; 15 | genmiup = 8760./ mttrp; 16 | mttfp = 8760 - mttrp; 17 | genlambdap = 8760./mttfp; 18 | 19 | % compoent of forced outage 20 | genmiu = 8760./ failrate.genmttr; 21 | genlambda = 8760./ failrate.genmttf; 22 | 23 | % combination of two parts 24 | 25 | probgen = (genlambda * genmiup + genlambdap * genmiu) ./ ... 26 | (genlambda * genmiup + genlambdap * genmiu + genmiup * genmiu); 27 | %} 28 | 29 | %%----------------------------------Branch data----------------------------------%% 30 | 31 | brmiu = 8760 ./ failrate.brdur; 32 | probbr = failrate.brlambda ./ (failrate.brlambda + brmiu); 33 | 34 | %%----------------------------------Combination matrix----------------------------------%% 35 | 36 | totalprob = horzcat(probgen, probbr); 37 | 38 | return; 39 | %%----------------------------------End-------------------------------------------%% 40 | -------------------------------------------------------------------------------- /Montecarlo_nsq_single/mc_sampling.m: -------------------------------------------------------------------------------- 1 | function eqstatus = mc_sampling(totalprob, SIMUNIT, Ng, Nl) 2 | 3 | %% Compare random matrix with augmented failure probability matrix to get the equipment status 4 | %% 1 - failure, 0 - normal 5 | 6 | eqstatus = rand(SIMUNIT, Ng+Nl) < repmat(totalprob, SIMUNIT, 1); 7 | 8 | eqstatus(:, 15) = 0; % neglect the failure probability of synchronous compensator @ bus 14 9 | 10 | eqstatus = sparse(double(eqstatus)); 11 | 12 | return 13 | 14 | %%----------------------------------End-------------------------------------------%% 15 | 16 | 17 | -------------------------------------------------------------------------------- /Montecarlo_nsq_single/mc_simulation.m: -------------------------------------------------------------------------------- 1 | function dns = mc_simulation(eqstatus, Testsys, mpopt, Ng, Nl) 2 | 3 | %%----------------------------------Obtain EENS for each sample----------------------------------------------%% 4 | 5 | statusgen = eqstatus(:, 1:Ng); 6 | Testsys.gen(1:Ng, 8) = ~ statusgen'; 7 | 8 | statusbranch = eqstatus(:, Ng+1:Ng+Nl); 9 | Testsys.branch(1: Nl, 11) = ~ statusbranch'; 10 | %%-------------------------------Run OPF calculation------------------------------------------------------%% 11 | Result = runopf(Testsys, mpopt); 12 | dns = Result.f + Testsys.load; 13 | 14 | if dns < 0.1 15 | dns = 0; 16 | end 17 | 18 | return 19 | %%----------------------------------End----------------------------------------------%% 20 | 21 | 22 | -------------------------------------------------------------------------------- /Montecarlo_nsq_single/nsqMain.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matrixeigs/PowerSystemsReliabilityAssessment/2ac04ffda6c2d23e24a783190472a0de1b4d55c8/Montecarlo_nsq_single/nsqMain.m -------------------------------------------------------------------------------- /Montecarlo_seq/anloducurve.m: -------------------------------------------------------------------------------- 1 | function [busPd, busQd, loadpropor] = anloducurve(YEAR_HOUR) 2 | %[busPd, busQd, loadpropor] = anloducurve(YEAR_HOUR) 3 | % 4 | %Establish the hourly load duration curve based on IEEE Reliability Test System 1979. 5 | %Normally, it consists of 8736 hours. Each bus has same load ratios at each certain time. 6 | % 7 | %Inputs: 8 | % YEAR_HOUR: scalar the number of points of annual load duration curve, 9 | % it actually should be 8760 (365*24), but IEEE RTS-79 10 | % only provides 8736 hours (52*7*24). 11 | % 12 | %Outputs: 13 | % busPd: Matrix(Ng+Nl, YEAR_HOUR) Active power load at each bus for each hour 14 | % busQd: Matrix(Ng+Nl, YEAR_HOUR) Reactive power load at a each bus for each hour 15 | % loadpropor Vector(1, YEAR_HOUR) Load ratio at each bus for each hour 16 | 17 | %%%-------------------------------Function start--------------------------------------------------------------------------%% 18 | %tic 19 | %%-------------------------------Load basic porfile-------------------------%% 20 | 21 | Loadprofile = case24_loadprofile; 22 | loadpropor = zeros(1, YEAR_HOUR); 23 | 24 | %%-------------------------------Circulation structure for hourly load curve-------------%% 25 | for ihour = 1 : YEAR_HOUR 26 | 27 | %%--------------------Identify a certain hour to corresponding week------------------------%% 28 | %% week classification 29 | corweek = ceil(ihour/168); 30 | 31 | if corweek <= 8 || corweek >=44 32 | weekflag = 'winter'; 33 | elseif corweek >= 18 && corweek <= 30 34 | weekflag = 'summer'; 35 | elseif corweek <= 17 || corweek >= 31 36 | weekflag = 'springfall'; 37 | else 38 | disp('error'); 39 | end 40 | %%-----------------------Identify a certain hour to corresponding day------------------------------------------%% 41 | 42 | corday = ceil(mod(ihour/24, 7)); 43 | 44 | if corday == 0 %%sunday the remainder is 0, needs to be calibrated 45 | corday = 7; 46 | end 47 | 48 | %% day classification 49 | if corday <= 5 50 | dayflag = 'weekday'; 51 | else 52 | dayflag = 'weekend'; 53 | end 54 | %%---------------------Identify a certain hour to corresponding hour-----------------------%% 55 | corhour = mod(ihour, 24); 56 | 57 | if corhour == 0 %%for 23:00-24:00 time interval, the remainder is 0, needs to be calibrated 58 | corhour = 24; 59 | end 60 | 61 | %%-----------------------Obtain weekly ratio, daily ratio and hourly ratio ---------------------%% 62 | 63 | weekpropor = Loadprofile.weekly(corweek); 64 | daypropor = Loadprofile.daily(corday); 65 | 66 | switch weekflag 67 | case 'winter' 68 | switch dayflag 69 | case 'weekday' 70 | hourpropor = Loadprofile.hourly(corhour, 1); 71 | case 'weekend' 72 | hourpropor = Loadprofile.hourly(corhour, 2); 73 | end 74 | case 'summer' 75 | switch dayflag 76 | case 'weekday' 77 | hourpropor = Loadprofile.hourly(corhour, 3); 78 | case 'weekend' 79 | hourpropor = Loadprofile.hourly(corhour, 4); 80 | end 81 | case 'springfall' 82 | switch dayflag 83 | case 'weekday' 84 | hourpropor = Loadprofile.hourly(corhour, 5); 85 | case 'weekend' 86 | hourpropor = Loadprofile.hourly(corhour, 6); 87 | end 88 | end 89 | 90 | %%-----------------------Obtain hourly load duration curve ---------------------%% 91 | loadpropor(1, ihour) = weekpropor * daypropor * hourpropor; 92 | end 93 | 94 | %%-------------------------------Obtain load duration curve for each bus---------------------%% 95 | 96 | busPd = Loadprofile.busload(:, 2) * loadpropor; 97 | busQd = Loadprofile.busload(:, 3) * loadpropor; 98 | 99 | %toc 100 | return 101 | %%-------------------------------function end---------------------%% 102 | -------------------------------------------------------------------------------- /Montecarlo_seq/calnlc.m: -------------------------------------------------------------------------------- 1 | function nlc = calnlc(sysstatus) 2 | %{ 3 | 4 | Calculates the number of load curtailment for one year. 5 | 6 | 7 | Inputs: 8 | sysstatus: the hourly system status. 9 | 10 | Outputs: 11 | nlc: number of load curtailments. 12 | 13 | 14 | For instance, sysstatus = [1 1 1 0 0 0 1 1 0 0 0 0 0 0 1] 15 | denotes 3 power outages in this year. 16 | 17 | 0 - system in normal operation. 18 | 1 - system in corresponding hour has a blackout. 19 | 20 | Power outages refers to each continuous blackouts. 21 | 22 | 23 | 24 | %} 25 | %%-----------------------Calculate the number of load curtailments--------------------------------------------------%% 26 | 27 | outagesign = diff(sysstatus); %% [sysstatus(column+1) - sysstatus(column)] 28 | transindex = find(outagesign ~= 0); %% find the position of nonzero numbers 29 | 30 | %%-----------------------------------Get correction term-------------------------------------------------%% 31 | 32 | if ~isempty(transindex) 33 | %% if the first nonzero element is -1 and the final nonzero element is 1, there need to be corrected to obtain the number of power outages 34 | if (outagesign(1, transindex(1))) == -1 && (outagesign(1, transindex(end)) == 1) 35 | corterm = 1; 36 | else 37 | corterm = 0; 38 | end 39 | %% otherwise, it doesn't need to be corrected 40 | else 41 | 42 | corterm = 0; 43 | 44 | end 45 | %%-----------------------Get nlc--------------------------------------------------%% 46 | nlc = ceil(sum(abs(outagesign))/2) + corterm; 47 | 48 | return 49 | %%-----------------------calnlc end--------------------------------------------------%% 50 | 51 | -------------------------------------------------------------------------------- /Montecarlo_seq/case24_loadprofile.m: -------------------------------------------------------------------------------- 1 | function Loadprofile = case24_loadprofile() 2 | 3 | %%---------------------------System total active power and reactive power----------------------------%% 4 | Loadprofile.MW = 2850; 5 | Loadprofile.MVAr = 580; 6 | 7 | %%--------------------------- 8 | Loadprofile.weekly = [ 0.862 0.900 0.878 0.834 ... %% weeks 1-4 9 | 0.880 0.841 0.832 0.806 ... %% weeks 5-8 10 | 0.740 0.737 0.715 0.727 ... %% weeks 9-12 11 | 0.704 0.750 0.721 0.800 ... %% weeks 13-16 12 | 0.754 0.837 0.870 0.880 ... %% weeks 17-20 13 | 0.856 0.811 0.900 0.887 ... %% weeks 21-24 14 | 0.896 0.861 0.755 0.816 ... %% weeks 25-28 15 | 0.801 0.880 0.722 0.776 ... %% weeks 29-32 16 | 0.800 0.729 0.726 0.705 ... %% weeks 33-36 17 | 0.780 0.695 0.724 0.723 ... %% weeks 37-40 18 | 0.743 0.744 0.800 0.881 ... %% weeks 41-44 19 | 0.885 0.909 0.940 0.890 ... %% weeks 45-48 20 | 0.942 0.970 1.000 0.952 ]; %% weeks 49-52 21 | 22 | 23 | % mon tue wed thu fri sat sun 24 | Loadprofile.daily = [ 0.93 1.00 0.98 0.96 0.94 0.77 0.75]; 25 | 26 | 27 | %% 28 | %% winter weeks summer weeks spring/fall 29 | %% 1-8 & 44-52 18-30 9-17 & 31-43 30 | %% wkdy wknd wkdy wknd wkdy wknd 31 | %% corresponding hour 32 | Loadprofile.hourly = [ 0.67 0.78 0.64 0.74 0.63 0.75; %% 0 - 1 33 | 0.63 0.72 0.60 0.70 0.62 0.73; %% 1 - 2 34 | 0.60 0.68 0.58 0.66 0.60 0.69; %% 2 - 3 35 | 0.59 0.66 0.56 0.65 0.58 0.66; %% 3 - 4 36 | 0.59 0.64 0.56 0.64 0.59 0.65; %% 4 - 5 37 | 0.60 0.65 0.58 0.62 0.65 0.65; %% 5 - 6 38 | 0.74 0.66 0.64 0.62 0.72 0.68; %% 6 - 7 39 | 0.86 0.70 0.76 0.66 0.85 0.74; %% 7 - 8 40 | 0.95 0.80 0.87 0.81 0.95 0.83; %% 8 - 9 41 | 0.96 0.88 0.95 0.86 0.99 0.89; %% 9 - 10 42 | 0.96 0.90 0.99 0.91 1.00 0.92; %% 10 - 11 43 | 0.95 0.91 1.00 0.93 0.99 0.94; %% 11 - 12 44 | 0.95 0.90 0.99 0.93 0.93 0.91; %% 12 - 13 45 | 0.95 0.88 1.00 0.92 0.92 0.90; %% 13 - 14 46 | 0.93 0.87 1.00 0.91 0.90 0.90; %% 14 - 15 47 | 0.94 0.87 0.97 0.91 0.88 0.86; %% 15 - 16 48 | 0.99 0.91 0.96 0.92 0.90 0.85; %% 16 - 17 49 | 1.00 1.00 0.96 0.94 0.92 0.88; %% 17 - 18 50 | 1.00 0.99 0.93 0.95 0.96 0.92; %% 18 - 19 51 | 0.96 0.97 0.92 0.95 0.98 1.00; %% 19 - 20 52 | 0.91 0.94 0.92 1.00 0.96 0.97; %% 20 - 21 53 | 0.83 0.92 0.93 0.93 0.90 0.95; %% 21 - 22 54 | 0.73 0.87 0.87 0.88 0.80 0.90; %% 22 - 23 55 | 0.63 0.81 0.72 0.80 0.70 0.85 ]; %% 23 - 24 56 | 57 | %% [ bus_i Pd Qd ] 58 | Loadprofile.busload = [ 59 | 1 108 22; 60 | 2 97 20; 61 | 3 180 37; 62 | 4 74 15; 63 | 5 71 14; 64 | 6 136 28; 65 | 7 125 25; 66 | 8 171 35; 67 | 9 175 36; 68 | 10 195 40; 69 | 13 265 54; 70 | 14 194 39; 71 | 15 317 64; 72 | 16 100 20; 73 | 18 333 68; 74 | 19 181 37; 75 | 20 128 26 ]; 76 | 77 | end 78 | 79 | -------------------------------------------------------------------------------- /Montecarlo_seq/dispaload.m: -------------------------------------------------------------------------------- 1 | function [Testsys, loadbus] = dispaload(Testsys) 2 | 3 | Nb = size(Testsys.bus, 1); 4 | Ng = size(Testsys.gen, 1); 5 | Nl = size(Testsys.branch, 1); 6 | 7 | loadbus = find(Testsys.bus(:, 3) ~= 0); 8 | sizeloadbus = size(loadbus, 1); 9 | 10 | Testsys.Pload = sum(Testsys.bus(:, 3)); 11 | Testsys.gencost = repmat([2 0 0 3 0 0 0 ], Ng, 1); 12 | 13 | Testsys.gencost = vertcat(Testsys.gencost, repmat([2 0 0 3 0 1 0], sizeloadbus, 1)); 14 | 15 | % addgen = Testsys.gen(1:sizeloadbus, :); 16 | addgen = zeros(sizeloadbus, size(Testsys.gen, 2)); 17 | 18 | addgen(:, 1:10) = horzcat( Testsys.bus(loadbus, 1), - Testsys.bus(loadbus, 3), ... 19 | - Testsys.bus(loadbus, 4), zeros(sizeloadbus, 1), ... 20 | - Testsys.bus(loadbus, 4), zeros(sizeloadbus, 1), ... 21 | Testsys.baseMVA * ones(sizeloadbus, 1), ones(sizeloadbus, 1), ... 22 | zeros(sizeloadbus, 1), - Testsys.bus(loadbus, 3) ); 23 | 24 | Testsys.gen = vertcat(Testsys.gen, addgen); 25 | 26 | Testsys.bus(loadbus, 3:4) = 0; 27 | 28 | end 29 | -------------------------------------------------------------------------------- /Montecarlo_seq/seqMain.m: -------------------------------------------------------------------------------- 1 | %%function [] = seqMain() 2 | % [] = seqMain() 3 | % 4 | % Monte Carlo sequential simulation main function 5 | % 6 | % 7 | % Inputs(void) 8 | % 9 | % Outputs(void) 10 | % 11 | % 12 | % 13 | 14 | %%---------------------------------Commence function-------------------------------------------------------------------------------------------%% 15 | 16 | clc; 17 | close all; 18 | clear; 19 | tic 20 | 21 | %% -------------------------------Establish variables and set initial value-------------------------------------------------------------------------------------------%% 22 | 23 | %% testsystem information 24 | Testsys = loadcase('case24_ieee_rts'); 25 | Nb = size(Testsys.bus, 1); 26 | Ng = size(Testsys.gen, 1); 27 | Nl = size(Testsys.branch, 1); 28 | 29 | %% simulation constant 30 | YEAR_HOUR = 8736; 31 | SAMPLE_YEAR = 4000; 32 | CVEENSTHR = 0.05; 33 | 34 | %% record error state, hour, load 35 | errorpointer = 0; 36 | % errorflag = 0; 37 | errorstate = zeros(Ng+Nl+3, 100); 38 | 39 | %% record failure state, hour, load that curtailment is greater than threshold 40 | curtailpointer = 0; 41 | CURTAILTHR = 0.01; 42 | curtailstate = zeros(Ng+Nl+4, 500); 43 | 44 | 45 | %% basic indices for each year 46 | plc_yr = zeros(SAMPLE_YEAR, 1); 47 | nlc = zeros(SAMPLE_YEAR, 1); 48 | dlc = zeros(SAMPLE_YEAR, 1); 49 | lc = zeros(SAMPLE_YEAR, 1); 50 | dns = zeros(SAMPLE_YEAR, 1); 51 | ens = zeros(SAMPLE_YEAR, 1); 52 | bpii_yr = zeros(SAMPLE_YEAR, 1); 53 | 54 | %% basic expected indices 55 | plc = zeros(SAMPLE_YEAR, 1); 56 | enlc = zeros(SAMPLE_YEAR, 1); 57 | edlc = zeros(SAMPLE_YEAR, 1); 58 | % adlc = zeros(SAMPLE_YEAR, 1); 59 | elc = zeros(SAMPLE_YEAR, 1); 60 | edns = zeros(SAMPLE_YEAR, 1); 61 | eens = zeros(SAMPLE_YEAR, 1); 62 | cveens = zeros(SAMPLE_YEAR, 1); % coefficient of varience for EENS, one of the criteria for stopping loop 63 | bpii = zeros(SAMPLE_YEAR, 1); 64 | % bpeci = zeros(SAMPLE_YEAR, 1); 65 | % bpaci = zeros(SAMPLE_YEAR, 1); 66 | % mbpci = zeros(SAMPLE_YEAR, 1); 67 | % si = zeros(SAMPLE_YEAR, 1); 68 | 69 | %% -------------------------------Establish annual load duration curve---------------------------------------------------------------%% 70 | [busPd, busQd, loadpropor] = anloducurve(YEAR_HOUR); 71 | meantime = seqmeantime; 72 | 73 | %% -------------------------MC sequential sampling and identify contingency hour---------------------------------------------------------------%% 74 | 75 | %{ 76 | loadbus = find(Testsys.bus(:, 3) ~= 0); 77 | sizeloadbus = size(loadbus, 1); 78 | 79 | % Testsys.bus(loadbus, 3) = ctbusPd; 80 | % Testsys.bus(loadbus, 4) = ctbusQd; 81 | 82 | Testsys.Aload = sum(Testsys.bus(:, 3)); 83 | Testsys.gencost = repmat([2 0 0 3 0 0 0 ], Ng, 1); 84 | 85 | 86 | Testsys.gencost = vertcat(Testsys.gencost, repmat([2 0 0 3 0 1 0], sizeloadbus, 1)); 87 | 88 | addgen = Testsys.gen(1:sizeloadbus, :); 89 | 90 | addgen(:, 1:10) = horzcat( Testsys.bus(loadbus, 1), - Testsys.bus(loadbus, 3), ... 91 | - Testsys.bus(loadbus, 4), zeros(sizeloadbus, 1), ... 92 | - Testsys.bus(loadbus, 4), zeros(sizeloadbus, 1), ... 93 | Testsys.baseMVA * ones(sizeloadbus, 1), ones(sizeloadbus, 1), ... 94 | zeros(sizeloadbus, 1), - Testsys.bus(loadbus, 3) ); 95 | 96 | Testsys.gen = vertcat(Testsys.gen, addgen); 97 | 98 | Testsys.bus(loadbus, 3:4) = 0; 99 | %} 100 | % Dispatchable load for objective function of minimum load curtailment 101 | [Testsys, loadbus]= dispaload(Testsys); 102 | 103 | %% -------------------------------Set Matpower parameters and preliminary test---------------------------------------------------------------%% 104 | mpopt = mpoption('PF_DC',1,'VERBOSE',0,'OUT_ALL',0,'OPF_ALG_DC',200,'OPF_FLOW_LIM',1); 105 | % OPFresult = runopf(Testsys, mpopt); 106 | 107 | %% -------------------------------Start loop simulation---------------------------------------------------------------------------%% 108 | for iYear = 1 : SAMPLE_YEAR 109 | 110 | %% -------------------------MC sequential sampling and identify contingency hour---------------------------------------------------------------%% 111 | stadur = seq_mcsampling(meantime, Ng, Nl, 1, YEAR_HOUR); 112 | 113 | cthour = find(sum(stadur, 1) > 0); 114 | ctstadur = stadur(:, cthour); 115 | % ctbusPd = busPd(:, cthour); 116 | % ctbusQd = busQd(:, cthour); 117 | %% -------------------------Analyze states in each contingency hour---------------------------------------------------------------%% 118 | ctana = zeros(2, size(cthour, 2)); 119 | errorflag = zeros(1, size(cthour, 2)); 120 | parfor jHour = 1 : size(cthour, 2) 121 | try 122 | ctana(1, jHour) = seq_mcsimulation(ctstadur(:, jHour), loadpropor(1, cthour(1, jHour)) , Testsys, mpopt, loadbus, Ng, Nl); %% do simulations for each contingency hour 123 | catch 124 | % break; 125 | % errorstate(1:Ng+Nl, errorpointer) = ctstadur(:, jHour); 126 | % errorstate(Ng+Nl+1, errorpointer) = cthour(1, jHour); 127 | % errorstate(Ng+Nl+2, errorpointer) = loadpropor(1, cthour(1, jHour)); 128 | % errorstate(Ng+Nl+3, errorpointer) = iYear; 129 | warning('Problem using Matpower runopf. Assigning a value of 0 to curtailment'); 130 | ctana(1, jHour) = 0; 131 | errorflag(1, jHour) = 1; 132 | end 133 | end 134 | ctana(2, :) = double(ctana(1, :) ~= 0); %% contingency flag 135 | 136 | %% ---------------------------Record error state that cause Matpower runopf malfunctioned-------------------------------%% 137 | numerror = sum(errorflag); 138 | if numerror 139 | errorstate(1:Ng+Nl, errorpointer+1:errorpointer+numerror) = ctstadur(:, errorflag == 1 ); 140 | errorstate(Ng+Nl+1, errorpointer+1:errorpointer+numerror) = cthour(1, errorflag == 1); 141 | errorstate(Ng+Nl+2, errorpointer+1:errorpointer+numerror) = loadpropor(1, cthour(1, errorflag == 1)); 142 | errorstate(Ng+Nl+3, errorpointer+1:errorpointer+numerror) = iYear; 143 | errorpointer = errorpointer + numerror; 144 | end 145 | 146 | %% ---------------------------Record failure state, hour, load that curtailment is greater than threshold-------------------------------%% 147 | recordcurtail = find(ctana(1, :) > CURTAILTHR); 148 | numcurtail = size(recordcurtail, 2); 149 | if numcurtail > 0 150 | curtailstate(1:Ng+Nl, curtailpointer+1:curtailpointer+numcurtail) = ctstadur(:, recordcurtail); 151 | curtailstate(Ng+Nl+1, curtailpointer+1:curtailpointer+numcurtail) = cthour(:, recordcurtail); 152 | curtailstate(Ng+Nl+2, curtailpointer+1:curtailpointer+numcurtail) = loadpropor(:, recordcurtail); 153 | curtailstate(Ng+Nl+3, curtailpointer+1:curtailpointer+numcurtail) = iYear; 154 | curtailstate(Ng+Nl+4, curtailpointer+1:curtailpointer+numcurtail) = ctana(1, recordcurtail); 155 | curtailpointer = curtailpointer + numcurtail; 156 | end 157 | 158 | %% -----------------------Calculate Basic Indices per year---------------------------------------------------------------%% 159 | plc_yr(iYear, 1) = sum(ctana(2, :) / YEAR_HOUR); 160 | nlc(iYear, 1) = calnlc(ctana(2, :)); 161 | dlc(iYear, 1) = sum(ctana(2, :)); 162 | lc(iYear, 1) = sum(ctana(1, :) * 1); % load curtailments(MW/yr) LC = sigma CiFi 163 | dns(iYear, 1) = sum(ctana(1, :) * 1) / YEAR_HOUR; 164 | ens(iYear, 1) = sum(ctana(1, :) * 1 * 1); % ENS = sigma CiFiDi 165 | bpii_yr(iYear, 1) = sum(ctana(1, :) * 1 / Testsys.Pload); 166 | 167 | %% -----------------------Calculate Basic Expected Indices------------------------------------------------------------------------------------------%% 168 | plc(iYear, 1) = mean(plc_yr(1:iYear, 1)); 169 | enlc(iYear, 1) = mean(nlc(1:iYear, 1)); 170 | edlc(iYear, 1) = mean(dlc(1:iYear, 1)); 171 | elc(iYear, 1) = mean(lc(1:iYear, 1)); 172 | edns(iYear, 1) = mean(dns(1:iYear, 1)); 173 | 174 | eens(iYear, 1) = mean(ens(1:iYear, 1)); 175 | cveens(iYear, 1) = std(eens(1:iYear, 1)) / mean(eens(1:iYear)); 176 | plot(eens(1:iYear, 1)); 177 | drawnow; 178 | 179 | bpii = mean(bpii_yr(1:iYear, 1)); 180 | 181 | %% -----------------------Determin if cveens smaller than setting threshold------------------------------------------------------------------------------------------%% 182 | if (cveens(iYear, 1) < CVEENSTHR) && (cveens(iYear, 1) ~= 0) 183 | % disp('Simulation terminated due to achieving accetable accuracy'); 184 | % flag_break = 1; 185 | break; % if already achieve acceptable accuracy, break year simulation 186 | end 187 | 188 | %% -----------------------------------------------------------------------------------------------------------------%% 189 | 190 | end 191 | 192 | %% -------------------------------Delete all zero placeholders----------------------------------------------------------------------------------%% 193 | if iYear < SAMPLE_YEAR 194 | plc(iYear+1:SAMPLE_YEAR, :) = []; 195 | enlc(iYear+1:SAMPLE_YEAR, :) = []; 196 | edlc(iYear+1:SAMPLE_YEAR, :) = []; 197 | elc(iYear+1:SAMPLE_YEAR, :) = []; 198 | edns(iYear+1:SAMPLE_YEAR, :) = []; 199 | eens(iYear+1:SAMPLE_YEAR, :) = []; 200 | bpii_yr(iYear+1:SAMPLE_YEAR, :) = []; 201 | end 202 | 203 | %% -----------------------Calculate Remaining Expected Indices------------------------------------------------------------------------------------------%% 204 | adlc = edlc ./ enlc; 205 | bpeci = eens / Testsys.Pload; 206 | bpaci = elc ./ enlc; 207 | mbpci = edns / Testsys.Pload; 208 | si = bpeci * 60; 209 | 210 | toc 211 | 212 | %%end 213 | %%---------------------------------------------End-------------------------------------------------------------------------%% 214 | -------------------------------------------------------------------------------- /Montecarlo_seq/seq_mcsampling.m: -------------------------------------------------------------------------------- 1 | function stadur = seq_mcsampling(meantime, Ng, Nl, SAMPLE_YEAR, YEAR_HOUR) 2 | %%------------------------------- 3 | %%tic 4 | 5 | sampleno = SAMPLE_YEAR*YEAR_HOUR; 6 | stadur = sparse(Ng+Nl, sampleno);%%an Ng+Nl-by-sampleno all zero matrix 7 | 8 | for ieqrow = 1 : Ng+Nl 9 | 10 | starow = sparse(1, sampleno); 11 | staflag = true; 12 | pointer = 0; 13 | 14 | %%-------------------------Monte Carlo state duration sampling---------------%% 15 | while pointer < sampleno 16 | 17 | if staflag 18 | t = floor(-meantime(ieqrow, 1) * log(rand(1))); 19 | pointer = pointer + t; 20 | else 21 | t = ceil(-meantime(ieqrow, 2) * log(rand(1))); 22 | starow(1, pointer+1:pointer+t) = 1; 23 | pointer = pointer + t; 24 | end 25 | 26 | staflag = ~ staflag; 27 | 28 | end 29 | 30 | if size(starow, 2) > sampleno 31 | starow(:, sampleno+1:end) = []; 32 | end 33 | 34 | stadur(ieqrow, :) = starow; 35 | 36 | %%---------------------------------------------------------------------------%% 37 | end 38 | 39 | %% stadur(:, sampleno+1:pointer) = []; 40 | %% stadur = double(stadur); 41 | 42 | 43 | %%toc 44 | 45 | return 46 | %%----------------------------------end----------------------------------%% 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Montecarlo_seq/seq_mcsimulation.m: -------------------------------------------------------------------------------- 1 | function curtailment = seq_mcsimulation(ctstadur, loadpropor, Testsys, mpopt, loadbus, Ng, Nl) 2 | 3 | 4 | %%-------------------------------Load modification--------------------------------------------------------%% 5 | 6 | %{ 7 | genbus = find(Testsys.bus(:, 3) ~= 0); 8 | sizegenbus = size(genbus, 1); 9 | 10 | Testsys.bus(genbus, 3) = ctbusPd; 11 | Testsys.bus(genbus, 4) = ctbusQd; 12 | 13 | Testsys.Pload = sum(Testsys.bus(:, 3)); 14 | Testsys.gencost = repmat([2 0 0 3 0 0 0 ], Ng, 1); 15 | 16 | 17 | Testsys.gencost = vertcat(Testsys.gencost, repmat([2 0 0 3 0 1 0], sizegenbus, 1)); 18 | 19 | addgen = Testsys.gen(1:sizegenbus, :); 20 | 21 | addgen(:, 1:10) = horzcat( Testsys.bus(genbus, 1), - Testsys.bus(genbus, 3), ... 22 | - Testsys.bus(genbus, 4), zeros(sizegenbus, 1), ... 23 | - Testsys.bus(genbus, 4), zeros(sizegenbus, 1), ... 24 | Testsys.baseMVA * ones(sizegenbus, 1), ones(sizegenbus, 1), ... 25 | zeros(sizegenbus, 1), - Testsys.bus(genbus, 3) ); 26 | 27 | Testsys.gen = [Testsys.gen; addgen]; 28 | 29 | Testsys.bus(genbus, 3:4) = 0; 30 | 31 | %} 32 | sizeloadbus = size(loadbus, 1); 33 | Testsys.Pload = Testsys.Pload * loadpropor; 34 | Testsys.gen(Ng+1:Ng+sizeloadbus, [2 3 5 10]) = Testsys.gen(Ng+1:Ng+sizeloadbus, [2 3 5 10]) * loadpropor; 35 | 36 | %%-------------------------------Generation modification---------------------------------------------------%% 37 | 38 | 39 | Testsys.gen(1:Ng, 8) = ~ ctstadur(1:Ng, 1); 40 | Testsys.branch(1:Nl, 11) = ~ ctstadur(Ng+1:Ng+Nl, 1); 41 | 42 | 43 | %%-------------------------------Generation modification------------------------------------------------%% 44 | 45 | Result = runopf(Testsys, mpopt); 46 | curtailment = Result.f + Testsys.Pload; 47 | 48 | if curtailment < 0.1 49 | curtailment = 0; 50 | end 51 | 52 | return -------------------------------------------------------------------------------- /Montecarlo_seq/seqmeantime.m: -------------------------------------------------------------------------------- 1 | function meantime = seqmeantime() 2 | % meantime = seqmeantime() 3 | % 4 | % Obtain MTTF and MTTR for each equipment 5 | % 6 | % Inputs(void) 7 | % 8 | % Outputs: 9 | % meantime matrix(Ng+Nl, [MTTF, MTTR]) MTTF and MTTR table for each equipment 10 | 11 | %{ 12 | equipment | MTTF MTTR | 13 | ----------------------------------- | 14 | 1 | | | 15 | generator ... | | | 16 | Ng | | | 17 | ----------------------------------- | 18 | Ng+1 | | | 19 | branch ... | | | 20 | Ng+Nl | | | 21 | ----------------------------------- | 22 | 23 | %} 24 | 25 | %% ---------------------------------Load case file--------------------------------------------------%% 26 | failrate = case24_failrate; 27 | % meantime = zeros(Ng+Nl, 2); 28 | 29 | %% ----------------------------------Generator data----------------------------------%% 30 | genmttf = failrate.genmttf'; 31 | genmttr = failrate.genmttr'; 32 | 33 | %% ----------------------------------Branch data----------------------------------%% 34 | brmttf = (8760 ./ failrate.brlambda)'; 35 | brmttr = failrate.brdur'; 36 | 37 | %% ----------------------------------Aggregate data to form meantime matrix----------------------------------%% 38 | 39 | meantime = [ genmttf, genmttr; 40 | brmttf, brmttr ]; 41 | 42 | 43 | return 44 | %%----------------------------------end----------------------------------%% -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerSystemsReliabilityAssessment 2 | Power systems reliabilyt assessment using Monte-Carlo simulation 3 | 4 | Please note that, Matpower and Cplex are required to implement these codes. 5 | --------------------------------------------------------------------------------