Home > matpower7.0 > extras > misc > plot_mpc.m

plot_mpc

PURPOSE ^

PLOT_MPC Plot an electrically meaningful drawing of a MATPOWER case.

SYNOPSIS ^

function [handle] = plot_mpc(mpc, varargin)

DESCRIPTION ^

PLOT_MPC  Plot an electrically meaningful drawing of a MATPOWER case.
   HANDLE = PLOT_MPC(MPC)
   HANDLE = PLOT_MPC(MPC, <option1_name>, <option1_value>, ...)

   Examples:
       plot_mpc('case118', 'MaxBusLabels', 40)

   Plots an electrically meaningful drawing of a MATPOWER case.
   Higher voltage branches will be drawn using thicker lines. Systems of
   more than ~300 buses may be very slow to draw.

   Inputs:
       MPC: a MATPOWER case struct or string with the name of the case file
       <options>: name, value pairs of options
           Three optional name/value attribute pairs are available:

           'MaxBusLabels' / integer : This integer sets how many bus numbers
               to display. Labels are given to the highest voltage level
               buses first, and progressing down as space permits.

           'DoThevLayout' / boolean : This sets which electrical distance
               metric to use as a basis for the layout. The default, 0, uses
               a distance metric based on power transfer distances, which
               tends to give attractive, legible layouts. Setting this to 1
               will give a diagram which explicitly shows the effective
               Thevenin impedance between all bus pairs

           'FigSize' / scalar : The basic image size is 8.89cm by 6.27 cm,
               which fits the column width of an IEEE journal. This can be
               scaled up by setting a scaler here, for larger or higher
               resolution images. The default is 3.

   This function implements visualization techniques described in this paper:

     P. Cuffe; A. Keane, "Visualizing the Electrical Structure of Power
     Systems," in IEEE Systems Journal, vol.PP, no.99, pp.1-12.
     doi: 10.1109/JSYST.2015.2427994
     https://doi.org/10.1109/JSYST.2015.2427994

   Figures produced using this script can be published freely, however a
   citation of the above paper would be appreciated.

   Contact:
     Paul Cuffe, University College Dublin, March 2016
     paul.cuffe@ucd.ie

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function [handle] = plot_mpc(mpc, varargin)
0002 %PLOT_MPC  Plot an electrically meaningful drawing of a MATPOWER case.
0003 %   HANDLE = PLOT_MPC(MPC)
0004 %   HANDLE = PLOT_MPC(MPC, <option1_name>, <option1_value>, ...)
0005 %
0006 %   Examples:
0007 %       plot_mpc('case118', 'MaxBusLabels', 40)
0008 %
0009 %   Plots an electrically meaningful drawing of a MATPOWER case.
0010 %   Higher voltage branches will be drawn using thicker lines. Systems of
0011 %   more than ~300 buses may be very slow to draw.
0012 %
0013 %   Inputs:
0014 %       MPC: a MATPOWER case struct or string with the name of the case file
0015 %       <options>: name, value pairs of options
0016 %           Three optional name/value attribute pairs are available:
0017 %
0018 %           'MaxBusLabels' / integer : This integer sets how many bus numbers
0019 %               to display. Labels are given to the highest voltage level
0020 %               buses first, and progressing down as space permits.
0021 %
0022 %           'DoThevLayout' / boolean : This sets which electrical distance
0023 %               metric to use as a basis for the layout. The default, 0, uses
0024 %               a distance metric based on power transfer distances, which
0025 %               tends to give attractive, legible layouts. Setting this to 1
0026 %               will give a diagram which explicitly shows the effective
0027 %               Thevenin impedance between all bus pairs
0028 %
0029 %           'FigSize' / scalar : The basic image size is 8.89cm by 6.27 cm,
0030 %               which fits the column width of an IEEE journal. This can be
0031 %               scaled up by setting a scaler here, for larger or higher
0032 %               resolution images. The default is 3.
0033 %
0034 %   This function implements visualization techniques described in this paper:
0035 %
0036 %     P. Cuffe; A. Keane, "Visualizing the Electrical Structure of Power
0037 %     Systems," in IEEE Systems Journal, vol.PP, no.99, pp.1-12.
0038 %     doi: 10.1109/JSYST.2015.2427994
0039 %     https://doi.org/10.1109/JSYST.2015.2427994
0040 %
0041 %   Figures produced using this script can be published freely, however a
0042 %   citation of the above paper would be appreciated.
0043 %
0044 %   Contact:
0045 %     Paul Cuffe, University College Dublin, March 2016
0046 %     paul.cuffe@ucd.ie
0047 
0048 %   MATPOWER
0049 %   Copyright (c) 2016, Power Systems Engineering Research Center (PSERC)
0050 %   by Paul Cuffe
0051 %
0052 %   This file is part of MATPOWER Extras.
0053 %   Covered by the 3-clause BSD License (see LICENSE file for details).
0054 %   See https://github.com/MATPOWER/matpower-extras for more info.
0055 
0056 
0057 MaxBusLabels=[]; %Maximum number of bus labels to plot - need to limit this to stop clutter
0058 DoThevLayout=[]; %Whether to use Thevenin impedance as the inter-node distance measure, or the power transfer distance measure
0059 FigSize=[]; %The multipier on figure size. Setting = 1 gives a figure with width 8.89 cm, which is equal to the column width for an IEEE publication. Height is set to width / sqrt(2) to maintain pleasing proportions
0060 
0061 
0062 i=1;
0063 while i <= length(varargin);
0064   switch (varargin{i});
0065     case 'MaxBusLabels';
0066       MaxBusLabels=varargin{i+1}; i=i+2;
0067     case 'DoThevLayout';
0068       DoThevLayout = 1; i=i+2;
0069     case 'FigSize';
0070       FigSize=varargin{i+1}; i=i+2;
0071       otherwise;
0072       error('Unknown option: %s\n',varargin{i}); i=i+1;
0073   end
0074 end
0075 
0076 % If the variables remains empty after the above assignment give them
0077 % defaults
0078 if isempty(MaxBusLabels), MaxBusLabels = 50; end %50 seems a sensible default so things don't get too cluttered
0079 if isempty(DoThevLayout), DoThevLayout =0; end %0 means do power transfer layout
0080 if isempty(FigSize), FigSize=3; end
0081 
0082 define_constants;
0083 
0084 NumberedSystem = ext2int(runpf(mpc)); %Just in case bus numbers are all out of whack
0085 
0086 NumBuses = size(NumberedSystem.bus,1);
0087 
0088         if(NumBuses > 300)
0089           warning('Large system: layout calculation may be quite slow')
0090         end
0091     
0092     if (DoThevLayout == 1) %Use internode effective impedances as the basis for the projection
0093 
0094         [Ybuspu, Yf, Yt] = makeYbus(NumberedSystem);
0095 
0096         Ybuspu = full(Ybuspu);
0097         Zbuspu = pinv(Ybuspu);
0098 
0099         ThevMatrix = repmat(diag(Zbuspu),1, NumBuses) + repmat(diag(Zbuspu),1, NumBuses).'  - 2*Zbuspu; %Klein resistance distance formula
0100 
0101         ThevMatrix = ThevMatrix - tril(ThevMatrix); %Force symmetry in case a little numerical noise is preventing us being a strict distance matrix
0102         ThevMatrix = ThevMatrix + ThevMatrix.';
0103 
0104         [Layout, stressThev] = mdscale(abs(ThevMatrix),2,'criterion','sammon','start','random'); %apply multidimensional scaling to project into two dimensions
0105 
0106     else %Use internode "power transfer distances" as the basis for the projection
0107     
0108             TransMatrixMW = zeros(NumBuses); %init with zeros
0109 
0110             for ThisBus = 1:NumBuses % loop through each bus
0111 
0112             CurPTDFMatrix = makePTDF(NumberedSystem.baseMVA, NumberedSystem.bus, NumberedSystem.branch, ThisBus); %calculate the bus-to-branch PTDFs for an injection at every bus absorbed at ThisBus
0113 
0114             TotalBusFlows = sum(abs(CurPTDFMatrix),1); %take ABS value as we're interested in total MW flow shift, regardless of direction
0115 
0116             TransMatrixMW(ThisBus,:) = TotalBusFlows;
0117 
0118             end
0119 
0120         TransMatrixMW = TransMatrixMW - tril(TransMatrixMW); %Force symmetry in case a little numerical noise is preventing us being a strict distance matrix
0121         TransMatrixMW = TransMatrixMW + TransMatrixMW';
0122         TransMatrixMW(isnan(TransMatrixMW)) = mean(TransMatrixMW(~isnan(TransMatrixMW))); %Just a hack in case some PTDF calculations haven't been succesful
0123 
0124         [Layout, stressMW] = mdscale(TransMatrixMW,2,'criterion','sammon','start','random');  %apply multidimensional scaling to project into two dimensions
0125     
0126     end
0127 
0128 BranchVoltages = ( NumberedSystem.bus(NumberedSystem.branch(:, F_BUS), BASE_KV) + NumberedSystem.bus(NumberedSystem.branch(:, T_BUS), BASE_KV) ) / 2; %Divide by two so transformers take on intermediate values
0129 
0130 DistinctVoltageLevels = unique([BranchVoltages; NumberedSystem.bus(:, BASE_KV)]);
0131 
0132 handle = figure;
0133 
0134 cmap = colormap(winter(size(DistinctVoltageLevels,1) + 5));
0135 
0136 AllHandles = [];
0137 
0138     for ThisVoltageLevel = 1:size(DistinctVoltageLevels,1) %Run through and draw all the branches first
0139 
0140         BranchesAtThisLevel = find(BranchVoltages == DistinctVoltageLevels(ThisVoltageLevel));
0141       
0142             if (~isempty(BranchesAtThisLevel))
0143 
0144             FromBuses = NumberedSystem.branch(BranchesAtThisLevel, F_BUS);
0145             ToBuses = NumberedSystem.branch(BranchesAtThisLevel, T_BUS);
0146 
0147             LineSegments = [Layout(FromBuses,:), Layout(ToBuses,:)];
0148 
0149             CurrentThickness = ThisVoltageLevel/size(DistinctVoltageLevels,1) * 3;
0150             
0151        ThisHandle = line([LineSegments(:,1),LineSegments(:,3)]' , [LineSegments(:,2), LineSegments(:,4)]', 'color', cmap(ThisVoltageLevel + 1, :), 'LineWidth', CurrentThickness*1.5, 'DisplayName', [int2str(DistinctVoltageLevels(ThisVoltageLevel)), ' kV'] );
0152 
0153        AllHandles = [AllHandles; ThisHandle(1)]; %Just take the first sample line from each
0154        
0155             hold on;
0156             end
0157         
0158     end
0159 
0160 
0161         for ThisVoltageLevel = 1:size(DistinctVoltageLevels,1) %Then draw in buses circles. Doing this in two steps stops unwanted overlaps
0162 
0163         BusesAtThisLevel = find(NumberedSystem.bus(:, BASE_KV) == DistinctVoltageLevels(ThisVoltageLevel));
0164         
0165         CurrentThickness = ThisVoltageLevel/size(DistinctVoltageLevels,1) * 3;
0166         
0167         hold on;
0168 
0169         scatter(Layout(BusesAtThisLevel,1), Layout(BusesAtThisLevel,2), CurrentThickness * 12, cmap((ThisVoltageLevel + 1),:),'fill','MarkerEdgeColor','w');
0170 
0171         end
0172     
0173         RemainingLabelSlots = MaxBusLabels;
0174         
0175         for ThisVoltageLevel = size(DistinctVoltageLevels,1):-1:1 %Now start at the highest voltage level and work our way down, labelling bus nodes until we are too cluttered.
0176 
0177         BusesAtThisLevel = find(NumberedSystem.bus(:, BASE_KV) == DistinctVoltageLevels(ThisVoltageLevel));
0178         
0179             if(size(BusesAtThisLevel,1) < RemainingLabelSlots)  %For consistency, we should label all buses at a voltage level together, or not at all
0180 
0181         text(Layout(BusesAtThisLevel,1), Layout(BusesAtThisLevel,2), int2str(NumberedSystem.order.bus.i2e(BusesAtThisLevel)), 'FontName', 'Times Roman', 'FontSize', 18, 'HorizontalAlignment','center','VerticalAlignment','top')
0182 
0183         RemainingLabelSlots = RemainingLabelSlots - size(BusesAtThisLevel,1);
0184 
0185             else
0186                break %Needs to be only the highest voltage buses we label
0187             end
0188 
0189     end
0190 
0191 FigWidth = 8.89 * FigSize; %Size of a column in an IEEE paper
0192 FigHeight = 6.27 * FigSize; %Height/sqrt(2) which is a classicaly nice proportion
0193 
0194 set(gcf, 'Units','centimeters', 'Position',[0 0 FigWidth FigHeight])
0195 set(gca, 'Units','centimeters')
0196 set(gca, 'Position',[0 0 FigWidth FigHeight])
0197 
0198 set(gcf, 'PaperUnits','centimeters', 'PaperPosition',[(FigWidth * 0.02) (FigHeight * 0.02) (FigWidth * 0.98) (FigHeight * 0.98)])
0199 
0200 axis([min(Layout(:,1))*1.05 max(Layout(:,1))*1.05 min(Layout(:,2))*1.1 max(Layout(:,2))*1.02]) %Just give a little breathing room around the edges
0201 
0202 axis equal; %it's an electrical map so need consistent scale
0203 
0204 set(gca,'YTick',get(gca,'XTick')) %consistent tick interval
0205 set(gca, 'XTickLabelMode', 'manual', 'XTickLabel', []); %no numbers just ticks
0206 set(gca, 'YTickLabelMode', 'manual', 'YTickLabel', []); %no numbers just ticks
0207 
0208 
0209 %Now put in a legend using the line handles we recorded earlier
0210 
0211     NumHandles = size(AllHandles,1); %Just in case we have too many voltage levels
0212     
0213     if(NumHandles > 5) %Limit it to five for clarity's sake
0214     
0215     TrimHandles = [AllHandles(1); AllHandles(floor(NumHandles*0.25));AllHandles(floor(NumHandles*0.5));AllHandles(floor(NumHandles*0.75))  ;AllHandles(end)]; %Top and bottom and a few intermediate
0216     
0217     else
0218         TrimHandles = AllHandles;
0219     end
0220     
0221     VoltageLegend = legend(flipud(TrimHandles));
0222     legend('boxoff');
0223     
0224     set(VoltageLegend, 'FontName', 'Times Roman', 'FontSize', 22); %To be consistent with node numbers
0225     set(VoltageLegend, 'Location', 'best'); %Locate it to not obscure data

Generated on Mon 24-Jun-2019 15:58:45 by m2html © 2005