diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 502f5ac98..b74bb8606 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,7 @@ Change Log ------------------------------- - [ADDED] added arbitrary keyword arguments, ``**kwargs``, in all create-functions - [FIX] from_ppc() converter and power system test cases: add missing factor for tap_side=="lv"; change tap_side to "hv" for all test cases (were converted without new factor, so as the tap_side is "hv") +- [ADDED] from_mpc() converter: added functionality to import .m files via external package - [CHANGED] from_ppc() converter: added option of tap_side and essential speed up [2.9.0]- 2022-03-23 diff --git a/pandapower/converter/matpower/from_mpc.py b/pandapower/converter/matpower/from_mpc.py index cf2835d5e..d6cb2f208 100644 --- a/pandapower/converter/matpower/from_mpc.py +++ b/pandapower/converter/matpower/from_mpc.py @@ -3,12 +3,19 @@ # Copyright (c) 2016-2022 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. - +import os import numpy as np +import pandas as pd import scipy.io from pandapower.converter.pypower import from_ppc +try: + from matpowercaseframes import CaseFrames + matpowercaseframes_imported = True +except ImportError: + matpowercaseframes_imported = False + try: import pandaplan.core.pplog as logging except ImportError: @@ -21,9 +28,9 @@ def from_mpc(mpc_file, f_hz=50, casename_mpc_file='mpc', validate_conversion=Fal """ This function converts a matpower case file version 2 to a pandapower net. - Note: The input is a .mat file not an .m script. You need to save the mpc dict variable as .mat - file. If the saved variable of the matlab workspace is not named 'mpc', you can adapt the value - of 'casename_mpc_file' as needed. + Note: If 'mpc_file' ends with '.m' the python package 'matpowercaseframes' is used. If + 'mpc_file' ends with '.mat' 'scipy.io.loadmat' is used. Other file endings are not supported. + In that other cases, please, rename the file ending or use the internal subfunctions. Note: python is 0-based while Matlab is 1-based. @@ -48,11 +55,16 @@ def from_mpc(mpc_file, f_hz=50, casename_mpc_file='mpc', validate_conversion=Fal import pandapower.converter as pc - pp_net = cv.from_mpc('case9.mat', f_hz=60) + pp_net1 = cv.from_mpc('case9.mat', f_hz=60) + pp_net2 = cv.from_mpc('case9.m', f_hz=60) """ - ppc = _mpc2ppc(mpc_file, casename_mpc_file) - net = from_ppc(ppc, f_hz, validate_conversion, **kwargs) + ending = os.path.splitext(os.path.basename(mpc_file))[1] + if ending == ".mat": + ppc = _mat2ppc(mpc_file, casename_mpc_file) + elif ending == ".m": + ppc = _m2ppc(mpc_file, casename_mpc_file) + net = from_ppc(ppc, f_hz=f_hz, validate_conversion=validate_conversion, **kwargs) if "mpc_additional_data" in ppc: net._options.update(ppc["mpc_additional_data"]) logger.info('added fields %s in net._options' % list(ppc["mpc_additional_data"].keys())) @@ -61,6 +73,10 @@ def from_mpc(mpc_file, f_hz=50, casename_mpc_file='mpc', validate_conversion=Fal def _mpc2ppc(mpc_file, casename_mpc_file): + raise DeprecationWarning("_mpc2ppc() has been renamed by _mat2ppc().") + + +def _mat2ppc(mpc_file, casename_mpc_file): # load mpc from file mpc = scipy.io.loadmat(mpc_file, squeeze_me=True, struct_as_record=False) @@ -74,6 +90,19 @@ def _mpc2ppc(mpc_file, casename_mpc_file): return ppc +def _m2ppc(mpc_file, casename_mpc_file): + if not matpowercaseframes_imported: + raise NotImplementedError( + "matpowercaseframes is used to convert .m file. Please install that python " + "package, e.g. via 'pip install matpowercaseframes'.") + mpc_frames = CaseFrames(mpc_file) + ppc = {key: mpc_frames.__getattribute__(key) if not isinstance( + mpc_frames.__getattribute__(key), pd.DataFrame) else mpc_frames.__getattribute__( + key).values for key in mpc_frames._attributes} + _adjust_ppc_indices(ppc) + return ppc + + def _adjust_ppc_indices(ppc): # adjust indices of ppc, since ppc must start at 0 rather than 1 (matlab) ppc["bus"][:, 0] -= 1 diff --git a/pandapower/test/converter/case24_ieee_rts.m b/pandapower/test/converter/case24_ieee_rts.m new file mode 100644 index 000000000..862c195c9 --- /dev/null +++ b/pandapower/test/converter/case24_ieee_rts.m @@ -0,0 +1,181 @@ +function mpc = case24_ieee_rts +%CASE24_IEEE_RTS Power flow data for the IEEE RELIABILITY TEST SYSTEM. +% Please see CASEFORMAT for details on the case file format. +% +% This system data is from the IEEE RELIABILITY TEST SYSTEM, see +% +% IEEE Reliability Test System Task Force of the Applications of +% Probability Methods Subcommittee, "IEEE reliability test system," +% IEEE Transactions on Power Apparatus and Systems, Vol. 98, No. 6, +% Nov./Dec. 1979, pp. 2047-2054. +% +% IEEE Reliability Test System Task Force of Applications of +% Probability Methods Subcommittee, "IEEE reliability test system-96," +% IEEE Transactions on Power Systems, Vol. 14, No. 3, Aug. 1999, +% pp. 1010-1020. +% +% Cost data is from Web site run by Georgia Tech Power Systems Control +% and Automation Laboratory: +% +% http://pscal.ece.gatech.edu/testsys/index.html +% +% MATPOWER case file data provided by Bruce Wollenberg. + +% MATPOWER + +%% MATPOWER Case Format : Version 2 +mpc.version = '2'; + +%%----- Power Flow Data -----%% +%% system MVA base +mpc.baseMVA = 100; + +%% bus data +% bus_i type Pd Qd Gs Bs area Vm Va baseKV zone Vmax Vmin +mpc.bus = [ + 1 2 108 22 0 0 1 1 0 138 1 1.05 0.95; + 2 2 97 20 0 0 1 1 0 138 1 1.05 0.95; + 3 1 180 37 0 0 1 1 0 138 1 1.05 0.95; + 4 1 74 15 0 0 1 1 0 138 1 1.05 0.95; + 5 1 71 14 0 0 1 1 0 138 1 1.05 0.95; + 6 1 136 28 0 -100 2 1 0 138 1 1.05 0.95; + 7 2 125 25 0 0 2 1 0 138 1 1.05 0.95; + 8 1 171 35 0 0 2 1 0 138 1 1.05 0.95; + 9 1 175 36 0 0 1 1 0 138 1 1.05 0.95; + 10 1 195 40 0 0 2 1 0 138 1 1.05 0.95; + 11 1 0 0 0 0 3 1 0 230 1 1.05 0.95; + 12 1 0 0 0 0 3 1 0 230 1 1.05 0.95; + 13 3 265 54 0 0 3 1 0 230 1 1.05 0.95; + 14 2 194 39 0 0 3 1 0 230 1 1.05 0.95; + 15 2 317 64 0 0 4 1 0 230 1 1.05 0.95; + 16 2 100 20 0 0 4 1 0 230 1 1.05 0.95; + 17 1 0 0 0 0 4 1 0 230 1 1.05 0.95; + 18 2 333 68 0 0 4 1 0 230 1 1.05 0.95; + 19 1 181 37 0 0 3 1 0 230 1 1.05 0.95; + 20 1 128 26 0 0 3 1 0 230 1 1.05 0.95; + 21 2 0 0 0 0 4 1 0 230 1 1.05 0.95; + 22 2 0 0 0 0 4 1 0 230 1 1.05 0.95; + 23 2 0 0 0 0 3 1 0 230 1 1.05 0.95; + 24 1 0 0 0 0 4 1 0 230 1 1.05 0.95; +]; + +%% generator data +% bus Pg Qg Qmax Qmin Vg mBase status Pmax Pmin Pc1 Pc2 Qc1min Qc1max Qc2min Qc2max ramp_agc ramp_10 ramp_30 ramp_q apf % Unit Code +mpc.gen = [ + 1 10 0 10 0 1.035 100 1 20 16 0 0 0 0 0 0 0 0 0 0 0; % U20 + 1 10 0 10 0 1.035 100 1 20 16 0 0 0 0 0 0 0 0 0 0 0; % U20 + 1 76 0 30 -25 1.035 100 1 76 15.2 0 0 0 0 0 0 0 0 0 0 0; % U76 + 1 76 0 30 -25 1.035 100 1 76 15.2 0 0 0 0 0 0 0 0 0 0 0; % U76 + 2 10 0 10 0 1.035 100 1 20 16 0 0 0 0 0 0 0 0 0 0 0; % U20 + 2 10 0 10 0 1.035 100 1 20 16 0 0 0 0 0 0 0 0 0 0 0; % U20 + 2 76 0 30 -25 1.035 100 1 76 15.2 0 0 0 0 0 0 0 0 0 0 0; % U76 + 2 76 0 30 -25 1.035 100 1 76 15.2 0 0 0 0 0 0 0 0 0 0 0; % U76 + 7 80 0 60 0 1.025 100 1 100 25 0 0 0 0 0 0 0 0 0 0 0; % U100 + 7 80 0 60 0 1.025 100 1 100 25 0 0 0 0 0 0 0 0 0 0 0; % U100 + 7 80 0 60 0 1.025 100 1 100 25 0 0 0 0 0 0 0 0 0 0 0; % U100 + 13 95.1 0 80 0 1.02 100 1 197 69 0 0 0 0 0 0 0 0 0 0 0; % U197 + 13 95.1 0 80 0 1.02 100 1 197 69 0 0 0 0 0 0 0 0 0 0 0; % U197 + 13 95.1 0 80 0 1.02 100 1 197 69 0 0 0 0 0 0 0 0 0 0 0; % U197 + 14 0 35.3 200 -50 0.98 100 1 0 0 0 0 0 0 0 0 0 0 0 0 0; % SynCond + 15 12 0 6 0 1.014 100 1 12 2.4 0 0 0 0 0 0 0 0 0 0 0; % U12 + 15 12 0 6 0 1.014 100 1 12 2.4 0 0 0 0 0 0 0 0 0 0 0; % U12 + 15 12 0 6 0 1.014 100 1 12 2.4 0 0 0 0 0 0 0 0 0 0 0; % U12 + 15 12 0 6 0 1.014 100 1 12 2.4 0 0 0 0 0 0 0 0 0 0 0; % U12 + 15 12 0 6 0 1.014 100 1 12 2.4 0 0 0 0 0 0 0 0 0 0 0; % U12 + 15 155 0 80 -50 1.014 100 1 155 54.3 0 0 0 0 0 0 0 0 0 0 0; % U155 + 16 155 0 80 -50 1.017 100 1 155 54.3 0 0 0 0 0 0 0 0 0 0 0; % U155 + 18 400 0 200 -50 1.05 100 1 400 100 0 0 0 0 0 0 0 0 0 0 0; % U400 + 21 400 0 200 -50 1.05 100 1 400 100 0 0 0 0 0 0 0 0 0 0 0; % U400 + 22 50 0 16 -10 1.05 100 1 50 10 0 0 0 0 0 0 0 0 0 0 0; % U50 + 22 50 0 16 -10 1.05 100 1 50 10 0 0 0 0 0 0 0 0 0 0 0; % U50 + 22 50 0 16 -10 1.05 100 1 50 10 0 0 0 0 0 0 0 0 0 0 0; % U50 + 22 50 0 16 -10 1.05 100 1 50 10 0 0 0 0 0 0 0 0 0 0 0; % U50 + 22 50 0 16 -10 1.05 100 1 50 10 0 0 0 0 0 0 0 0 0 0 0; % U50 + 22 50 0 16 -10 1.05 100 1 50 10 0 0 0 0 0 0 0 0 0 0 0; % U50 + 23 155 0 80 -50 1.05 100 1 155 54.3 0 0 0 0 0 0 0 0 0 0 0; % U155 + 23 155 0 80 -50 1.05 100 1 155 54.3 0 0 0 0 0 0 0 0 0 0 0; % U155 + 23 350 0 150 -25 1.05 100 1 350 140 0 0 0 0 0 0 0 0 0 0 0; % U350 +]; + +%% branch data +% fbus tbus r x b rateA rateB rateC ratio angle status angmin angmax +mpc.branch = [ + 1 2 0.0026 0.0139 0.4611 175 250 200 0 0 1 -360 360; + 1 3 0.0546 0.2112 0.0572 175 208 220 0 0 1 -360 360; + 1 5 0.0218 0.0845 0.0229 175 208 220 0 0 1 -360 360; + 2 4 0.0328 0.1267 0.0343 175 208 220 0 0 1 -360 360; + 2 6 0.0497 0.192 0.052 175 208 220 0 0 1 -360 360; + 3 9 0.0308 0.119 0.0322 175 208 220 0 0 1 -360 360; + 3 24 0.0023 0.0839 0 400 510 600 1.03 0 1 -360 360; + 4 9 0.0268 0.1037 0.0281 175 208 220 0 0 1 -360 360; + 5 10 0.0228 0.0883 0.0239 175 208 220 0 0 1 -360 360; + 6 10 0.0139 0.0605 2.459 175 193 200 0 0 1 -360 360; + 7 8 0.0159 0.0614 0.0166 175 208 220 0 0 1 -360 360; + 8 9 0.0427 0.1651 0.0447 175 208 220 0 0 1 -360 360; + 8 10 0.0427 0.1651 0.0447 175 208 220 0 0 1 -360 360; + 9 11 0.0023 0.0839 0 400 510 600 1.03 0 1 -360 360; + 9 12 0.0023 0.0839 0 400 510 600 1.03 0 1 -360 360; + 10 11 0.0023 0.0839 0 400 510 600 1.02 0 1 -360 360; + 10 12 0.0023 0.0839 0 400 510 600 1.02 0 1 -360 360; + 11 13 0.0061 0.0476 0.0999 500 600 625 0 0 1 -360 360; + 11 14 0.0054 0.0418 0.0879 500 625 625 0 0 1 -360 360; + 12 13 0.0061 0.0476 0.0999 500 625 625 0 0 1 -360 360; + 12 23 0.0124 0.0966 0.203 500 625 625 0 0 1 -360 360; + 13 23 0.0111 0.0865 0.1818 500 625 625 0 0 1 -360 360; + 14 16 0.005 0.0389 0.0818 500 625 625 0 0 1 -360 360; + 15 16 0.0022 0.0173 0.0364 500 600 625 0 0 1 -360 360; + 15 21 0.0063 0.049 0.103 500 600 625 0 0 1 -360 360; + 15 21 0.0063 0.049 0.103 500 600 625 0 0 1 -360 360; + 15 24 0.0067 0.0519 0.1091 500 600 625 0 0 1 -360 360; + 16 17 0.0033 0.0259 0.0545 500 600 625 0 0 1 -360 360; + 16 19 0.003 0.0231 0.0485 500 600 625 0 0 1 -360 360; + 17 18 0.0018 0.0144 0.0303 500 600 625 0 0 1 -360 360; + 17 22 0.0135 0.1053 0.2212 500 600 625 0 0 1 -360 360; + 18 21 0.0033 0.0259 0.0545 500 600 625 0 0 1 -360 360; + 18 21 0.0033 0.0259 0.0545 500 600 625 0 0 1 -360 360; + 19 20 0.0051 0.0396 0.0833 500 600 625 0 0 1 -360 360; + 19 20 0.0051 0.0396 0.0833 500 600 625 0 0 1 -360 360; + 20 23 0.0028 0.0216 0.0455 500 600 625 0 0 1 -360 360; + 20 23 0.0028 0.0216 0.0455 500 600 625 0 0 1 -360 360; + 21 22 0.0087 0.0678 0.1424 500 600 625 0 0 1 -360 360; +]; + +%%----- OPF Data -----%% +%% generator cost data +% 1 startup shutdown n x1 y1 ... xn yn +% 2 startup shutdown n c(n-1) ... c0 +mpc.gencost = [ % bus Pmin Pmax Qmin Qmax Unit Code + 2 1500 0 3 0 130 400.6849; % 1 16 20 0 10 U20 + 2 1500 0 3 0 130 400.6849; % 1 16 20 0 10 U20 + 2 1500 0 3 0.014142 16.0811 212.3076; % 1 15.2 76 -25 30 U76 + 2 1500 0 3 0.014142 16.0811 212.3076; % 1 15.2 76 -25 30 U76 + 2 1500 0 3 0 130 400.6849; % 2 16 20 0 10 U20 + 2 1500 0 3 0 130 400.6849; % 2 16 20 0 10 U20 + 2 1500 0 3 0.014142 16.0811 212.3076; % 2 15.2 76 -25 30 U76 + 2 1500 0 3 0.014142 16.0811 212.3076; % 2 15.2 76 -25 30 U76 + 2 1500 0 3 0.052672 43.6615 781.521; % 7 25 100 0 60 U100 + 2 1500 0 3 0.052672 43.6615 781.521; % 7 25 100 0 60 U100 + 2 1500 0 3 0.052672 43.6615 781.521; % 7 25 100 0 60 U100 + 2 1500 0 3 0.00717 48.5804 832.7575; % 13 69 197 0 80 U197 + 2 1500 0 3 0.00717 48.5804 832.7575; % 13 69 197 0 80 U197 + 2 1500 0 3 0.00717 48.5804 832.7575; % 13 69 197 0 80 U197 + 2 1500 0 3 0 0 0; % 14 SynCond + 2 1500 0 3 0.328412 56.564 86.3852; % 15 2.4 12 0 6 U12 + 2 1500 0 3 0.328412 56.564 86.3852; % 15 2.4 12 0 6 U12 + 2 1500 0 3 0.328412 56.564 86.3852; % 15 2.4 12 0 6 U12 + 2 1500 0 3 0.328412 56.564 86.3852; % 15 2.4 12 0 6 U12 + 2 1500 0 3 0.328412 56.564 86.3852; % 15 2.4 12 0 6 U12 + 2 1500 0 3 0.008342 12.3883 382.2391; % 15 54.3 155 -50 80 U155 + 2 1500 0 3 0.008342 12.3883 382.2391; % 16 54.3 155 -50 80 U155 + 2 1500 0 3 0.000213 4.4231 395.3749; % 18 100 400 -50 200 U400 + 2 1500 0 3 0.000213 4.4231 395.3749; % 21 100 400 -50 200 U400 + 2 1500 0 3 0 0.001 0.001; % 22 10 50 -10 16 U50 + 2 1500 0 3 0 0.001 0.001; % 22 10 50 -10 16 U50 + 2 1500 0 3 0 0.001 0.001; % 22 10 50 -10 16 U50 + 2 1500 0 3 0 0.001 0.001; % 22 10 50 -10 16 U50 + 2 1500 0 3 0 0.001 0.001; % 22 10 50 -10 16 U50 + 2 1500 0 3 0 0.001 0.001; % 22 10 50 -10 16 U50 + 2 1500 0 3 0.008342 12.3883 382.2391; % 23 54.3 155 -50 80 U155 + 2 1500 0 3 0.008342 12.3883 382.2391; % 23 54.3 155 -50 80 U155 + 2 1500 0 3 0.004895 11.8495 665.1094; % 23 140 350 -25 150 U350 +]; diff --git a/pandapower/test/converter/test_from_mpc.py b/pandapower/test/converter/test_from_mpc.py index 236f808c8..005119863 100644 --- a/pandapower/test/converter/test_from_mpc.py +++ b/pandapower/test/converter/test_from_mpc.py @@ -12,6 +12,12 @@ import pandapower.networks as pn from pandapower.converter import from_mpc +try: + import matpowercaseframes + matpowercaseframes_imported = True +except ImportError: + matpowercaseframes_imported = False + try: import pandaplan.core.pplog as logging except ImportError: @@ -20,12 +26,12 @@ logger = logging.getLogger(__name__) -def test_from_mpc(): +def test_from_mpc_mat(): case24 = pn.case24_ieee_rts() pp.set_user_pf_options(case24) this_folder = os.path.join(pp.pp_dir, "test", "converter") - mat_case_path = os.path.join(this_folder, 'case24_ieee_rts.mat') - case24_from_mpc = from_mpc(mat_case_path, f_hz=60, casename_mpc_file='mpc', tap_side="hv") + mat_case = os.path.join(this_folder, 'case24_ieee_rts.mat') + case24_from_mpc = from_mpc(mat_case, f_hz=60, casename_mpc_file='mpc', tap_side="hv") pp.runpp(case24) pp.runpp(case24_from_mpc) @@ -34,5 +40,21 @@ def test_from_mpc(): assert pp.nets_equal(case24, case24_from_mpc, check_only_results=True) +@pytest.mark.skipif(not matpowercaseframes_imported, + reason="matpowercaseframes is needed to convert .m files.") +def test_from_mpc_m(): + this_folder = os.path.join(pp.pp_dir, "test", "converter") + mat_case = os.path.join(this_folder, 'case24_ieee_rts.mat') + m_case = os.path.join(this_folder, 'case24_ieee_rts.m') + case24_mat = from_mpc(mat_case, f_hz=60, casename_mpc_file='mpc', tap_side="hv") + case24_m = from_mpc(m_case, f_hz=60, tap_side="hv") + + pp.runpp(case24_mat) + pp.runpp(case24_m) + + assert case24_m.converged + assert pp.nets_equal(case24_mat, case24_m) + + if __name__ == '__main__': pytest.main(["test_from_mpc.py"]) diff --git a/setup.py b/setup.py index bb39b8139..80c2471e0 100644 --- a/setup.py +++ b/setup.py @@ -57,11 +57,13 @@ "performance": ["ortools"], # , "lightsim2grid"], "fileio": ["xlsxwriter", "openpyxl", "cryptography", "geopandas"], # "fiona" is a depedency of geopandas and so already available + "converter": ["matpowercaseframes"], "all": ["numpydoc", "sphinx", "sphinx_rtd_theme", "plotly", "matplotlib", "python-igraph", "geopandas", "pytest", "pytest-xdist", "ortools", # lightsim2grid, - "xlsxwriter", "openpyxl", "cryptography" + "xlsxwriter", "openpyxl", "cryptography", + "matpowercaseframes" ]}, # "shapely", "pyproj", "fiona" are depedencies of geopandas and so already available # "hashlib", "zlib", "base64" produce installing problems, so it is not included packages=find_packages(),