From f955c3e46ab92d33a4373b0ed989e67059bb090c Mon Sep 17 00:00:00 2001 From: Wei <58371015+weigao-123@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:57:59 -0700 Subject: [PATCH 01/20] Typo in DAE Scalar Descriptions --- andes/variables/dae.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/andes/variables/dae.py b/andes/variables/dae.py index 87e842cc0..51b8ca8f9 100644 --- a/andes/variables/dae.py +++ b/andes/variables/dae.py @@ -280,7 +280,7 @@ class DAE: +-----------+---------------------------------------------+ | Scalar | Description | +===========+=============================================+ - | m | The number of algebraic variables/equations | + | m | The number of state variables/equations | +-----------+---------------------------------------------+ | n | The number of algebraic variables/equations | +-----------+---------------------------------------------+ From f73b12465d36c7f0bff4072ef0709bde37f7e710 Mon Sep 17 00:00:00 2001 From: Wei <58371015+weigao-123@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:36:13 -0700 Subject: [PATCH 02/20] Typo in DAE Scalar Descriptions --- andes/variables/dae.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/andes/variables/dae.py b/andes/variables/dae.py index 51b8ca8f9..845121d60 100644 --- a/andes/variables/dae.py +++ b/andes/variables/dae.py @@ -280,9 +280,9 @@ class DAE: +-----------+---------------------------------------------+ | Scalar | Description | +===========+=============================================+ - | m | The number of state variables/equations | + | m | The number of algebraic variables/equations | +-----------+---------------------------------------------+ - | n | The number of algebraic variables/equations | + | n | The number of state variables/equations | +-----------+---------------------------------------------+ | o | The number of limiter state flags | +-----------+---------------------------------------------+ From 732e8b7b6b3657779ebb7a353190d91701d6ba27 Mon Sep 17 00:00:00 2001 From: Wei <58371015+weigao-123@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:22:41 -0700 Subject: [PATCH 03/20] Typo in DAE Scalar Descriptions --- andes/variables/dae.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/andes/variables/dae.py b/andes/variables/dae.py index 845121d60..6d02ece62 100644 --- a/andes/variables/dae.py +++ b/andes/variables/dae.py @@ -282,7 +282,7 @@ class DAE: +===========+=============================================+ | m | The number of algebraic variables/equations | +-----------+---------------------------------------------+ - | n | The number of state variables/equations | + | n | The number of state variables/equations | +-----------+---------------------------------------------+ | o | The number of limiter state flags | +-----------+---------------------------------------------+ From 7ea376ea12c202894773bfb56e6ec33d16cd0978 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Sat, 16 Mar 2024 00:20:21 +0000 Subject: [PATCH 04/20] Add placeholder field `amachint` for PSS/E data --- andes/models/static/pv.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/andes/models/static/pv.py b/andes/models/static/pv.py index 9c052b449..65e070faa 100644 --- a/andes/models/static/pv.py +++ b/andes/models/static/pv.py @@ -36,6 +36,18 @@ def __init__(self): self.ra = NumParam(default=0.0, info='armature resistance', tex_name='r_a') self.xs = NumParam(default=0.3, info='armature reactance', tex_name='x_s') + # `amachint`: data holder for PSS/E file; not yet used in ANDES + # 0: conventional + # 1: renewable standard QT/ QB limits + # 2: renewable +, - Qlimits based on WPF + # 3: renewable, fixed Qlimit based on WPF + # 4: Infeed machine + self.amachint = NumParam(default=0, + info='Machine ctrl. mode [PSS/E]', + tex_name=r'a_{machint}', + export=False, + ) + class PVModel(Model): """ From c8373e71dfdd2f3f44f4c99f8241fea2e602f5bb Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Sat, 16 Mar 2024 02:31:07 +0000 Subject: [PATCH 05/20] Add `wmod` field for psse data. --- andes/io/psse.py | 14 ++++++++++++-- andes/models/static/pv.py | 13 +++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/andes/io/psse.py b/andes/io/psse.py index 176637e49..0d9be241b 100644 --- a/andes/io/psse.py +++ b/andes/io/psse.py @@ -402,8 +402,16 @@ def _parse_fshunt_v33(raw, system): def _parse_gen_v33(raw, system, sw): - # 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11, 12, 13, 14, 15, 16,17,18,19 - # I,ID,PG,QG,QT,QB,VS,IREG,MBASE,ZR,ZX,RT,XT,GTAP,STAT,RMPCT,PT,PB,O1,F1 + """ + Helper function for parsing static generator section. + """ + + # 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + # I, ID, PG, QG, QT, QB, VS, IREG, MBASE, ZR, ZX, RT, XT, GTAP, STAT, + # 15, 16, 17, 18, 19, ..., 26, 27 + # RMPCT, PT, PB, O1, F1, ..., O4, F4, WMOD, WPF + # The columns above for v33 is different from the manual of v34.5, which includes two new columns: + # `NREG`` at 8 and `BSLOD` before `O1` mva = system.config.mva out = defaultdict(list) @@ -417,6 +425,7 @@ def _parse_gen_v33(raw, system, sw): gen_mva = data[8] gen_idx += 1 status = data[14] + wmod = data[26] if len(data) >= 26 else 0 param = {'Sn': gen_mva, 'Vn': vn, 'u': status, 'bus': bus, 'subidx': subidx, @@ -428,6 +437,7 @@ def _parse_gen_v33(raw, system, sw): 'v0': data[6], 'ra': data[9], # ra - armature resistance 'xs': data[10], # xs - synchronous reactance + 'wmod': wmod, # generator control mode } if data[0] in sw.keys(): diff --git a/andes/models/static/pv.py b/andes/models/static/pv.py index 65e070faa..f3280719e 100644 --- a/andes/models/static/pv.py +++ b/andes/models/static/pv.py @@ -36,17 +36,18 @@ def __init__(self): self.ra = NumParam(default=0.0, info='armature resistance', tex_name='r_a') self.xs = NumParam(default=0.3, info='armature reactance', tex_name='x_s') - # `amachint`: data holder for PSS/E file; not yet used in ANDES + # `wmod`: generator model included in PSS/E file; not yet used in ANDES # 0: conventional # 1: renewable standard QT/ QB limits # 2: renewable +, - Qlimits based on WPF # 3: renewable, fixed Qlimit based on WPF # 4: Infeed machine - self.amachint = NumParam(default=0, - info='Machine ctrl. mode [PSS/E]', - tex_name=r'a_{machint}', - export=False, - ) + self.wmod = NumParam(default=0, + info='Machine ctrl. mode [PSS/E]', + tex_name=r'w_{mod}', + unit='int', + export=False, + ) class PVModel(Model): From 308ecd8c39400f5beba0eb2410d6905a600377b7 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Sat, 16 Mar 2024 02:34:41 +0000 Subject: [PATCH 06/20] Update notes. --- docs/source/release-notes.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 4aa228f9c..794ec46d3 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -9,6 +9,11 @@ The APIs before v3.0.0 are in beta and may change without prior notice. v1.9 Notes ========== +v1.9.2 (2024-03-XX) +------------------- +- Improve PSS/E parser for the `wmod` field in the static generator + section. + v1.9.1 (2024-02-04) ------------------- This is a hotfix to pass ``pip check`` for KVXOPT version. From 22a9dd1b76e45cba2ef534084acddd4f4de04454 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Sun, 17 Mar 2024 01:47:54 +0000 Subject: [PATCH 07/20] Update github remote name to `curent`. --- CONTRIBUTING.rst | 8 ++++---- docs/source/conf.py | 2 +- docs/source/examples/index.rst | 4 ++-- docs/source/getting_started/install.rst | 4 ++-- docs/source/getting_started/overview.rst | 6 +++--- docs/source/getting_started/testcases/index.rst | 2 +- docs/source/getting_started/tutorial/cheatsheet.rst | 2 +- docs/source/getting_started/tutorial/cli.rst | 2 +- docs/source/index.rst | 2 +- examples/ex2.ipynb | 8 ++++---- examples/ex5.ipynb | 4 ++-- setup.py | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 68877f5af..b8eb5c641 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -13,7 +13,7 @@ Types of Contributions Report Bugs ~~~~~~~~~~~ -Report bugs at https://github.com/cuihantao/andes/issues. +Report bugs at https://github.com/curent/andes/issues. If you are reporting a bug, please include: @@ -42,7 +42,7 @@ or even on the web in blog posts, articles, and such. Submit Feedback ~~~~~~~~~~~~~~~ -The best way to send feedback is to file an issue at https://github.com/cuihantao/andes/issues. +The best way to send feedback is to file an issue at https://github.com/curent/andes/issues. If you are proposing a feature: @@ -97,8 +97,8 @@ Before you submit a pull request, check that it meets these guidelines: 2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst. -3. The pull request should work for Python 3.6 and up. Check - https://github.com/cuihantao/andes/actions +3. The pull request should work for Python 3.8 and up. Check + https://github.com/curent/andes/actions and make sure that the tests pass for all supported Python versions. ============ diff --git a/docs/source/conf.py b/docs/source/conf.py index 0b42fffac..1090116c6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -125,7 +125,7 @@ html_context = { "github_url": "https://github.com", - "github_user": "cuihantao", + "github_user": "curent", "github_repo": "andes", "github_version": "master", "doc_path": "docs/source", diff --git a/docs/source/examples/index.rst b/docs/source/examples/index.rst index dd725326b..c3acd93fb 100644 --- a/docs/source/examples/index.rst +++ b/docs/source/examples/index.rst @@ -6,9 +6,9 @@ Examples A collection of examples are presented to supplement the tutorial. The examples below are identical to the Jupyter Notebook in the ``examples`` folder of the repository -`here `__. You +`here `__. You can run the examples in a live Jupyter Notebook online using -`Binder `__. +`Binder `__. .. toctree:: :maxdepth: 2 diff --git a/docs/source/getting_started/install.rst b/docs/source/getting_started/install.rst index 005aeaefc..64cbcbb34 100644 --- a/docs/source/getting_started/install.rst +++ b/docs/source/getting_started/install.rst @@ -112,7 +112,7 @@ from either your fork or the original repository. Clone the repository with .. code:: bash - git clone https://github.com/cuihantao/andes + git clone https://github.com/curent/andes .. note:: @@ -120,7 +120,7 @@ from either your fork or the original repository. Clone the repository with update the source code and perform version control. Alternatively, you can download the ANDES source code from -https://github.com/cuihantao/andes and extract all files to the path of your +https://github.com/curent/andes and extract all files to the path of your choice. Although works, this method is discouraged because tracking changes and pushing back code edits will require significant manual efforts. diff --git a/docs/source/getting_started/overview.rst b/docs/source/getting_started/overview.rst index 434b1025d..069afbfe9 100644 --- a/docs/source/getting_started/overview.rst +++ b/docs/source/getting_started/overview.rst @@ -34,13 +34,13 @@ ANDES is currently under active development. To get involved, * Follow the tutorial at `https://andes.readthedocs.io `_ * Checkout the Notebook examples in the - `examples folder `_ + `examples folder `_ * Try ANDES in Jupyter Notebook - `with Binder `_ + `with Binder `_ * Download the PDF manual at `download `_ * Report issues in the - `GitHub issues page `_ + `GitHub issues page `_ * Learn version control with `the command-line git `_ or `GitHub Desktop `_ diff --git a/docs/source/getting_started/testcases/index.rst b/docs/source/getting_started/testcases/index.rst index b25ce7307..b81d569bd 100644 --- a/docs/source/getting_started/testcases/index.rst +++ b/docs/source/getting_started/testcases/index.rst @@ -8,7 +8,7 @@ Test Cases ANDES ships with with test cases in the ``andes/cases`` folder. The cases can be found in the `online repository`_. -.. _`online repository`: https://github.com/cuihantao/andes/tree/master/andes/cases +.. _`online repository`: https://github.com/curent/andes/tree/master/andes/cases Summary ======= diff --git a/docs/source/getting_started/tutorial/cheatsheet.rst b/docs/source/getting_started/tutorial/cheatsheet.rst index dc5cc36a7..3caaa1ac3 100644 --- a/docs/source/getting_started/tutorial/cheatsheet.rst +++ b/docs/source/getting_started/tutorial/cheatsheet.rst @@ -5,4 +5,4 @@ A cheatsheet is available for quick lookup of supported commands. View the PDF version at -https://www.cheatography.com//cuihantao/cheat-sheets/andes-for-power-system-simulation/pdf/ +https://www.cheatography.com/cuihantao/cheat-sheets/andes-for-power-system-simulation/pdf/ diff --git a/docs/source/getting_started/tutorial/cli.rst b/docs/source/getting_started/tutorial/cli.rst index 16d78fb46..21ea30e41 100644 --- a/docs/source/getting_started/tutorial/cli.rst +++ b/docs/source/getting_started/tutorial/cli.rst @@ -252,7 +252,7 @@ Power flow If you have cloned the ANDES repository, it can be found in ``andes/cases/kundur`` in the source code folder. You can also download it from - `here `_. + `here `_. To run power flow, change to the directory containing ``kundur_full.xlsx``, and execute the following in the command line: diff --git a/docs/source/index.rst b/docs/source/index.rst index 1c317eb13..fa174d959 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -21,7 +21,7 @@ ANDES documentation .. _`Report Issues`: https://github.com/CURENT/andes/issues .. _`Q&A`: https://github.com/CURENT/andes/discussions .. _`Binary Installer`: https://pypi.org/project/andes/ -.. _`Try in Jupyter Notebooks`: https://mybinder.org/v2/gh/cuihantao/andes/master +.. _`Try in Jupyter Notebooks`: https://mybinder.org/v2/gh/curent/andes/master .. _`LTB Repository`: https://github.com/CURENT/ .. image:: /images/sponsors/CURENT_Logo_NameOnTrans.png diff --git a/examples/ex2.ipynb b/examples/ex2.ipynb index 421e9b934..2c651f375 100644 --- a/examples/ex2.ipynb +++ b/examples/ex2.ipynb @@ -78,7 +78,7 @@ "The ANDES xlsx file is the best supported format. Other formats can be converted to the xlsx format.\n", "\n", "See the link below for more about format conversion.\n", - "https://github.com/cuihantao/andes/blob/master/README.md#format-converter" + "https://github.com/curent/andes/blob/master/README.md#format-converter" ] }, { @@ -181,8 +181,8 @@ } ], "source": [ - "ss = andes.load(get_case('kundur/kundur_full.xlsx'), \n", - " default_config=True, \n", + "ss = andes.load(get_case('kundur/kundur_full.xlsx'),\n", + " default_config=True,\n", " setup=False)" ] }, @@ -2783,4 +2783,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/examples/ex5.ipynb b/examples/ex5.ipynb index c9f13bf74..ee5f93233 100644 --- a/examples/ex5.ipynb +++ b/examples/ex5.ipynb @@ -13,7 +13,7 @@ "source": [ "This example notebook is a supplement to the ANDES tutorial. Make sure you have read the tutorial on using the CLI first.\n", "\n", - "A brief version can be found at https://github.com/cuihantao/andes/blob/master/README.md#run-simulations" + "A brief version can be found at https://github.com/curent/andes/blob/master/README.md#run-simulations" ] }, { @@ -1096,4 +1096,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/setup.py b/setup.py index c7d7cb0c1..938e88fc2 100644 --- a/setup.py +++ b/setup.py @@ -82,7 +82,7 @@ def get_extra_requires(filename, add_all=True): long_description_content_type='text/markdown', author="Hantao Cui", author_email='cuihantao@gmail.com', - url='https://github.com/cuihantao/andes', + url='https://github.com/curent/andes', packages=find_packages(exclude=[]), entry_points={ 'console_scripts': [ From a00f05fc111bb2a42d649c691f1a7c7ddfe78d83 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Mon, 18 Mar 2024 16:31:03 +0000 Subject: [PATCH 08/20] Fix PSS/E line parser to include status field `u`. --- andes/io/psse.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/andes/io/psse.py b/andes/io/psse.py index 0d9be241b..fd2b4297d 100644 --- a/andes/io/psse.py +++ b/andes/io/psse.py @@ -453,12 +453,14 @@ def _parse_gen_v33(raw, system, sw): def _parse_line_v33(raw, system): # + # 1,2, 3,4,5,6, 7, 8, 9,10,11,12,13,14, 15,16,17 # I,J,CKT,R,X,B,RATEA,RATEB,RATEC,GI,BI,GJ,BJ,ST,LEN,O1,F1,...,O4,F4 # out = defaultdict(list) for data in raw['branch']: param = { + 'u': data[14], 'bus1': data[0], 'bus2': data[1], 'r': data[3], 'x': data[4], 'b': data[5], 'rate_a': data[6], 'rate_b': data[7], 'rate_c': data[8], From b79a8cfad4bbb939cf66e5890a2df4e0ba8bd8ee Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Mon, 18 Mar 2024 17:05:33 +0000 Subject: [PATCH 09/20] Fix an indexing issue with Line. --- andes/io/psse.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/andes/io/psse.py b/andes/io/psse.py index fd2b4297d..178638499 100644 --- a/andes/io/psse.py +++ b/andes/io/psse.py @@ -453,14 +453,14 @@ def _parse_gen_v33(raw, system, sw): def _parse_line_v33(raw, system): # - # 1,2, 3,4,5,6, 7, 8, 9,10,11,12,13,14, 15,16,17 + # 0,1, 2,3,4,5, 6, 7, 8, 9,10,11,12,13, 14,15,16 # I,J,CKT,R,X,B,RATEA,RATEB,RATEC,GI,BI,GJ,BJ,ST,LEN,O1,F1,...,O4,F4 # out = defaultdict(list) for data in raw['branch']: param = { - 'u': data[14], + 'u': data[13], 'bus1': data[0], 'bus2': data[1], 'r': data[3], 'x': data[4], 'b': data[5], 'rate_a': data[6], 'rate_b': data[7], 'rate_c': data[8], From 5b7acbdb660cc4813759453ec3e69f2112b84820 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Tue, 19 Mar 2024 15:58:54 +0000 Subject: [PATCH 10/20] [WIP] Add function for generating network admittance matrix. --- andes/models/line/line.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/andes/models/line/line.py b/andes/models/line/line.py index bec300150..7172e5be3 100644 --- a/andes/models/line/line.py +++ b/andes/models/line/line.py @@ -3,8 +3,10 @@ """ import numpy as np + from andes.core import (ModelData, IdxParam, NumParam, DataParam, Model, ExtAlgeb, ConstService) +from andes.shared import spmatrix class LineData(ModelData): @@ -241,3 +243,30 @@ def get_tf_idx(self): """ return np.array(self.idx.v)[self.istf] + + def build_y(self): + """ + Build bus admittance matrix. Store the matrix in ``self.Y``. + + Returns + ------- + Y : spmatrix + Bus admittance matrix. + """ + + nb = self.system.Bus.n + + y1 = self.u.v * (self.g1.v + self.b1.v * 1j) + y2 = self.u.v * (self.g2.v + self.b2.v * 1j) + y12 = self.u.v / (self.r.v + self.x.v * 1j) + m = self.tap.v * np.exp(1j * self.phi.v) + m2 = self.tap.v**2 + mconj = np.conj(m) + + # build self and mutual admittances into Y + self.Y = spmatrix((y12 + y1 / m2), self.a1.a, self.a1.a, (nb, nb), 'z') + self.Y -= spmatrix(y12 / mconj, self.a1.a, self.a2.a, (nb, nb), 'z') + self.Y -= spmatrix(y12 / m, self.a2.a, self.a1.a, (nb, nb), 'z') + self.Y += spmatrix(y12 + y2, self.a2.a, self.a2.a, (nb, nb), 'z') + + return self.Y From b4624e26f6109900278a3a051007ef7181284626 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Wed, 20 Mar 2024 22:32:41 +0000 Subject: [PATCH 11/20] Add note on Octave installation. --- tests/test_pflow_matpower.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_pflow_matpower.py b/tests/test_pflow_matpower.py index b6b223f21..fe9ccd764 100644 --- a/tests/test_pflow_matpower.py +++ b/tests/test_pflow_matpower.py @@ -33,7 +33,9 @@ def test_pflow_mpc_process(self): case_path = [get_case(os.path.join('matpower', item)) for item in self.cases] andes.run(case_path, no_output=True, ncpu=2, pool=False, verbose=40, default_config=True) - +# Note: +# On Ubuntu, Octave needs to be installed with `apt`. +# The Octave installed with `snap` will run into I/O error. @unittest.skipUnless(MATPOWER_WORKING, "MATPOWER not available") class TestMATPOWEROct2Py(unittest.TestCase): From 46c1adb3d62618f7241e09446ef4e63c6f5a0693 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Wed, 20 Mar 2024 23:03:25 +0000 Subject: [PATCH 12/20] Update variable names for clarity. --- andes/io/matpower.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/andes/io/matpower.py b/andes/io/matpower.py index 2a6d06234..ea9be607b 100644 --- a/andes/io/matpower.py +++ b/andes/io/matpower.py @@ -272,12 +272,12 @@ def mpc2system(mpc: dict, system) -> bool: if (data[8] == 0.0) or (data[8] == 1.0 and data[9] == 0.0): # not a transformer tf = False - ratio = 1 - angle = 0 + tap_raio = 1 + phase_shift = 0 else: tf = True - ratio = data[8] - angle = data[9] * deg2rad + tap_raio = data[8] + phase_shift = data[9] * deg2rad vf = system.Bus.Vn.v[system.Bus.idx2uid(fbus)] vt = system.Bus.Vn.v[system.Bus.idx2uid(tbus)] @@ -285,7 +285,7 @@ def mpc2system(mpc: dict, system) -> bool: Vn1=vf, Vn2=vt, bus1=fbus, bus2=tbus, r=r, x=x, b=b, - trans=tf, tap=ratio, phi=angle, + trans=tf, tap=tap_raio, phi=phase_shift, rate_a=rate_a, rate_b=rate_b, rate_c=rate_c) if ('bus_name' in mpc) and (len(mpc['bus_name']) == len(system.Bus.name.v)): From 4a8a7f46a89bf764859eba4817b13b17c26f2910 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Wed, 20 Mar 2024 23:04:01 +0000 Subject: [PATCH 13/20] Add test code. --- tests/test_pflow_matpower.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/tests/test_pflow_matpower.py b/tests/test_pflow_matpower.py index fe9ccd764..ab9ffa5e7 100644 --- a/tests/test_pflow_matpower.py +++ b/tests/test_pflow_matpower.py @@ -3,8 +3,9 @@ import andes import numpy as np -from andes.utils.paths import get_case +from andes.linsolvers.scipy import spmatrix_to_csc from andes.shared import deg2rad +from andes.utils.paths import get_case andes.main.config_logger(30, file=True) @@ -34,8 +35,10 @@ def test_pflow_mpc_process(self): andes.run(case_path, no_output=True, ncpu=2, pool=False, verbose=40, default_config=True) # Note: -# On Ubuntu, Octave needs to be installed with `apt`. +# On Ubuntu, Octave needs to be installed with `apt`. # The Octave installed with `snap` will run into I/O error. + + @unittest.skipUnless(MATPOWER_WORKING, "MATPOWER not available") class TestMATPOWEROct2Py(unittest.TestCase): @@ -48,7 +51,8 @@ def test_pflow_against_matpower_extra_test(self): ss = andes.run(andes.get_case(os.path.join("matpower", name)), no_output=True, - default_config=True) + default_config=True, + ) m.eval('clear mpc') andes.interop.matpower.to_matpower(m, 'mpc', ss) @@ -63,3 +67,23 @@ def test_pflow_against_matpower_extra_test(self): np.testing.assert_almost_equal(v_mpc, v_andes, decimal=5) np.testing.assert_almost_equal(a_mpc, a_andes, decimal=5) + + def test_Bdc_against_matpower(self): + m = start_instance() + cases = ('case5.m', 'case14.m', 'case118.m') + + for name in cases: + ss = andes.load(andes.get_case(os.path.join("matpower", name)), + no_output=True, + default_config=True, + ) + m.eval('clear mpc') + andes.interop.matpower.to_matpower(m, 'mpc', ss) + m.eval("mpopt = mpoption('verbose', 0, 'out.all', 0);") + m.eval('Bdc = makeBdc(mpc, mpopt);') + Bdc = m.pull('Bdc') + + ss.Line.build_b("dcpf") + Bpp = spmatrix_to_csc(ss.Line.Bpp) + + assert(np.all(np.abs((Bpp + Bdc).data) < 1e-6)) From dfd6bb9f13aaab61d7054b7d5add65500db60be7 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Thu, 21 Mar 2024 02:08:02 +0000 Subject: [PATCH 14/20] Add function for building Bdc matrix like in MATPOWER. Update tests. --- andes/models/line/line.py | 142 ++++++++++++++++++++++++++++++++++- tests/test_pflow_matpower.py | 8 +- 2 files changed, 144 insertions(+), 6 deletions(-) diff --git a/andes/models/line/line.py b/andes/models/line/line.py index 7172e5be3..4158679de 100644 --- a/andes/models/line/line.py +++ b/andes/models/line/line.py @@ -243,7 +243,7 @@ def get_tf_idx(self): """ return np.array(self.idx.v)[self.istf] - + def build_y(self): """ Build bus admittance matrix. Store the matrix in ``self.Y``. @@ -255,7 +255,7 @@ def build_y(self): """ nb = self.system.Bus.n - + y1 = self.u.v * (self.g1.v + self.b1.v * 1j) y2 = self.u.v * (self.g2.v + self.b2.v * 1j) y12 = self.u.v / (self.r.v + self.x.v * 1j) @@ -270,3 +270,141 @@ def build_y(self): self.Y += spmatrix(y12 + y2, self.a2.a, self.a2.a, (nb, nb), 'z') return self.Y + + def build_b(self, method='fdpf'): + """ + build Bp and Bpp matrices for fast decoupled power flow and DC power flow. + + Results are saved to ``self.Bp`` and ``self.Bpp`` without return. + """ + self.build_Bp(method) + self.build_Bpp(method) + + def build_Bp(self, method='fdpf'): + """ + Function for building B' matrix. + + The result is saved to ``self.Bp`` and returned + + Parameters + ---------- + method : str + Method for building B' matrix. Choose from 'fdpf', 'fdbx', 'dcpf'. + Returns + ------- + Bp : spmatrix + B' matrix. + + """ + nb = self.system.Bus.n + + if method not in ("fdpf", "fdbx", "dcpf"): + raise ValueError(f"Invalid method {method}; choose from 'fdpf', 'fdbx', 'dcpf'") + + # Build B prime matrix -- FDPF + # `y1`` neglects line charging shunt, and g1 is usually 0 in HV lines + # `y2`` neglects line charging shunt, and g2 is usually 0 in HV lines + y1 = self.u.v * self.g1.v + y2 = self.u.v * self.g2.v + + # `m` neglected tap ratio + m = np.exp(self.phi.v * 1j) + mconj = np.conj(m) + m2 = np.ones(self.n) + + if method in ('fdxb', 'dcpf'): + # neglect line resistance in Bp in XB method + y12 = self.u.v / (self.x.v * 1j) + else: + y12 = self.u.v / (self.r.v + self.x.v * 1j) + + self.Bdc = spmatrix((y12 + y1) / m2, self.a1.a, self.a1.a, (nb, nb), 'z') + self.Bdc -= spmatrix(y12 / mconj, self.a1.a, self.a2.a, (nb, nb), 'z') + self.Bdc -= spmatrix(y12 / m, self.a2.a, self.a1.a, (nb, nb), 'z') + self.Bdc += spmatrix(y12 + y2, self.a2.a, self.a2.a, (nb, nb), 'z') + self.Bdc = self.Bdc.imag() + + for item in range(nb): + if abs(self.Bdc[item, item]) == 0: + self.Bdc[item, item] = 1e-6 + 0j + + return self.Bdc + + def build_Bpp(self, method='fdpf'): + """ + Function for building B'' matrix. + + The result is saved to ``self.Bpp`` and returned + + Parameters + ---------- + method : str + Method for building B'' matrix. Choose from 'fdpf', 'fdbx', 'dcpf'. + + Returns + ------- + Bpp : spmatrix + B'' matrix. + """ + + nb = self.system.Bus.n + + if method not in ("fdpf", "fdbx", "dcpf"): + raise ValueError(f"Invalid method {method}; choose from 'fdpf', 'fdbx', 'dcpf'") + + # Build B double prime matrix + # y1 neglected line charging shunt, and g1 is usually 0 in HV lines + # y2 neglected line charging shunt, and g2 is usually 0 in HV lines + # m neglected phase shifter + y1 = self.u.v * (self.g1.v + self.b1.v * 1j) + y2 = self.u.v * (self.g2.v + self.b2.v * 1j) + + m = self.tap.v + m2 = abs(m)**2 + + if method in ('fdbx', 'fdpf', 'dcpf'): + # neglect line resistance in Bpp in BX method + y12 = self.u.v / (self.x.v * 1j) + else: + y12 = self.u.v / (self.r.v + self.x.v * 1j) + + self.Bpp = spmatrix((y12 + y1) / m2, self.a1.a, self.a1.a, (nb, nb), 'z') + self.Bpp -= spmatrix(y12 / np.conj(m), self.a1.a, self.a2.a, (nb, nb), 'z') + self.Bpp -= spmatrix(y12 / m, self.a2.a, self.a1.a, (nb, nb), 'z') + self.Bpp += spmatrix(y12 + y2, self.a2.a, self.a2.a, (nb, nb), 'z') + self.Bpp = self.Bpp.imag() + + for item in range(nb): + if abs(self.Bpp[item, item]) == 0: + self.Bpp[item, item] = 1e-6 + 0j + + return self.Bpp + + def build_Bdc(self): + """ + The MATPOWER-flavor Bdc matrix for DC power flow. Saves results to `self.Bdc`. + + The method neglects line charging and line resistance. It retains tap ratio. + + Returns + ------- + Bdc : spmatrix + Bdc matrix. + """ + + nb = self.system.Bus.n + + y12 = self.u.v / (self.x.v * 1j) + y12 = y12 / self.tap.v + + self.Bdc = spmatrix(y12, self.a1.a, self.a1.a, (nb, nb), 'z') + self.Bdc -= spmatrix(y12, self.a1.a, self.a2.a, (nb, nb), 'z') + self.Bdc -= spmatrix(y12, self.a2.a, self.a1.a, (nb, nb), 'z') + self.Bdc += spmatrix(y12, self.a2.a, self.a2.a, (nb, nb), 'z') + self.Bdc = self.Bdc.imag() + + for item in range(nb): + if abs(self.Bdc[item, item]) == 0: + self.Bdc[item, item] = 1e-6 + + return self.Bdc diff --git a/tests/test_pflow_matpower.py b/tests/test_pflow_matpower.py index ab9ffa5e7..550f767d3 100644 --- a/tests/test_pflow_matpower.py +++ b/tests/test_pflow_matpower.py @@ -73,7 +73,7 @@ def test_Bdc_against_matpower(self): cases = ('case5.m', 'case14.m', 'case118.m') for name in cases: - ss = andes.load(andes.get_case(os.path.join("matpower", name)), + ss = andes.load('/home/hacui/repos/matpower/data/case14.m', no_output=True, default_config=True, ) @@ -83,7 +83,7 @@ def test_Bdc_against_matpower(self): m.eval('Bdc = makeBdc(mpc, mpopt);') Bdc = m.pull('Bdc') - ss.Line.build_b("dcpf") - Bpp = spmatrix_to_csc(ss.Line.Bpp) + ss.Line.build_Bdc() + Bp = spmatrix_to_csc(ss.Line.Bdc) - assert(np.all(np.abs((Bpp + Bdc).data) < 1e-6)) + np.testing.assert_array_almost_equal((Bp + Bdc).data, np.zeros_like((Bp + Bdc).data)) From 4f991fc543e8756c42055820117057fe34ed1697 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Thu, 21 Mar 2024 02:21:26 +0000 Subject: [PATCH 15/20] Update. --- docs/source/release-notes.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 794ec46d3..8cd56372f 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -13,6 +13,9 @@ v1.9.2 (2024-03-XX) ------------------- - Improve PSS/E parser for the `wmod` field in the static generator section. +- Added functions in `Line` for building network admittance matrix, `Bdc` + matrix for DC power flow, and `Bp` and `Bpp` matrices for fast decoupled + power flow. See ``build_y``, ``build_b`` and ``build_Bdc``. v1.9.1 (2024-02-04) ------------------- From 127dc462899e555dd44244b05343853ac59bc679 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Thu, 21 Mar 2024 02:23:24 +0000 Subject: [PATCH 16/20] Fix format. --- andes/models/line/line.py | 2 +- tests/test_pflow_matpower.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/andes/models/line/line.py b/andes/models/line/line.py index 4158679de..167b87cdc 100644 --- a/andes/models/line/line.py +++ b/andes/models/line/line.py @@ -383,7 +383,7 @@ def build_Bpp(self, method='fdpf'): def build_Bdc(self): """ The MATPOWER-flavor Bdc matrix for DC power flow. Saves results to `self.Bdc`. - + The method neglects line charging and line resistance. It retains tap ratio. Returns diff --git a/tests/test_pflow_matpower.py b/tests/test_pflow_matpower.py index 550f767d3..29e0e218d 100644 --- a/tests/test_pflow_matpower.py +++ b/tests/test_pflow_matpower.py @@ -85,5 +85,5 @@ def test_Bdc_against_matpower(self): ss.Line.build_Bdc() Bp = spmatrix_to_csc(ss.Line.Bdc) - + np.testing.assert_array_almost_equal((Bp + Bdc).data, np.zeros_like((Bp + Bdc).data)) From 4f533a0c9d84f1cae6c24a8608dd7a4ab8e6811a Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Mon, 25 Mar 2024 16:13:07 +0000 Subject: [PATCH 17/20] Save intermediate matrices. --- andes/routines/eig.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/andes/routines/eig.py b/andes/routines/eig.py index bdcd2cff1..94363688d 100644 --- a/andes/routines/eig.py +++ b/andes/routines/eig.py @@ -103,16 +103,18 @@ def _reduce(self, fx, fy, gx, gy, Tf, dense=True): spmatrix The reduced state matrix """ - gyx = matrix(gx) - self.solver.linsolve(gy, gyx) + self.gyx = matrix(gx) + self.solver.linsolve(gy, self.gyx) Tfnz = Tf + np.ones_like(Tf) * np.equal(Tf, 0.0) iTf = spdiag((1 / Tfnz).tolist()) + self.fxy = (fx - fy * self.gyx) + if dense: - return iTf * (fx - fy * gyx) + return iTf * self.fxy else: - return sparse(iTf * (fx - fy * gyx)) + return sparse(iTf * self.fxy) def _reorder(self): """ From a7b27f2e72ff002f1596e0eba3f9d744e3533c40 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Mon, 25 Mar 2024 16:13:22 +0000 Subject: [PATCH 18/20] Edit year. --- andes/system.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/andes/system.py b/andes/system.py index 379d84b02..1536cd87a 100644 --- a/andes/system.py +++ b/andes/system.py @@ -1,8 +1,8 @@ """ -System class for power system data and methods +System class for power system data and methods. """ -# [ANDES] (C)2015-2022 Hantao Cui +# [ANDES] (C)2015-2024 Hantao Cui # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From b1c5f2cf941fbf62494d67c8a9eed4d8d2c480fe Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Mon, 25 Mar 2024 17:23:29 +0000 Subject: [PATCH 19/20] Update. --- docs/source/release-notes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 8cd56372f..f84d942c0 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -13,6 +13,7 @@ v1.9.2 (2024-03-XX) ------------------- - Improve PSS/E parser for the `wmod` field in the static generator section. +- Consider line status when parsing PSS/E file. - Added functions in `Line` for building network admittance matrix, `Bdc` matrix for DC power flow, and `Bp` and `Bpp` matrices for fast decoupled power flow. See ``build_y``, ``build_b`` and ``build_Bdc``. From 7a87ad5d6ebfe8c954614f55815202f7f83e2f0d Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Mon, 25 Mar 2024 17:24:01 +0000 Subject: [PATCH 20/20] Update date. --- docs/source/release-notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index f84d942c0..d766892bc 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -9,7 +9,7 @@ The APIs before v3.0.0 are in beta and may change without prior notice. v1.9 Notes ========== -v1.9.2 (2024-03-XX) +v1.9.2 (2024-03-25) ------------------- - Improve PSS/E parser for the `wmod` field in the static generator section.