From c7de3f9535cb621406c4a5ceb24e63eeb39f813d Mon Sep 17 00:00:00 2001 From: J-K-Peng Date: Fri, 18 Apr 2025 14:16:10 -0700 Subject: [PATCH 01/11] pvd1 model debug PVD1 is designed to respond only to downward frequency deviations. Please update the frequency deadband to 999 to effectively disable upward frequency droop. --- andes/models/distributed/pvd1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/andes/models/distributed/pvd1.py b/andes/models/distributed/pvd1.py index 649caa4ef..788389882 100644 --- a/andes/models/distributed/pvd1.py +++ b/andes/models/distributed/pvd1.py @@ -324,7 +324,7 @@ def __init__(self, system, config): unit='Hz', tex_name='f_{dev}', ) - self.DB = DeadBand1(u=self.Fdev, center=0.0, lower=self.fdbd, upper=0.0, gain=self.ddn, + self.DB = DeadBand1(u=self.Fdev, center=0.0, lower=self.fdbd, upper=999.0, gain=self.ddn, info='frequency deviation deadband with gain', ) # outputs `Pdrp` self.DB.db.no_warn = True From 14ef67c3aa1ee199cb5e1cfcb2f0a7ceaee77684 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Wed, 4 Jun 2025 22:29:05 -0400 Subject: [PATCH 02/11] Add two models PVD2 and ESD2 --- andes/models/__init__.py | 3 ++- andes/models/distributed/__init__.py | 4 ++-- andes/models/distributed/esd1.py | 14 +++++++++++++ andes/models/distributed/pvd1.py | 30 ++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/andes/models/__init__.py b/andes/models/__init__.py index 094e939b9..614e26893 100644 --- a/andes/models/__init__.py +++ b/andes/models/__init__.py @@ -38,7 +38,8 @@ ('renewable', ['REPCA1']), ('renewable', ['WTDTA1', 'WTDS', 'WTARA1', 'WTPTA1', 'WTTQA1', 'WTARV1', 'REGCV1', 'REGCV2', 'REGF1', 'REGF2', 'REGF3']), - ('distributed', ['PVD1', 'ESD1', 'EV1', 'EV2', 'DGPRCT1', 'DGPRCTExt']), + ('distributed', ['PVD1', 'ESD1', 'EV1', 'EV2', 'DGPRCT1', 'DGPRCTExt', + 'PVD2', 'ESD2']), ('coi', ['COI']), # ('experimental', ['PI2', 'TestDB1', 'TestPI', 'TestLagAWFreeze', 'FixedGen']), ]) diff --git a/andes/models/distributed/__init__.py b/andes/models/distributed/__init__.py index b65ebc3fd..665ed50b9 100644 --- a/andes/models/distributed/__init__.py +++ b/andes/models/distributed/__init__.py @@ -1,4 +1,4 @@ from andes.models.distributed.dgprct import DGPRCT1, DGPRCTExt # NOQA -from andes.models.distributed.esd1 import ESD1 # NOQA +from andes.models.distributed.esd1 import ESD1, ESD2 # NOQA from andes.models.distributed.ev import EV1, EV2 # NOQA -from andes.models.distributed.pvd1 import PVD1 # NOQA +from andes.models.distributed.pvd1 import PVD1, PVD2 # NOQA diff --git a/andes/models/distributed/esd1.py b/andes/models/distributed/esd1.py index da51a87b2..46a6bf9c0 100644 --- a/andes/models/distributed/esd1.py +++ b/andes/models/distributed/esd1.py @@ -105,3 +105,17 @@ class ESD1(ESD1Data, ESD1Model): def __init__(self, system, config): ESD1Data.__init__(self) ESD1Model.__init__(self, system, config) + + +class ESD2(ESD1Data, ESD1Model): + """ + Distributed energy storage model. + + Revised from `ESD1`, where `fdbd` is not non-positive to allow + bi-directional frequency deviation response. + """ + + def __init__(self, system, config): + ESD1Data.__init__(self) + ESD1Model.__init__(self, system, config) + self.fdbd.non_positive = False diff --git a/andes/models/distributed/pvd1.py b/andes/models/distributed/pvd1.py index 649caa4ef..352a50b51 100644 --- a/andes/models/distributed/pvd1.py +++ b/andes/models/distributed/pvd1.py @@ -554,3 +554,33 @@ class PVD1(PVD1Data, PVD1Model): def __init__(self, system, config): PVD1Data.__init__(self) PVD1Model.__init__(self, system, config) + + +class PVD2Data(PVD1Data): + """ + Data for distributed PV with additional frequency droop. + """ + + def __init__(self): + PVD1Data.__init__(self) + + self.fdbd.non_positive = False + + +class PVD2(PVD2Data, PVD1Model): + """ + WECC Distributed PV model with additional frequency droop. + + This model is revised from `PVD1`, where `fdbd` is not non-positive to allow + bi-directional frequency deviation response. + + Reference: + [1] ESIG, WECC Distributed and Small PV Plants Generic Model (PVD2), [Online], + Available: + + https://www.esig.energy/wiki-main-page/wecc-distributed-and-small-pv-plants-generic-model-pvd2/ + """ + + def __init__(self, system, config): + PVD2Data.__init__(self) + PVD1Model.__init__(self, system, config) From 05096873cecd31ce7ff00234db514fdaa4775c63 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Thu, 5 Jun 2025 17:49:11 -0400 Subject: [PATCH 03/11] Minor fix in PVD2 and ESD2 --- andes/models/distributed/esd1.py | 6 +++--- andes/models/distributed/pvd1.py | 28 +++++++++------------------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/andes/models/distributed/esd1.py b/andes/models/distributed/esd1.py index 46a6bf9c0..345bfb99a 100644 --- a/andes/models/distributed/esd1.py +++ b/andes/models/distributed/esd1.py @@ -111,11 +111,11 @@ class ESD2(ESD1Data, ESD1Model): """ Distributed energy storage model. - Revised from `ESD1`, where `fdbd` is not non-positive to allow - bi-directional frequency deviation response. + This model is revised from `ESD1`, where `DB.upper` is set to `-fdbd` + to allow bi-directional frequency regulation response. """ def __init__(self, system, config): ESD1Data.__init__(self) ESD1Model.__init__(self, system, config) - self.fdbd.non_positive = False + self.DB.upper = "-fdbd" diff --git a/andes/models/distributed/pvd1.py b/andes/models/distributed/pvd1.py index 352a50b51..3d67e57a7 100644 --- a/andes/models/distributed/pvd1.py +++ b/andes/models/distributed/pvd1.py @@ -556,31 +556,21 @@ def __init__(self, system, config): PVD1Model.__init__(self, system, config) -class PVD2Data(PVD1Data): - """ - Data for distributed PV with additional frequency droop. - """ - - def __init__(self): - PVD1Data.__init__(self) - - self.fdbd.non_positive = False - - -class PVD2(PVD2Data, PVD1Model): +class PVD2(PVD1Data, PVD1Model): """ WECC Distributed PV model with additional frequency droop. - This model is revised from `PVD1`, where `fdbd` is not non-positive to allow - bi-directional frequency deviation response. + This model is revised from `PVD1`, where `DB.upper` is set to `-fdbd` + to allow bi-directional frequency regulation response. Reference: - [1] ESIG, WECC Distributed and Small PV Plants Generic Model (PVD2), [Online], - Available: - - https://www.esig.energy/wiki-main-page/wecc-distributed-and-small-pv-plants-generic-model-pvd2/ + [1] X. Fang, H. Yuan and J. Tan, "Secondary Frequency Regulation from Variable + Generation Through Uncertainty Decomposition: An Economic and Reliability + Perspective," in IEEE Transactions on Sustainable Energy, vol. 12, no. 4, + pp. 2019-2030, Oct. 2021, doi: 10.1109/TSTE.2021.3076758. """ def __init__(self, system, config): - PVD2Data.__init__(self) + PVD1Data.__init__(self) PVD1Model.__init__(self, system, config) + self.DB.upper = "-fdbd" From cac7cd3e0f661692cff9e049323dfa7603e8089f Mon Sep 17 00:00:00 2001 From: jinningwang Date: Thu, 5 Jun 2025 19:01:54 -0400 Subject: [PATCH 04/11] Doc --- andes/models/distributed/esd1.py | 6 +++--- andes/models/distributed/pvd1.py | 12 +++--------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/andes/models/distributed/esd1.py b/andes/models/distributed/esd1.py index 345bfb99a..ff884233a 100644 --- a/andes/models/distributed/esd1.py +++ b/andes/models/distributed/esd1.py @@ -109,10 +109,10 @@ def __init__(self, system, config): class ESD2(ESD1Data, ESD1Model): """ - Distributed energy storage model. + Distributed energy storage model with bi-directional frequency regulation + deadband. - This model is revised from `ESD1`, where `DB.upper` is set to `-fdbd` - to allow bi-directional frequency regulation response. + This model is revised from `ESD1`, where `DB.upper` is set to `-fdbd`. """ def __init__(self, system, config): diff --git a/andes/models/distributed/pvd1.py b/andes/models/distributed/pvd1.py index 3cf977ffc..72996f01b 100644 --- a/andes/models/distributed/pvd1.py +++ b/andes/models/distributed/pvd1.py @@ -558,16 +558,10 @@ def __init__(self, system, config): class PVD2(PVD1Data, PVD1Model): """ - WECC Distributed PV model with additional frequency droop. + WECC Distributed PV model with bi-directional frequency regulation + deadband. - This model is revised from `PVD1`, where `DB.upper` is set to `-fdbd` - to allow bi-directional frequency regulation response. - - Reference: - [1] X. Fang, H. Yuan and J. Tan, "Secondary Frequency Regulation from Variable - Generation Through Uncertainty Decomposition: An Economic and Reliability - Perspective," in IEEE Transactions on Sustainable Energy, vol. 12, no. 4, - pp. 2019-2030, Oct. 2021, doi: 10.1109/TSTE.2021.3076758. + This model is revised from `PVD1`, where `DB.upper` is set to `-fdbd`. """ def __init__(self, system, config): From 1554ca439a9dae0c7fb4f12cf51fa5cf9fcddebe Mon Sep 17 00:00:00 2001 From: jinningwang Date: Thu, 5 Jun 2025 19:13:32 -0400 Subject: [PATCH 05/11] Format --- andes/io/psse.py | 98 ++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/andes/io/psse.py b/andes/io/psse.py index 530a15586..5ba2a1363 100644 --- a/andes/io/psse.py +++ b/andes/io/psse.py @@ -67,12 +67,12 @@ def get_block_lines(b, mdata): def _parse_csv_with_quotes(line): """ Parse a line of PSS/E data that may contain single-quoted strings with commas or slashes. - + Parameters ---------- line : str A line from a PSS/E file that needs parsing - + Returns ------- list @@ -82,11 +82,11 @@ def _parse_csv_with_quotes(line): result = [] current = "" in_quotes = False - + # Handle empty input if not line: return [""] - + # Process each character for char in line: if char == "'" and not in_quotes: @@ -104,10 +104,10 @@ def _parse_csv_with_quotes(line): else: # Add character to current field current += char - + # Add the last field result.append(current) - + # Process each field to remove quotes and strip whitespace for i in range(len(result)): field = result[i] @@ -115,21 +115,21 @@ def _parse_csv_with_quotes(line): if field and len(field) >= 2 and field[0] == "'" and field[-1] == "'": field = field[1:-1] result[i] = field.strip() - + return result def _split_line_with_quoted_parts(line, separator='/'): """ Split a line by a separator character, but preserve the separator inside quoted strings. - + Parameters ---------- line : str Line to split separator : str Character to split by, defaults to '/' - + Returns ------- list @@ -138,7 +138,7 @@ def _split_line_with_quoted_parts(line, separator='/'): result = [] current = "" in_quotes = False - + for char in line: if char == "'" and not in_quotes: in_quotes = True @@ -151,10 +151,10 @@ def _split_line_with_quoted_parts(line, separator='/'): current = "" else: current += char - + if current: result.append(current) - + return result @@ -612,11 +612,11 @@ def _parse_transf_v33(raw, system, max_bus): # CZ=3: Load loss & |Z| # Convert power loss and impedance magnitude to R and X on winding base Sn = data[1][2] # Use winding base MVA - + # Convert load loss (W) to R (pu on winding base) # R = W / (SBASE_winding * 1e6) r_pu_wb = data[1][0] / (Sn * 1e6) - + # Calculate X from |Z| and R: X = sqrt(|Z|^2 - R^2) # Handle numeric issues - ensure we don't get imaginary numbers if data[1][1]**2 > r_pu_wb**2: @@ -626,11 +626,11 @@ def _parse_transf_v33(raw, system, max_bus): logger.warning(f"Branch {data[0][0]}-{data[0][1]}:") logger.warning(" CZ=3 conversion issue: |Z|^2 < R^2. Setting X to small value.") x_pu_wb = 1e-6 - + # Replace the data[1][0] and data[1][1] with calculated R and X data[1][0] = r_pu_wb data[1][1] = x_pu_wb - + # Now it's in CZ=2 format and will be processed accordingly else: logger.warning('Unknown impedance code %s', data[0][5]) @@ -640,18 +640,18 @@ def _parse_transf_v33(raw, system, max_bus): # CM=2: No load loss & exc. loss mag1 = data[0][7] # No-load loss in watts mag2 = data[0][8] # Excitation current in pu - + # Vbase for winding 1 (kV) Vn1 = data[2][1] if data[2][1] != 0.0 else bus_Vn1 - + # Step 1: Convert power loss to conductance (G) in Siemens # G [S] = MAG1 / VNOM1_kV^2 / 1e6 - g_s = mag1 / (Vn1 **2) / 1e6 - + g_s = mag1 / (Vn1 ** 2) / 1e6 + # Step 2: Calculate B in Siemens from excitation current # First convert excitation current to |Y| in Siemens y_mag_s = abs(mag2) * system.config.mva / (Vn1**2) - + # B [S] = sqrt(|Y|^2 - G^2) # Handle numeric issues if y_mag_s**2 > g_s**2: @@ -661,11 +661,11 @@ def _parse_transf_v33(raw, system, max_bus): # If |Y| is too small compared to G, assume B is very small logger.warning("CM=2 conversion issue: |Y|^2 < G^2. Setting B to small value.") b_s = 1e-6 - + # Convert back to per unit on system base g_pu = g_s * (Vn1**2) / system.config.mva b_pu = b_s * (Vn1**2) / system.config.mva - + # Replace MAG1 and MAG2 with calculated G and B in pu data[0][7] = g_pu data[0][8] = b_pu @@ -704,7 +704,7 @@ def _parse_transf_v33(raw, system, max_bus): data = _process_3wt_cz3(data, system) # After processing, treat this as CZ=2 data[0][5] = 2 - + if data[0][6] == 2: # CM=2 data = _process_3wt_cm2(data, system) # After processing, treat this as CM=1 @@ -715,7 +715,7 @@ def _parse_transf_v33(raw, system, max_bus): if new_bus in system.Bus.idx.v: new_bus = max_bus + xf_3_count logger.debug('Added bus <%s> for 3-winding transformer <%s-%s-%s>', - new_bus, data[0][0], data[0][1], data[0][2]) + new_bus, data[0][0], data[0][1], data[0][2]) # Assign `area`, `owner`, and `zone` using the high-voltage side bus values high_voltage_bus = data[0][0] @@ -742,7 +742,7 @@ def _parse_transf_v33(raw, system, max_bus): x_23 = data[1][4] r_31 = data[1][6] x_31 = data[1][7] - + # Convert to system base if CZ=2 if data[0][5] == 2: # Convert from winding base to system base @@ -753,7 +753,7 @@ def _parse_transf_v33(raw, system, max_bus): x_23 = x_23 * sbase / data[1][5] r_31 = r_31 * sbase / data[1][8] # SBASE3-1 x_31 = x_31 * sbase / data[1][8] - + # Calculate star-point resistances and reactances # These values are on system base after the conversion above r = [] @@ -768,7 +768,7 @@ def _parse_transf_v33(raw, system, max_bus): for i in range(0, 3): # Always use system base after conversion Sn = system.config.mva - + # Set magnetizing conductance and susceptance for winding 1 only (first branch) if i == 0: g1_value = data[0][7] # Magnetizing conductance (G) @@ -776,7 +776,7 @@ def _parse_transf_v33(raw, system, max_bus): else: g1_value = 0.0 # No magnetization for other windings b1_value = 0.0 # No magnetization for other windings - + param = {'trans': True, 'bus1': data[0][i], 'bus2': new_bus, @@ -915,16 +915,16 @@ def sort_psse_models(dyr_yaml, system): def _process_3wt_cz3(data, system): """ Process 3-winding transformer data with CZ=3 (Load loss & |Z|). - + Converts power loss (W) and impedance magnitude to R and X on winding base (CZ=2). - + Parameters ---------- data : list Multi-line transformer data system : System The ANDES system object - + Returns ------- list @@ -933,23 +933,23 @@ def _process_3wt_cz3(data, system): # For each winding pair, convert load loss and |Z| to R and X # Winding pairs are 1-2, 2-3, and 3-1 sbase = [data[1][2], data[1][5], data[1][8]] # SBASE1-2, SBASE2-3, SBASE3-1 - + # Data indices for each winding pair indices = [ (0, 1), # R1-2, X1-2 indices (3, 4), # R2-3, X2-3 indices (6, 7), # R3-1, X3-1 indices ] - + for i, (loss_idx, z_idx) in enumerate(indices): # Load loss in watts loss = data[1][loss_idx] # |Z| in pu on winding base z_mag = data[1][z_idx] - + # Convert load loss to R in pu on winding base r_pu = loss / (sbase[i] * 1e6) - + # Calculate X from |Z| and R: X = sqrt(|Z|^2 - R^2) # Handle numeric issues - ensure we don't get imaginary numbers if z_mag**2 > r_pu**2: @@ -958,27 +958,27 @@ def _process_3wt_cz3(data, system): # If |Z| is too small compared to R, assume X is very small logger.warning(f"CZ=3 conversion issue: |Z|^2 < R^2 for winding pair {i+1}. Setting X to small value.") x_pu = 1e-6 - + # Replace the original values with calculated R and X data[1][loss_idx] = r_pu data[1][z_idx] = x_pu - + return data def _process_3wt_cm2(data, system): """ Process 3-winding transformer data with CM=2 (No load loss & exc. loss). - + Converts no-load loss and excitation current to G and B on system base (CM=1). - + Parameters ---------- data : list Multi-line transformer data system : System The ANDES system object - + Returns ------- list @@ -987,18 +987,18 @@ def _process_3wt_cm2(data, system): # Get no-load loss in watts and excitation current in pu mag1 = data[0][7] # No-load loss in watts mag2 = data[0][8] # Excitation current in pu - + # Get the rated voltage of winding 1 (kV) Vn1 = data[2][1] if data[2][1] != 0.0 else system.Bus.get(src='Vn', idx=data[0][0], attr='v') - + # Convert power loss to conductance (G) in Siemens # G [S] = MAG1 / VNOM1_V^2 / 1e6 - g_s = mag1 / (Vn1 **2) / 1e6 - + g_s = mag1 / (Vn1 ** 2) / 1e6 + # Convert |Y| pu to actual in Siemens # |Y| [S] = |MAG2| * SBASE_MVA / NOMV1_kV^2 y_mag_s = abs(mag2) * system.config.mva / (Vn1**2) - + # Calculate B in Siemens # B [S] = sqrt(|Y|^2 - G^2) # Handle numeric issues @@ -1010,13 +1010,13 @@ def _process_3wt_cm2(data, system): logger.warning(f"Branch {data[0][0]}-{data[0][1]}-{data[0][2]}:") logger.warning(" CM=2 conversion issue: |Y|^2 < G^2. Setting B to small value.") b_s = 1e-6 - + # Convert G and B to per unit on system base g_pu = g_s * (Vn1**2) / system.config.mva b_pu = b_s * (Vn1**2) / system.config.mva - + # Replace MAG1 and MAG2 with calculated G and B in pu data[0][7] = g_pu data[0][8] = b_pu - + return data From 479a6dc81c6f41ced91448f80c44ff601c2bf32d Mon Sep 17 00:00:00 2001 From: jinningwang Date: Fri, 6 Jun 2025 23:38:06 -0400 Subject: [PATCH 06/11] Fix PVD2 and ESD2 --- andes/models/distributed/esd1.py | 13 +++++++++++-- andes/models/distributed/pvd1.py | 11 ++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/andes/models/distributed/esd1.py b/andes/models/distributed/esd1.py index ff884233a..4c82d414b 100644 --- a/andes/models/distributed/esd1.py +++ b/andes/models/distributed/esd1.py @@ -1,6 +1,6 @@ """Distributed energy storage system model""" -from andes.core.block import Integrator +from andes.core.block import Integrator, DeadBand1 from andes.core.discrete import HardLimiter, LessThan from andes.core.param import NumParam from andes.core.service import ConstService @@ -118,4 +118,13 @@ class ESD2(ESD1Data, ESD1Model): def __init__(self, system, config): ESD1Data.__init__(self) ESD1Model.__init__(self, system, config) - self.DB.upper = "-fdbd" + + self.fdbdn = ConstService(v_str='-fdbd', + info='-fdbd') + + self.DB2 = DeadBand1(u=self.Fdev, center=0.0, lower=self.fdbd, upper=self.fdbdn, + gain=self.ddn, + info='frequency deviation deadband with gain', + ) # outputs `Pdrp` + self.Psum.v_str = 'u * (Pext + Pref + DB2_y)' + self.Psum.e_str = 'u * (Pext + Pref + DB2_y) - Psum' diff --git a/andes/models/distributed/pvd1.py b/andes/models/distributed/pvd1.py index 72996f01b..2ed914b5e 100644 --- a/andes/models/distributed/pvd1.py +++ b/andes/models/distributed/pvd1.py @@ -567,4 +567,13 @@ class PVD2(PVD1Data, PVD1Model): def __init__(self, system, config): PVD1Data.__init__(self) PVD1Model.__init__(self, system, config) - self.DB.upper = "-fdbd" + + self.fdbdn = ConstService(v_str='-fdbd', + info='-fdbd') + + self.DB2 = DeadBand1(u=self.Fdev, center=0.0, lower=self.fdbd, upper=self.fdbdn, + gain=self.ddn, + info='frequency deviation deadband with gain', + ) # outputs `Pdrp` + self.Psum.v_str = 'u * (Pext + Pref + DB2_y)' + self.Psum.e_str = 'u * (Pext + Pref + DB2_y) - Psum' From 1731035f22729fb06e706f491c060f5a3e17dda7 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Sun, 8 Jun 2025 02:41:00 -0400 Subject: [PATCH 07/11] Improve PVD2, fix ESD1, remove ESD2 --- andes/models/__init__.py | 3 +-- andes/models/distributed/__init__.py | 2 +- andes/models/distributed/esd1.py | 33 +++++++---------------- andes/models/distributed/pvd1.py | 39 ++++++++++++++++++++-------- 4 files changed, 39 insertions(+), 38 deletions(-) diff --git a/andes/models/__init__.py b/andes/models/__init__.py index 614e26893..a28e105f2 100644 --- a/andes/models/__init__.py +++ b/andes/models/__init__.py @@ -38,8 +38,7 @@ ('renewable', ['REPCA1']), ('renewable', ['WTDTA1', 'WTDS', 'WTARA1', 'WTPTA1', 'WTTQA1', 'WTARV1', 'REGCV1', 'REGCV2', 'REGF1', 'REGF2', 'REGF3']), - ('distributed', ['PVD1', 'ESD1', 'EV1', 'EV2', 'DGPRCT1', 'DGPRCTExt', - 'PVD2', 'ESD2']), + ('distributed', ['PVD1', 'PVD2', 'ESD1', 'EV1', 'EV2', 'DGPRCT1', 'DGPRCTExt']), ('coi', ['COI']), # ('experimental', ['PI2', 'TestDB1', 'TestPI', 'TestLagAWFreeze', 'FixedGen']), ]) diff --git a/andes/models/distributed/__init__.py b/andes/models/distributed/__init__.py index 665ed50b9..e9744a4c4 100644 --- a/andes/models/distributed/__init__.py +++ b/andes/models/distributed/__init__.py @@ -1,4 +1,4 @@ from andes.models.distributed.dgprct import DGPRCT1, DGPRCTExt # NOQA -from andes.models.distributed.esd1 import ESD1, ESD2 # NOQA +from andes.models.distributed.esd1 import ESD1 # NOQA from andes.models.distributed.ev import EV1, EV2 # NOQA from andes.models.distributed.pvd1 import PVD1, PVD2 # NOQA diff --git a/andes/models/distributed/esd1.py b/andes/models/distributed/esd1.py index 4c82d414b..208a7dd5d 100644 --- a/andes/models/distributed/esd1.py +++ b/andes/models/distributed/esd1.py @@ -1,6 +1,6 @@ """Distributed energy storage system model""" -from andes.core.block import Integrator, DeadBand1 +from andes.core.block import Integrator from andes.core.discrete import HardLimiter, LessThan from andes.core.param import NumParam from andes.core.service import ConstService @@ -86,6 +86,8 @@ def __init__(self, system, config): self.Ipcmd.lim.lower = self.Ipmin self.Ipcmd.y.deps = ['Ipmin'] + self.fdbdu.default = 0.017 + class ESD1(ESD1Data, ESD1Model): """ @@ -96,6 +98,12 @@ class ESD1(ESD1Data, ESD1Model): The state of charge is in state variable ``SOC``, which is an alias of ``pIG_y``. + Notes + ----- + .. versionchanged:: 1.9.4 + `DB.upper` is set to be `fdbdu` instead of 0 in order to allow + bi-directional frequency regulation deadband. + Reference: [1] Powerworld, Renewable Energy Electrical Control Model REEC_C Available: @@ -105,26 +113,3 @@ class ESD1(ESD1Data, ESD1Model): def __init__(self, system, config): ESD1Data.__init__(self) ESD1Model.__init__(self, system, config) - - -class ESD2(ESD1Data, ESD1Model): - """ - Distributed energy storage model with bi-directional frequency regulation - deadband. - - This model is revised from `ESD1`, where `DB.upper` is set to `-fdbd`. - """ - - def __init__(self, system, config): - ESD1Data.__init__(self) - ESD1Model.__init__(self, system, config) - - self.fdbdn = ConstService(v_str='-fdbd', - info='-fdbd') - - self.DB2 = DeadBand1(u=self.Fdev, center=0.0, lower=self.fdbd, upper=self.fdbdn, - gain=self.ddn, - info='frequency deviation deadband with gain', - ) # outputs `Pdrp` - self.Psum.v_str = 'u * (Pext + Pref + DB2_y)' - self.Psum.e_str = 'u * (Pext + Pref + DB2_y) - Psum' diff --git a/andes/models/distributed/pvd1.py b/andes/models/distributed/pvd1.py index 2ed914b5e..e5f798991 100644 --- a/andes/models/distributed/pvd1.py +++ b/andes/models/distributed/pvd1.py @@ -96,6 +96,10 @@ def __init__(self): unit='Hz', non_positive=True, ) + self.fdbdu = NumParam(default=999, tex_name='f_{dbd,u}', + info='frequency deviation upper deadband, placeholder', + unit='Hz', + non_negative=True) # added on 11/14/2020: convert to system base pu self.ddn = NumParam(default=0.0, tex_name='D_{dn}', @@ -324,7 +328,7 @@ def __init__(self, system, config): unit='Hz', tex_name='f_{dev}', ) - self.DB = DeadBand1(u=self.Fdev, center=0.0, lower=self.fdbd, upper=999.0, gain=self.ddn, + self.DB = DeadBand1(u=self.Fdev, center=0.0, lower=self.fdbd, upper=self.fdbdu, gain=self.ddn, info='frequency deviation deadband with gain', ) # outputs `Pdrp` self.DB.db.no_warn = True @@ -549,6 +553,17 @@ class PVD1(PVD1Data, PVD1Model): Available: https://www.esig.energy/wiki-main-page/wecc-distributed-and-small-pv-plants-generic-model-pvd1/ + + Notes + ----- + This model introduces the parameter `fdbdu`, which is set to 999 by default. + It enables support for bi-directional frequency regulation deadband in + derived models. + + **Important:** Do not modify `fdbdu` when using the `PVD1` model. + + .. versionchanged:: 1.9.4 + Added the `fdbdu` parameter to support bi-directional deadband control. """ def __init__(self, system, config): @@ -561,19 +576,21 @@ class PVD2(PVD1Data, PVD1Model): WECC Distributed PV model with bi-directional frequency regulation deadband. - This model is revised from `PVD1`, where `DB.upper` is set to `-fdbd`. + This model is revised from `PVD1`, where `DB.upper` is set to `fdbdu`. + + Reference: + [1] X. Fang, H. Yuan and J. Tan, "Secondary Frequency Regulation from + Variable Generation Through Uncertainty Decomposition: An Economic and + Reliability Perspective," in IEEE Transactions on Sustainable Energy, + vol. 12, no. 4, pp. 2019-2030, Oct. 2021, doi: 10.1109/TSTE.2021.3076758. + + Notes + ----- + .. versionadded:: 1.9.4 """ def __init__(self, system, config): PVD1Data.__init__(self) PVD1Model.__init__(self, system, config) - self.fdbdn = ConstService(v_str='-fdbd', - info='-fdbd') - - self.DB2 = DeadBand1(u=self.Fdev, center=0.0, lower=self.fdbd, upper=self.fdbdn, - gain=self.ddn, - info='frequency deviation deadband with gain', - ) # outputs `Pdrp` - self.Psum.v_str = 'u * (Pext + Pref + DB2_y)' - self.Psum.e_str = 'u * (Pext + Pref + DB2_y) - Psum' + self.fdbdu.default = 0.017 From fbee9eafc5aae05158ecb0c4a302584d667f373d Mon Sep 17 00:00:00 2001 From: jinningwang Date: Sun, 8 Jun 2025 18:13:39 -0400 Subject: [PATCH 08/11] Doc --- andes/models/distributed/pvd1.py | 34 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/andes/models/distributed/pvd1.py b/andes/models/distributed/pvd1.py index e5f798991..e9c513862 100644 --- a/andes/models/distributed/pvd1.py +++ b/andes/models/distributed/pvd1.py @@ -329,7 +329,7 @@ def __init__(self, system, config): ) self.DB = DeadBand1(u=self.Fdev, center=0.0, lower=self.fdbd, upper=self.fdbdu, gain=self.ddn, - info='frequency deviation deadband with gain', + info='frequency deviation deadband with gain (DB_y is Pdrp)', ) # outputs `Pdrp` self.DB.db.no_warn = True @@ -379,19 +379,22 @@ def __init__(self, system, config): self.Pext = Algeb(tex_name='P_{ext}', info='External power signal (for AGC)', v_str='u * Pext0', - e_str='u * Pext0 - Pext' + e_str='u * Pext0 - Pext', + unit='pu', ) self.Pref = Algeb(tex_name='P_{ref}', info='Reference power signal (for scheduling setpoint)', v_str='u * pref0', - e_str='u * pref0 - Pref' + e_str='u * pref0 - Pref', + unit='pu', ) self.Psum = Algeb(tex_name='P_{tot}', info='Sum of P signals', v_str='u * (Pext + Pref + DB_y)', e_str='u * (Pext + Pref + DB_y) - Psum', + unit='pu', ) # `DB_y` is `Pdrp` (f droop) self.PHL = Limiter(u=self.Psum, lower=0.0, upper=self.pmx, @@ -436,12 +439,14 @@ def __init__(self, system, config): v_str=Qdrp, e_str=f'{Qdrp} - Qdrp', discrete=(self.VQ1, self.VQ2), + unit='pu', ) self.Qref = Algeb(tex_name=r'Q_{ref}', info='Reference power signal (for scheduling setpoint)', v_str='u * qref0', - e_str='u * qref0 - Qref' + e_str='u * qref0 - Qref', + unit='pu', ) self.Qsum = Algeb(tex_name=r'Q_{tot}', @@ -449,6 +454,7 @@ def __init__(self, system, config): v_str=f'u * (qref0 + {Qdrp})', e_str='u * (Qref + Qdrp) - Qsum', discrete=(self.VQ1, self.VQ2), + unit='pu', ) self.Ipul = Algeb(info='Ipcmd before Ip hard limit', @@ -548,11 +554,10 @@ class PVD1(PVD1Data, PVD1Model): Maximum power limit `pmx` can be disabled by editing the configuration file by setting `plim=0`. It cannot be modified in runtime. - Reference: - [1] ESIG, WECC Distributed and Small PV Plants Generic Model (PVD1), [Online], - Available: - - https://www.esig.energy/wiki-main-page/wecc-distributed-and-small-pv-plants-generic-model-pvd1/ + References + ----------- + 1. ESIG, WECC Distributed and Small PV Plants Generic Model (PVD1), + https://www.esig.energy/wiki-main-page/wecc-distributed-and-small-pv-plants-generic-model-pvd1/ Notes ----- @@ -578,11 +583,12 @@ class PVD2(PVD1Data, PVD1Model): This model is revised from `PVD1`, where `DB.upper` is set to `fdbdu`. - Reference: - [1] X. Fang, H. Yuan and J. Tan, "Secondary Frequency Regulation from - Variable Generation Through Uncertainty Decomposition: An Economic and - Reliability Perspective," in IEEE Transactions on Sustainable Energy, - vol. 12, no. 4, pp. 2019-2030, Oct. 2021, doi: 10.1109/TSTE.2021.3076758. + References + ----------- + 1. X. Fang, H. Yuan and J. Tan, "Secondary Frequency Regulation from + Variable Generation Through Uncertainty Decomposition: An Economic and + Reliability Perspective," in IEEE Transactions on Sustainable Energy, + vol. 12, no. 4, pp. 2019-2030, Oct. 2021, doi: 10.1109/TSTE.2021.3076758. Notes ----- From 9c508559bee3c22a60d46a6d353a1060d9665297 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Sun, 8 Jun 2025 18:16:07 -0400 Subject: [PATCH 09/11] Doc --- andes/models/distributed/esd1.py | 17 ++++++++++------- andes/models/distributed/pvd1.py | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/andes/models/distributed/esd1.py b/andes/models/distributed/esd1.py index 208a7dd5d..90ac044c8 100644 --- a/andes/models/distributed/esd1.py +++ b/andes/models/distributed/esd1.py @@ -98,17 +98,20 @@ class ESD1(ESD1Data, ESD1Model): The state of charge is in state variable ``SOC``, which is an alias of ``pIG_y``. + Unlink PVD1, frequency deviation deadband upper limit is + set to `fdbdu` instead of 0, allowing bi-directional + frequency deviation deadband. + + References + ----------- + 1. ESIG, WECC Distributed and Small PV Plants Generic Model (PVD1), + https://www.esig.energy/wiki-main-page/wecc-distributed-and-small-pv-plants-generic-model-pvd1/ + Notes ----- .. versionchanged:: 1.9.4 `DB.upper` is set to be `fdbdu` instead of 0 in order to allow - bi-directional frequency regulation deadband. - - Reference: - [1] Powerworld, Renewable Energy Electrical Control Model REEC_C - Available: - - https://www.powerworld.com/WebHelp/Content/TransientModels_HTML/Exciter%20REEC_C.htm + bi-directional frequency deviation deadband. """ def __init__(self, system, config): ESD1Data.__init__(self) diff --git a/andes/models/distributed/pvd1.py b/andes/models/distributed/pvd1.py index e9c513862..149857f89 100644 --- a/andes/models/distributed/pvd1.py +++ b/andes/models/distributed/pvd1.py @@ -562,7 +562,7 @@ class PVD1(PVD1Data, PVD1Model): Notes ----- This model introduces the parameter `fdbdu`, which is set to 999 by default. - It enables support for bi-directional frequency regulation deadband in + It enables support for bi-directional frequency deviation deadband in derived models. **Important:** Do not modify `fdbdu` when using the `PVD1` model. @@ -578,7 +578,7 @@ def __init__(self, system, config): class PVD2(PVD1Data, PVD1Model): """ - WECC Distributed PV model with bi-directional frequency regulation + WECC Distributed PV model with bi-directional frequency deviation deadband. This model is revised from `PVD1`, where `DB.upper` is set to `fdbdu`. From 0335c1479f070cc9ecab9290014966368fedb7e0 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Sun, 8 Jun 2025 18:36:35 -0400 Subject: [PATCH 10/11] Mention DAETimeSeries.get_data() in ex2 --- examples/ex2.ipynb | 5516 ++++++++++++++++++++++---------------------- 1 file changed, 2734 insertions(+), 2782 deletions(-) diff --git a/examples/ex2.ipynb b/examples/ex2.ipynb index 2c651f375..b7acdc75c 100644 --- a/examples/ex2.ipynb +++ b/examples/ex2.ipynb @@ -1,2786 +1,2738 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Working with Data\n", - "\n", - "This example shows how to work with the data of a loaded test system, including parameters and variables." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:43.135158Z", - "start_time": "2021-03-19T01:57:42.407900Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:51.026526Z", - "iopub.status.busy": "2021-09-26T22:41:51.026141Z", - "iopub.status.idle": "2021-09-26T22:41:51.758845Z", - "shell.execute_reply": "2021-09-26T22:41:51.758473Z" - } - }, - "outputs": [], - "source": [ - "import andes\n", - "from andes.utils.paths import get_case\n", - "\n", - "andes.config_logger()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To show all the rows and columns, change the pandas configuration with" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:43.381726Z", - "start_time": "2021-03-19T01:57:43.140307Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:51.761987Z", - "iopub.status.busy": "2021-09-26T22:41:51.761718Z", - "iopub.status.idle": "2021-09-26T22:41:51.965566Z", - "shell.execute_reply": "2021-09-26T22:41:51.965217Z" - } - }, - "outputs": [], - "source": [ - "import pandas as pd\n", - "\n", - "pd.options.display.max_columns = None\n", - "pd.options.display.max_rows = None" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's load the Kundur's system." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Load System from an ANDES XLSX File\n", - "\n", - "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/curent/andes/blob/master/README.md#format-converter" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As previously shown, test cases can be loaded with ``andes.run()``:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:43.965479Z", - "start_time": "2021-03-19T01:57:43.383941Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:51.968113Z", - "iopub.status.busy": "2021-09-26T22:41:51.967833Z", - "iopub.status.idle": "2021-09-26T22:41:52.429156Z", - "shell.execute_reply": "2021-09-26T22:41:52.429542Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Working directory: \"/home/hacui/repos/andes/examples\"\n", - "> Loaded generated Python code in \"/home/hacui/.andes/pycode\".\n", - "Parsing input file \"/home/hacui/repos/andes/andes/cases/kundur/kundur_full.xlsx\"...\n", - "Input file parsed in 0.1394 seconds.\n", - "System internal structure set up in 0.0319 seconds.\n", - "-> System connectivity check results:\n", - " No islanded bus detected.\n", - " System is interconnected.\n", - " Each island has a slack bus correctly defined and enabled.\n", - "\n", - "-> Power flow calculation\n", - " Numba: Off\n", - " Sparse solver: KLU\n", - " Solution method: NR method\n", - "Power flow initialized in 0.0036 seconds.\n", - "0: |F(x)| = 14.9282832\n", - "1: |F(x)| = 3.608627841\n", - "2: |F(x)| = 0.1701107882\n", - "3: |F(x)| = 0.002038626956\n", - "4: |F(x)| = 3.745104027e-07\n", - "Converged in 5 iterations in 0.0074 seconds.\n", - "Report saved to \"kundur_full_out.txt\" in 0.0010 seconds.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "-> Single process finished in 0.3582 seconds.\n" - ] - } - ], - "source": [ - "ss = andes.run(get_case('kundur/kundur_full.xlsx'),\n", - " default_config=True) # one can remove `default_config=True` to use custom config file" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Alternatively, one can load a test case _without setting up_ using `andes.load(..., setup=False)`. Note that `setup=False` option.\n", - "It is useful to apply parameter changes to an existing test case." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.168248Z", - "start_time": "2021-03-19T01:57:43.970545Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:52.433901Z", - "iopub.status.busy": "2021-09-26T22:41:52.431622Z", - "iopub.status.idle": "2021-09-26T22:41:52.626472Z", - "shell.execute_reply": "2021-09-26T22:41:52.626800Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Working directory: \"/home/hacui/repos/andes/examples\"\n", - "> Reloaded generated Python code of module \"pycode\".\n", - "Parsing input file \"/home/hacui/repos/andes/andes/cases/kundur/kundur_full.xlsx\"...\n", - "Input file parsed in 0.1263 seconds.\n" - ] - } - ], - "source": [ - "ss = andes.load(get_case('kundur/kundur_full.xlsx'),\n", - " default_config=True,\n", - " setup=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-18T00:25:32.261166Z", - "start_time": "2021-03-18T00:25:32.256088Z" - } - }, - "source": [ - "For example, we can toggle the connectivity status `u` of `Line_3` to `0` using" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.176858Z", - "start_time": "2021-03-19T01:57:44.173846Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:52.628710Z", - "iopub.status.busy": "2021-09-26T22:41:52.628330Z", - "iopub.status.idle": "2021-09-26T22:41:52.631669Z", - "shell.execute_reply": "2021-09-26T22:41:52.632025Z" - } - }, - "outputs": [], - "source": [ - "ss.Line.alter('u', 'Line_3', 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When done, remember to set up the system before running calculation routines:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.266523Z", - "start_time": "2021-03-19T01:57:44.181450Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:52.634009Z", - "iopub.status.busy": "2021-09-26T22:41:52.633570Z", - "iopub.status.idle": "2021-09-26T22:41:52.723455Z", - "shell.execute_reply": "2021-09-26T22:41:52.724002Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "System internal structure set up in 0.0366 seconds.\n", - "-> System connectivity check results:\n", - " No islanded bus detected.\n", - " System is interconnected.\n", - " Each island has a slack bus correctly defined and enabled.\n", - "\n", - "-> Power flow calculation\n", - " Numba: Off\n", - " Sparse solver: KLU\n", - " Solution method: NR method\n", - "Power flow initialized in 0.0049 seconds.\n", - "0: |F(x)| = 14.9282832\n", - "1: |F(x)| = 3.579044433\n", - "2: |F(x)| = 0.119268955\n", - "3: |F(x)| = 0.03278820195\n", - "4: |F(x)| = 2.880943096e-05\n", - "5: |F(x)| = 3.93747257e-11\n", - "Converged in 6 iterations in 0.0122 seconds.\n", - "Report saved to \"kundur_full_out.txt\" in 0.0016 seconds.\n" - ] - }, - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.setup()\n", - "\n", - "ss.PFlow.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After setting up the system, adding or removing devices are not yet allowed." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-18T00:28:42.135119Z", - "start_time": "2021-03-18T00:28:42.128972Z" - } - }, - "source": [ - "## Load System from PSS/E RAW and DYR Files\n", - "\n", - "ANDES supports loading systems from PSS/E RAW and DYR files.\n", - "\n", - "The PSS/E v32 raw format is best supported.\n", - "\n", - "Note that this feature is experimental. We try out best to support this format, but the compatibility is not guaranteed." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.274794Z", - "start_time": "2021-03-19T01:57:44.271077Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:52.727707Z", - "iopub.status.busy": "2021-09-26T22:41:52.726149Z", - "iopub.status.idle": "2021-09-26T22:41:52.731861Z", - "shell.execute_reply": "2021-09-26T22:41:52.732410Z" - } - }, - "outputs": [], - "source": [ - "raw_path = get_case('kundur/kundur.raw')\n", - "dyr_path = get_case('kundur/kundur_full.dyr')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The raw file is passed to the positional argument, whereas the dyr file is passed to `addfile`." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.606632Z", - "start_time": "2021-03-19T01:57:44.277112Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:52.735486Z", - "iopub.status.busy": "2021-09-26T22:41:52.734401Z", - "iopub.status.idle": "2021-09-26T22:41:53.107538Z", - "shell.execute_reply": "2021-09-26T22:41:53.106943Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Working directory: \"/home/hacui/repos/andes/examples\"\n", - "> Reloaded generated Python code of module \"pycode\".\n", - "Parsing input file \"/home/hacui/repos/andes/andes/cases/kundur/kundur.raw\"...\n", - " MODIFIED KUNDUR'S TWO-AREA TEST SYSTEM, DISTRIBUTED WITH ANDES\n", - " SEE THE BOOK \"POWER SYSTEM STABILITY AND CONTROL\" FOR ORIGINAL DATA\n", - "Input file parsed in 0.0047 seconds.\n", - "Parsing additional file \"/home/hacui/repos/andes/andes/cases/kundur/kundur_full.dyr\"...\n", - "Addfile parsed in 0.0912 seconds.\n", - "System internal structure set up in 0.0310 seconds.\n", - "-> System connectivity check results:\n", - " No islanded bus detected.\n", - " System is interconnected.\n", - " Each island has a slack bus correctly defined and enabled.\n", - "\n", - "-> Power flow calculation\n", - " Numba: Off\n", - " Sparse solver: KLU\n", - " Solution method: NR method\n", - "Power flow initialized in 0.0037 seconds.\n", - "0: |F(x)| = 3.175850023\n", - "1: |F(x)| = 3.176155228e-08\n", - "Converged in 2 iterations in 0.0044 seconds.\n", - "Report saved to \"kundur_out.txt\" in 0.0006 seconds.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "-> Single process finished in 0.2371 seconds.\n" - ] - } - ], - "source": [ - "ss = andes.run(raw_path, addfile=dyr_path, default_config=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Attributes for storing values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Parameters are stored as attributes of the model. For example, `ss.GENROU.M`, the machine starting time constant (`2H`), is stored in `ss.GENROU.M`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.618529Z", - "start_time": "2021-03-19T01:57:44.611845Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.110215Z", - "iopub.status.busy": "2021-09-26T22:41:53.109452Z", - "iopub.status.idle": "2021-09-26T22:41:53.115421Z", - "shell.execute_reply": "2021-09-26T22:41:53.116038Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "NumParam: GENROU.M, v=[117. 117. 111.15 111.15], vin=[13. 13. 12.35 12.35]" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.GENROU.M" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It is an instance of `NumParam`, which contains fields `v` for the values after converting to system-base per unit values." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.629108Z", - "start_time": "2021-03-19T01:57:44.623058Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.120712Z", - "iopub.status.busy": "2021-09-26T22:41:53.117859Z", - "iopub.status.idle": "2021-09-26T22:41:53.122771Z", - "shell.execute_reply": "2021-09-26T22:41:53.123377Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([117. , 117. , 111.15, 111.15])" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.GENROU.M.v" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And field `vin` is for the original input data." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.639730Z", - "start_time": "2021-03-19T01:57:44.633864Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.126169Z", - "iopub.status.busy": "2021-09-26T22:41:53.125381Z", - "iopub.status.idle": "2021-09-26T22:41:53.131834Z", - "shell.execute_reply": "2021-09-26T22:41:53.132558Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([13. , 13. , 12.35, 12.35])" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.GENROU.M.vin" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Tabulated view" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "ANDES provides tabulated **view** of model parameters by using DataFrame. Each model object has an attribute called `cache` for caching the parameter dataframes." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The original parameters from the input file are stored in `cache.df_in` of the model object. For `GENROU`, do" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.681593Z", - "start_time": "2021-03-19T01:57:44.644323Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.135811Z", - "iopub.status.busy": "2021-09-26T22:41:53.134902Z", - "iopub.status.idle": "2021-09-26T22:41:53.170616Z", - "shell.execute_reply": "2021-09-26T22:41:53.171215Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
idxunamebusgencoicoi2SnVnfnDMraxlxd1kpkwS10S12gammapgammaqxdxqxd2xq1xq2Td10Td20Tq10Tq20
uid
0GENROU_11.0GENROU_111NoneNone900.020.060.00.013.000.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
1GENROU_21.0GENROU_222NoneNone900.020.060.00.013.000.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
2GENROU_31.0GENROU_333NoneNone900.020.060.00.012.350.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
3GENROU_41.0GENROU_444NoneNone900.020.060.00.012.350.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
\n", - "
" - ], - "text/plain": [ - " idx u name bus gen coi coi2 Sn Vn fn D \\\n", - "uid \n", - "0 GENROU_1 1.0 GENROU_1 1 1 None None 900.0 20.0 60.0 0.0 \n", - "1 GENROU_2 1.0 GENROU_2 2 2 None None 900.0 20.0 60.0 0.0 \n", - "2 GENROU_3 1.0 GENROU_3 3 3 None None 900.0 20.0 60.0 0.0 \n", - "3 GENROU_4 1.0 GENROU_4 4 4 None None 900.0 20.0 60.0 0.0 \n", - "\n", - " M ra xl xd1 kp kw S10 S12 gammap gammaq xd xq \\\n", - "uid \n", - "0 13.00 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", - "1 13.00 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", - "2 12.35 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", - "3 12.35 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", - "\n", - " xd2 xq1 xq2 Td10 Td20 Tq10 Tq20 \n", - "uid \n", - "0 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", - "1 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", - "2 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", - "3 0.25 0.55 0.25 8.0 0.03 0.4 0.05 " - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.GENROU.cache.df_in" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Parameters will be **converted** to per-unit in the system base after loading. This process have been done if `andes.run` is used for loading the data file.\n", - "\n", - "To inspect the converted parameters, check the `cache.df` parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.721712Z", - "start_time": "2021-03-19T01:57:44.686492Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.173678Z", - "iopub.status.busy": "2021-09-26T22:41:53.173050Z", - "iopub.status.idle": "2021-09-26T22:41:53.207341Z", - "shell.execute_reply": "2021-09-26T22:41:53.208345Z" - }, - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
idxunamebusgencoicoi2SnVnfnDMraxlxd1kpkwS10S12gammapgammaqxdxqxd2xq1xq2Td10Td20Tq10Tq20
uid
0GENROU_11.0GENROU_111NoneNone900.020.060.00.0117.000.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
1GENROU_21.0GENROU_222NoneNone900.020.060.00.0117.000.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
2GENROU_31.0GENROU_333NoneNone900.020.060.00.0111.150.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
3GENROU_41.0GENROU_444NoneNone900.020.060.00.0111.150.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
\n", - "
" - ], - "text/plain": [ - " idx u name bus gen coi coi2 Sn Vn fn D \\\n", - "uid \n", - "0 GENROU_1 1.0 GENROU_1 1 1 None None 900.0 20.0 60.0 0.0 \n", - "1 GENROU_2 1.0 GENROU_2 2 2 None None 900.0 20.0 60.0 0.0 \n", - "2 GENROU_3 1.0 GENROU_3 3 3 None None 900.0 20.0 60.0 0.0 \n", - "3 GENROU_4 1.0 GENROU_4 4 4 None None 900.0 20.0 60.0 0.0 \n", - "\n", - " M ra xl xd1 kp kw S10 S12 gammap gammaq xd \\\n", - "uid \n", - "0 117.00 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", - "1 117.00 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", - "2 111.15 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", - "3 111.15 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", - "\n", - " xq xd2 xq1 xq2 Td10 Td20 Tq10 Tq20 \n", - "uid \n", - "0 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", - "1 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", - "2 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", - "3 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 " - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.GENROU.cache.df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One will notice the converted parameters such as `M`, `xl`, and all other impedances." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**It is very important to notice that `cache.df` and `cache.df_in` are both views. Altering data in these views will NOT alter the underlying parameter values.**\n", - "\n", - "To alter values, see the example below." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One may have noticed that `ss.GENROU.cache.df` and `ss.GENROU.as_df()` returns\n", - "the same dataframe. The difference is that the latter creates a new dataframe everytime it is\n", - "called, but the former caches the dataframe when it is initally accessed." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Altering parameters" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Parameters can be altered by calling the `alter` method on a model instance. \n", - "\n", - "We first look up the original value through `get`. \n", - "\n", - "Either `v` or `vin` can be passed to argument `attr` to retrieve the converted or the original data. Here we are retrieving the original input data. If `attr` is not provided, `get` returns the value after per-unit conversion, which is the value used for calculation, by default. " - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.732256Z", - "start_time": "2021-03-19T01:57:44.726208Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.212924Z", - "iopub.status.busy": "2021-09-26T22:41:53.211899Z", - "iopub.status.idle": "2021-09-26T22:41:53.218292Z", - "shell.execute_reply": "2021-09-26T22:41:53.218865Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "13.0" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.GENROU.get(\"M\", \"GENROU_1\", attr='vin')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To change the `M` of `GENROU_1` to `10`, do" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:44.740180Z", - "start_time": "2021-03-19T01:57:44.736736Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.221616Z", - "iopub.status.busy": "2021-09-26T22:41:53.220894Z", - "iopub.status.idle": "2021-09-26T22:41:53.224631Z", - "shell.execute_reply": "2021-09-26T22:41:53.225167Z" - }, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "ss.GENROU.alter(\"M\", \"GENROU_1\", 10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The value set through `alter` is always the data before per-unit conversion - just like it should have been in an input file. ANDES will perform the conversion and set `vin` and `v` correctly." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Parameters altered through `Model.alter()` can be saved as a new system using" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:46.442430Z", - "start_time": "2021-03-19T01:57:44.741232Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.227858Z", - "iopub.status.busy": "2021-09-26T22:41:53.227125Z", - "iopub.status.idle": "2021-09-26T22:41:53.566469Z", - "shell.execute_reply": "2021-09-26T22:41:53.567053Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "xlsx file written to \"new_system.xlsx\"\n" - ] - }, - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "andes.io.xlsx.write(ss, 'new_system.xlsx', overwrite=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### In-place update" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`alter()` can be used to change the value of `ConstService` to modify the\n", - "equation that depend on such `ConstService`. For example, the distributed PV\n", - "model `PVD1` implements a `ConstService` called `pref0` to store the initial\n", - "value of the power reference. An equation associated with variable `Pref`\n", - "enforces that `0 = Pref - pref0`.\n", - "\n", - "If one needs to modify `Pref`, it has to be done through modifying `pref0`.\n", - "Modifying `Pref` directly will not take any effect since the variable will be\n", - "overwritten by the solution of equations. \n", - "\n", - "To update `pref0` for a `PVD1` device with `idx = \"PVD_1\"`, one can do\n", - "\n", - "`ss.PVD1.alter('pref0', 'PVD_1', 0.005)`\n", - "\n", - "or, using keyword arguments in any order, \n", - "\n", - "`ss.PVD1.alter(src='pref0', idx='PVD_1', value=0.005)`\n", - "\n", - "If `PVD_1` is the first (i.e., 0-th in the Python indexing) in the idx list, this modification is equivalent to setting\n", - "\n", - "`ss.PVD1.pref0.v[0] = 0.005`.\n", - "\n", - "Since index `0` is given, the array `ss.PVD1.pref0.v` is updated in-place." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When one needs to modify the `pref0` of all `PVD1` devices to 0.005, one can do\n", - "\n", - "`ss.PVD1.alter('pref0', ss.PVD1.idx.v, 0.005)`\n", - "\n", - "This is equivalent to \n", - "\n", - "`ss.PVD1.pref0.v[:] = 0.005`\n", - "\n", - "Note the `[:]` in the above line. This is a slice operation so that the\n", - "assignment happens in-place.\n", - "\n", - "One must never do out-of-place assignment, i.e.,\n", - "\n", - "`ss.PVD1.pref0.v = 0.005` \n", - "\n", - "or \n", - "\n", - "`ss.PVD1.pref0.v = 0.005 * np.ones_line(ss.PVD1.pref0.v)`\n", - "\n", - "because the assignment will point `ss.PVD1.pref0.v` to a new array. Internally,\n", - "ANDES reuses the memory for all arrays, meaning that their addresses are assumed\n", - "to be constant. If one modifies `ss.PVD1.pref0.v` out of place, the previous\n", - "memory will no longer be accessible through `ss.PVD1.pref0.v`.\n", - "\n", - "On the safe side, one should modify variables using `alter()` or, at least,\n", - "always use in-place assignment to internal arrays." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Refreshing the view" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As mentioned, `cache.df` and `cache.df_in` are *cached* views and will not be automatically updated for inspection.\n", - "\n", - "This is generally not an issue if one performs the simulation after altering data. However, if one needs to inspect the data again, `cache.refresh()` needs to be called manually.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:46.458001Z", - "start_time": "2021-03-19T01:57:46.447362Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.572499Z", - "iopub.status.busy": "2021-09-26T22:41:53.570745Z", - "iopub.status.idle": "2021-09-26T22:41:53.580096Z", - "shell.execute_reply": "2021-09-26T22:41:53.580667Z" - }, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "ss.GENROU.cache.refresh()" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:46.492578Z", - "start_time": "2021-03-19T01:57:46.462694Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.583427Z", - "iopub.status.busy": "2021-09-26T22:41:53.582671Z", - "iopub.status.idle": "2021-09-26T22:41:53.619262Z", - "shell.execute_reply": "2021-09-26T22:41:53.619964Z" - }, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
idxunamebusgencoicoi2SnVnfnDMraxlxd1kpkwS10S12gammapgammaqxdxqxd2xq1xq2Td10Td20Tq10Tq20
uid
0GENROU_11.0GENROU_111NoneNone900.020.060.00.010.000.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
1GENROU_21.0GENROU_222NoneNone900.020.060.00.013.000.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
2GENROU_31.0GENROU_333NoneNone900.020.060.00.012.350.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
3GENROU_41.0GENROU_444NoneNone900.020.060.00.012.350.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
\n", - "
" - ], - "text/plain": [ - " idx u name bus gen coi coi2 Sn Vn fn D \\\n", - "uid \n", - "0 GENROU_1 1.0 GENROU_1 1 1 None None 900.0 20.0 60.0 0.0 \n", - "1 GENROU_2 1.0 GENROU_2 2 2 None None 900.0 20.0 60.0 0.0 \n", - "2 GENROU_3 1.0 GENROU_3 3 3 None None 900.0 20.0 60.0 0.0 \n", - "3 GENROU_4 1.0 GENROU_4 4 4 None None 900.0 20.0 60.0 0.0 \n", - "\n", - " M ra xl xd1 kp kw S10 S12 gammap gammaq xd xq \\\n", - "uid \n", - "0 10.00 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", - "1 13.00 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", - "2 12.35 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", - "3 12.35 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", - "\n", - " xd2 xq1 xq2 Td10 Td20 Tq10 Tq20 \n", - "uid \n", - "0 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", - "1 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", - "2 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", - "3 0.25 0.55 0.25 8.0 0.03 0.4 0.05 " - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.GENROU.cache.df_in" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Alternatively, one can call the `as_df()` function to build a new dataframe *without overwriting* the cache:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
idxunamebusgencoicoi2SnVnfnDMraxlxd1kpkwS10S12gammapgammaqxdxqxd2xq1xq2Td10Td20Tq10Tq20
uid
0GENROU_11.0GENROU_111NoneNone900.020.060.00.090.000.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
1GENROU_21.0GENROU_222NoneNone900.020.060.00.0117.000.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
2GENROU_31.0GENROU_333NoneNone900.020.060.00.0111.150.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
3GENROU_41.0GENROU_444NoneNone900.020.060.00.0111.150.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
\n", - "
" - ], - "text/plain": [ - " idx u name bus gen coi coi2 Sn Vn fn D \\\n", - "uid \n", - "0 GENROU_1 1.0 GENROU_1 1 1 None None 900.0 20.0 60.0 0.0 \n", - "1 GENROU_2 1.0 GENROU_2 2 2 None None 900.0 20.0 60.0 0.0 \n", - "2 GENROU_3 1.0 GENROU_3 3 3 None None 900.0 20.0 60.0 0.0 \n", - "3 GENROU_4 1.0 GENROU_4 4 4 None None 900.0 20.0 60.0 0.0 \n", - "\n", - " M ra xl xd1 kp kw S10 S12 gammap gammaq xd \\\n", - "uid \n", - "0 90.00 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", - "1 117.00 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", - "2 111.15 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", - "3 111.15 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", - "\n", - " xq xd2 xq1 xq2 Td10 Td20 Tq10 Tq20 \n", - "uid \n", - "0 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", - "1 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", - "2 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", - "3 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 " - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.GENROU.as_df()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Variables" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Snapshots" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One might also want to check the variable values in a similar way to that for a parameter. Certainly, a variable has a `v` attribute which stores values. \n", - "\n", - "**It is important to note that `v` only holds the values at the last program state.** Such program state could be the solution of power flow, the initialization of time-domain simulation, or the end of a simulation disturbances. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since we have only ran power flow for ``ss``, ``ss.Bus.v.v`` are the voltage magnitude solutions, where the first `v` is for \"voltage\", and the second `v` is the first `v`'s value attribute." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:46.497826Z", - "start_time": "2021-03-19T01:57:46.494209Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.623325Z", - "iopub.status.busy": "2021-09-26T22:41:53.622522Z", - "iopub.status.idle": "2021-09-26T22:41:53.631840Z", - "shell.execute_reply": "2021-09-26T22:41:53.633842Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1. , 1. , 1. , 1. , 0.98337472,\n", - " 0.96908585, 0.9562181 , 0.95400018, 0.96856366, 0.98377143])" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.Bus.v.v" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Variables hold more than values. They have an attribute `a` for the addresses indexing into the corresponding type of array." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There are two system-level arrays, `ss.dae.x` and `ss.dae.y` for the right-hand-side of the differential and algebraic equations, respectively. " - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:46.502680Z", - "start_time": "2021-03-19T01:57:46.499574Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.637756Z", - "iopub.status.busy": "2021-09-26T22:41:53.636881Z", - "iopub.status.idle": "2021-09-26T22:41:53.644532Z", - "shell.execute_reply": "2021-09-26T22:41:53.645478Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "andes.core.var.Algeb" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(ss.Bus.v)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`ss.Bus.v` is an algebraic variable, thus `ss.Bus.v.a` holds the indices into ``ss.dae.g``." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:46.508151Z", - "start_time": "2021-03-19T01:57:46.504486Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.651249Z", - "iopub.status.busy": "2021-09-26T22:41:53.649274Z", - "iopub.status.idle": "2021-09-26T22:41:53.660558Z", - "shell.execute_reply": "2021-09-26T22:41:53.661510Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1. , 1. , 1. , 1. , 0.98337472,\n", - " 0.96908585, 0.9562181 , 0.95400018, 0.96856366, 0.98377143])" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.dae.y[ss.Bus.v.a]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that these two values are the same." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Time series" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After a time-domain simulation, the time series of the variables can be retrieved through `ss.dae.ts`. Let's first run a simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:48.088224Z", - "start_time": "2021-03-19T01:57:46.510076Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:53.665872Z", - "iopub.status.busy": "2021-09-26T22:41:53.665049Z", - "iopub.status.idle": "2021-09-26T22:41:54.800200Z", - "shell.execute_reply": "2021-09-26T22:41:54.800737Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "-> Time Domain Simulation Summary:\n", - "Sparse Solver: KLU\n", - "Simulation time: 0.0-20.0 s.\n", - "Fixed step size: h=33.33 ms. Shrink if not converged.\n", - "Initialization for dynamics completed in 0.0489 seconds.\n", - "Initialization was successful.\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "2429fb4a50554e308c65f5d1c4f839d4", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - " 0%| | 0/100 [00:00: Line.Line_8 status changed to 0 at t=2.0 sec.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Simulation completed in 1.1192 seconds.\n", - "Outputs to \"kundur_out.lst\" and \"kundur_out.npz\".\n", - "Outputs written in 0.0226 seconds.\n" - ] - }, - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.TDS.run()" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:48.098566Z", - "start_time": "2021-03-19T01:57:48.094479Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:54.803246Z", - "iopub.status.busy": "2021-09-26T22:41:54.802554Z", - "iopub.status.idle": "2021-09-26T22:41:54.808083Z", - "shell.execute_reply": "2021-09-26T22:41:54.807578Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.dae.ts" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`ss.dae.ts` has four commonly used attributes: `t` for time stamps, `xy` for variables (differential and then algebraic), `z` for discontinuous states, and `df` for the dataframe of all.\n", - "\n", - "- Each point in `ss.dae.ts.t` correspond to a row in `ss.dae.ts.xy`.\n", - "- Each column in `ss.dae.ts.xy` correspond to a variable, whose name can be located in `ss.dae.xy_name`, for all timestamps.\n", - "- `z` is not stored by default unless one enables it before simulation by setting `ss.TDS.config.store_z = 1`. \n", - "- `df` is not built by default but can be manually triggered after simulation by calling `ss.dae.ts.unpack(df=True)`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following are some statistics of the shapes of arrays:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:48.107524Z", - "start_time": "2021-03-19T01:57:48.103549Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:54.812055Z", - "iopub.status.busy": "2021-09-26T22:41:54.811606Z", - "iopub.status.idle": "2021-09-26T22:41:54.815761Z", - "shell.execute_reply": "2021-09-26T22:41:54.815414Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(603,)" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.dae.ts.t.shape" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:48.116573Z", - "start_time": "2021-03-19T01:57:48.110379Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:54.818527Z", - "iopub.status.busy": "2021-09-26T22:41:54.817728Z", - "iopub.status.idle": "2021-09-26T22:41:54.823096Z", - "shell.execute_reply": "2021-09-26T22:41:54.823642Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(603, 201)" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.dae.ts.xy.shape # x-axis is for time stamps, and y-axis is for variables" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:48.125081Z", - "start_time": "2021-03-19T01:57:48.119155Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:54.826233Z", - "iopub.status.busy": "2021-09-26T22:41:54.825522Z", - "iopub.status.idle": "2021-09-26T22:41:54.846410Z", - "shell.execute_reply": "2021-09-26T22:41:54.847005Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "201" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "len(ss.dae.xy_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Extracting Variable Data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's extract the data for rotor speed (variable `omega`) of `GENROU` generators.\n", - "The first step to extract variable data is to determine the type of the variable: differential or algebraic.\n", - "One can print the variable to see the type:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "State: GENROU.omega, a=[4 5 6 7], v=[1.00165687 1.00166417 1.00182915 1.00184744], e=[-0.00251535 -0.00375723 0.00131868 0.00182826]" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.GENROU.omega" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The output shows that `omega` is a state (differential variable), which should be looked up in `ss.dae.x`. For algebraic variables such as `ss.Bus.v`, they should be looked up in `ss.dae.y`.\n", - "\n", - "Therefore, all `omega` variables can be extracted as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "omega = ss.dae.ts.x[:, ss.GENROU.omega.a]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "where the `:` in the first axis will access such data for all time stamps, and\n", - "`ss.GENROU.omega.a` stores the addresses of all `omega` into `ss.dae.x`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To access all bus voltages (algebraic variable `v`) of the generators, one can use:" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1. , 1. , 1. , 1. ],\n", - " [1. , 1. , 1. , 1. ],\n", - " [1. , 1. , 1. , 1. ],\n", - " ...,\n", - " [1.00240968, 1.00148908, 0.99526693, 1.00007159],\n", - " [1.00249935, 1.00159007, 0.99515528, 0.99997846],\n", - " [1.00259067, 1.0016924 , 0.99504062, 0.999883 ]])" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.dae.ts.y[:, ss.GENROU.v.a]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These data correspond to the timestamps stored in `ss.dae.ts.t`. One can process such data as necessary. \n", - "\n", - "To show verify the extracted data, we plot them with `ss.TDS.plt.plot_data`." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "(
, )" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.TDS.plt.plot_data(ss.dae.ts.t, omega )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Altering Fault duration" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "``Alter`` can be used for updating any model parameter. We show another example\n", - "of updating the duration of a fault. Using the `ieee14_fault.xlsx` test case, we have" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Working directory: \"/home/hacui/repos/andes/examples\"\n", - "> Loaded config from file \"/home/hacui/.andes/andes.rc\"\n", - "> Reloaded generated Python code of module \"pycode\".\n", - "Parsing input file \"/home/hacui/repos/andes/andes/cases/ieee14/ieee14_fault.xlsx\"...\n", - "Input file parsed in 0.0546 seconds.\n", - "System internal structure set up in 0.0348 seconds.\n", - "-> System connectivity check results:\n", - " No islanded bus detected.\n", - " System is interconnected.\n", - " Each island has a slack bus correctly defined and enabled.\n", - "\n", - "-> Power flow calculation\n", - " Numba: Off\n", - " Sparse solver: KLU\n", - " Solution method: NR method\n", - "Power flow initialized in 0.0037 seconds.\n", - "0: |F(x)| = 0.5605182134\n", - "1: |F(x)| = 0.006202200332\n", - "2: |F(x)| = 5.819382824e-06\n", - "3: |F(x)| = 6.96508129e-12\n", - "Converged in 4 iterations in 0.0047 seconds.\n", - "Initialization for dynamics completed in 0.0353 seconds.\n", - "Initialization was successful.\n", - "Report saved to \"ieee14_fault_out.txt\" in 0.0021 seconds.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "-> Single process finished in 0.3141 seconds.\n" - ] - } - ], - "source": [ - "ss = andes.run(get_case(\"ieee14/ieee14_fault.xlsx\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Again, if you need to add devices, you should use ``ss = andes.load(..,\n", - "setup=False)`` and ``ss.setup()`` instead of ``andes.run()``.\n", - "\n", - "List the existing ``Fault`` devices:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
idxunamebustftcxfrf
uid
011.0Fault_191.01.10.00010.0
\n", - "
" - ], - "text/plain": [ - " idx u name bus tf tc xf rf\n", - "uid \n", - "0 1 1.0 Fault_1 9 1.0 1.1 0.0001 0.0" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.Fault.as_df()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One fault on Bus 9 is applied at t=1.0 sec and cleared at t=1.1 sec. Suppose\n", - "that we want to clear the fault at t = 1.05 sec, we can do" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "ss.Fault.alter('tc', 1, 1.05) # arguments are `src`, `idx`, `value`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "where `tc` is the parameter to alter, `1` is the idx of the Fault to find, and\n", - "`1.05` is the new value. Inspect the Fault devices to see the updated value.\n", - "The simulation for the new system can be performed next." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
idxunamebustftcxfrf
uid
011.0Fault_191.01.050.00010.0
\n", - "
" - ], - "text/plain": [ - " idx u name bus tf tc xf rf\n", - "uid \n", - "0 1 1.0 Fault_1 9 1.0 1.05 0.0001 0.0" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ss.Fault.as_df()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Cleanup" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-19T01:57:49.038948Z", - "start_time": "2021-03-19T01:57:48.127782Z" - }, - "execution": { - "iopub.execute_input": "2021-09-26T22:41:54.849805Z", - "iopub.status.busy": "2021-09-26T22:41:54.849023Z", - "iopub.status.idle": "2021-09-26T22:41:56.074777Z", - "shell.execute_reply": "2021-09-26T22:41:56.074018Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\"/home/hacui/repos/andes/examples/kundur_full_out.txt\" removed.\r\n", - "\"/home/hacui/repos/andes/examples/kundur_out.lst\" removed.\r\n", - "\"/home/hacui/repos/andes/examples/kundur_out.npz\" removed.\r\n", - "\"/home/hacui/repos/andes/examples/ieee14_fault_out.txt\" removed.\r\n", - "\"/home/hacui/repos/andes/examples/kundur_out.txt\" removed.\r\n" - ] - } - ], - "source": [ - "!andes misc -C" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": { - "execution": { - "iopub.execute_input": "2021-09-26T22:41:56.081852Z", - "iopub.status.busy": "2021-09-26T22:41:56.081368Z", - "iopub.status.idle": "2021-09-26T22:41:56.391028Z", - "shell.execute_reply": "2021-09-26T22:41:56.390296Z" - } - }, - "outputs": [], - "source": [ - "!rm new_system.xlsx" - ] - } - ], + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Working with Data\n", + "\n", + "This example shows how to work with the data of a loaded test system, including parameters and variables." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:43.135158Z", + "start_time": "2021-03-19T01:57:42.407900Z" + } + }, + "outputs": [], + "source": [ + "import andes\n", + "from andes.utils.paths import get_case\n", + "\n", + "andes.config_logger()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To show all the rows and columns, change the pandas configuration with" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:43.381726Z", + "start_time": "2021-03-19T01:57:43.140307Z" + } + }, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "pd.options.display.max_columns = None\n", + "pd.options.display.max_rows = None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's load the Kundur's system." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load System from an ANDES XLSX File\n", + "\n", + "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/curent/andes/blob/master/README.md#format-converter" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As previously shown, test cases can be loaded with ``andes.run()``:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:43.965479Z", + "start_time": "2021-03-19T01:57:43.383941Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Working directory: \"/Users/jinningwang/work/andes/examples\"\n", + "> Loaded generated Python code in \"/Users/jinningwang/.andes/pycode\".\n", + "Generated code for is stale.\n", + "Numerical code generation (rapid incremental mode) started...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generating code for 1 models on 12 processes.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Saved generated pycode to \"/Users/jinningwang/.andes/pycode\"\n", + "> Reloaded generated Python code of module \"pycode\".\n", + "Generated numerical code for 1 models in 0.1992 seconds.\n", + "Parsing input file \"/Users/jinningwang/work/andes/andes/cases/kundur/kundur_full.xlsx\"...\n", + "Input file parsed in 0.0667 seconds.\n", + "System internal structure set up in 0.0163 seconds.\n", + "-> System connectivity check results:\n", + " No islanded bus detected.\n", + " System is interconnected.\n", + " Each island has a slack bus correctly defined and enabled.\n", + "\n", + "-> Power flow calculation\n", + " Numba: Off\n", + " Sparse solver: KLU\n", + " Solution method: NR method\n", + "Power flow initialized in 0.0019 seconds.\n", + "0: |F(x)| = 14.9282832\n", + "1: |F(x)| = 3.608627841\n", + "2: |F(x)| = 0.1701107882\n", + "3: |F(x)| = 0.002038626956\n", + "4: |F(x)| = 3.745103979e-07\n", + "Converged in 5 iterations in 0.0027 seconds.\n", + "Report saved to \"kundur_full_out.txt\" in 0.0007 seconds.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-> Single process finished in 0.4420 seconds.\n" + ] + } + ], + "source": [ + "ss = andes.run(get_case('kundur/kundur_full.xlsx'),\n", + " default_config=True) # one can remove `default_config=True` to use custom config file" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Alternatively, one can load a test case _without setting up_ using `andes.load(..., setup=False)`. Note that `setup=False` option.\n", + "It is useful to apply parameter changes to an existing test case." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.168248Z", + "start_time": "2021-03-19T01:57:43.970545Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Working directory: \"/Users/jinningwang/work/andes/examples\"\n", + "> Reloaded generated Python code of module \"pycode\".\n", + "Generated code for is stale.\n", + "Numerical code generation (rapid incremental mode) started...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generating code for 1 models on 12 processes.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Saved generated pycode to \"/Users/jinningwang/.andes/pycode\"\n", + "> Reloaded generated Python code of module \"pycode\".\n", + "Generated numerical code for 1 models in 0.0892 seconds.\n", + "Parsing input file \"/Users/jinningwang/work/andes/andes/cases/kundur/kundur_full.xlsx\"...\n", + "Input file parsed in 0.0190 seconds.\n" + ] + } + ], + "source": [ + "ss = andes.load(get_case('kundur/kundur_full.xlsx'),\n", + " default_config=True,\n", + " setup=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-18T00:25:32.261166Z", + "start_time": "2021-03-18T00:25:32.256088Z" + } + }, + "source": [ + "For example, we can toggle the connectivity status `u` of `Line_3` to `0` using" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.176858Z", + "start_time": "2021-03-19T01:57:44.173846Z" + } + }, + "outputs": [], + "source": [ + "ss.Line.alter('u', 'Line_3', 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When done, remember to set up the system before running calculation routines:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.266523Z", + "start_time": "2021-03-19T01:57:44.181450Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "System internal structure set up in 0.0186 seconds.\n", + "-> System connectivity check results:\n", + " No islanded bus detected.\n", + " System is interconnected.\n", + " Each island has a slack bus correctly defined and enabled.\n", + "\n", + "-> Power flow calculation\n", + " Numba: Off\n", + " Sparse solver: KLU\n", + " Solution method: NR method\n", + "Power flow initialized in 0.0021 seconds.\n", + "0: |F(x)| = 14.9282832\n", + "1: |F(x)| = 3.579044433\n", + "2: |F(x)| = 0.119268955\n", + "3: |F(x)| = 0.03278820195\n", + "4: |F(x)| = 2.880943096e-05\n", + "5: |F(x)| = 3.937117299e-11\n", + "Converged in 6 iterations in 0.0038 seconds.\n", + "Report saved to \"kundur_full_out.txt\" in 0.0010 seconds.\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.setup()\n", + "\n", + "ss.PFlow.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After setting up the system, adding or removing devices are not yet allowed." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-18T00:28:42.135119Z", + "start_time": "2021-03-18T00:28:42.128972Z" + } + }, + "source": [ + "## Load System from PSS/E RAW and DYR Files\n", + "\n", + "ANDES supports loading systems from PSS/E RAW and DYR files.\n", + "\n", + "The PSS/E v32 raw format is best supported.\n", + "\n", + "Note that this feature is experimental. We try out best to support this format, but the compatibility is not guaranteed." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.274794Z", + "start_time": "2021-03-19T01:57:44.271077Z" + } + }, + "outputs": [], + "source": [ + "raw_path = get_case('kundur/kundur.raw')\n", + "dyr_path = get_case('kundur/kundur_full.dyr')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The raw file is passed to the positional argument, whereas the dyr file is passed to `addfile`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.606632Z", + "start_time": "2021-03-19T01:57:44.277112Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Working directory: \"/Users/jinningwang/work/andes/examples\"\n", + "> Reloaded generated Python code of module \"pycode\".\n", + "Generated code for is stale.\n", + "Numerical code generation (rapid incremental mode) started...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generating code for 1 models on 12 processes.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Saved generated pycode to \"/Users/jinningwang/.andes/pycode\"\n", + "> Reloaded generated Python code of module \"pycode\".\n", + "Generated numerical code for 1 models in 0.0867 seconds.\n", + "Parsing input file \"/Users/jinningwang/work/andes/andes/cases/kundur/kundur.raw\"...\n", + " MODIFIED KUNDUR'S TWO-AREA TEST SYSTEM, DISTRIBUTED WITH ANDES\n", + " SEE THE BOOK \"POWER SYSTEM STABILITY AND CONTROL\" FOR ORIGINAL DATA\n", + "Input file parsed in 0.0040 seconds.\n", + "Parsing additional file \"/Users/jinningwang/work/andes/andes/cases/kundur/kundur_full.dyr\"...\n", + "Addfile parsed in 0.0423 seconds.\n", + "System internal structure set up in 0.0163 seconds.\n", + "-> System connectivity check results:\n", + " No islanded bus detected.\n", + " System is interconnected.\n", + " Each island has a slack bus correctly defined and enabled.\n", + "\n", + "-> Power flow calculation\n", + " Numba: Off\n", + " Sparse solver: KLU\n", + " Solution method: NR method\n", + "Power flow initialized in 0.0017 seconds.\n", + "0: |F(x)| = 3.175850023\n", + "1: |F(x)| = 3.176155228e-08\n", + "Converged in 2 iterations in 0.0012 seconds.\n", + "Report saved to \"kundur_out.txt\" in 0.0005 seconds.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-> Single process finished in 0.2407 seconds.\n" + ] + } + ], + "source": [ + "ss = andes.run(raw_path, addfile=dyr_path, default_config=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Attributes for storing values" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Parameters are stored as attributes of the model. For example, `ss.GENROU.M`, the machine starting time constant (`2H`), is stored in `ss.GENROU.M`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.618529Z", + "start_time": "2021-03-19T01:57:44.611845Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NumParam: GENROU.M, v=[117. 117. 111.15 111.15], vin=[13. 13. 12.35 12.35]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.GENROU.M" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is an instance of `NumParam`, which contains fields `v` for the values after converting to system-base per unit values." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.629108Z", + "start_time": "2021-03-19T01:57:44.623058Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([117. , 117. , 111.15, 111.15])" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.GENROU.M.v" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And field `vin` is for the original input data." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.639730Z", + "start_time": "2021-03-19T01:57:44.633864Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([13. , 13. , 12.35, 12.35])" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.GENROU.M.vin" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Tabulated view" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "ANDES provides tabulated **view** of model parameters by using DataFrame. Each model object has an attribute called `cache` for caching the parameter dataframes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The original parameters from the input file are stored in `cache.df_in` of the model object. For `GENROU`, do" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.681593Z", + "start_time": "2021-03-19T01:57:44.644323Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idxunamebusgencoicoi2SnVnfnDMraxlxd1kpkwS10S12gammapgammaqxdxqxd2xq1xq2Td10Td20Tq10Tq20
uid
0GENROU_11.0GENROU_111NoneNone900.020.060.00.013.000.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
1GENROU_21.0GENROU_222NoneNone900.020.060.00.013.000.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
2GENROU_31.0GENROU_333NoneNone900.020.060.00.012.350.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
3GENROU_41.0GENROU_444NoneNone900.020.060.00.012.350.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
\n", + "
" + ], + "text/plain": [ + " idx u name bus gen coi coi2 Sn Vn fn D \\\n", + "uid \n", + "0 GENROU_1 1.0 GENROU_1 1 1 None None 900.0 20.0 60.0 0.0 \n", + "1 GENROU_2 1.0 GENROU_2 2 2 None None 900.0 20.0 60.0 0.0 \n", + "2 GENROU_3 1.0 GENROU_3 3 3 None None 900.0 20.0 60.0 0.0 \n", + "3 GENROU_4 1.0 GENROU_4 4 4 None None 900.0 20.0 60.0 0.0 \n", + "\n", + " M ra xl xd1 kp kw S10 S12 gammap gammaq xd xq \\\n", + "uid \n", + "0 13.00 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", + "1 13.00 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", + "2 12.35 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", + "3 12.35 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", + "\n", + " xd2 xq1 xq2 Td10 Td20 Tq10 Tq20 \n", + "uid \n", + "0 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", + "1 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", + "2 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", + "3 0.25 0.55 0.25 8.0 0.03 0.4 0.05 " + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.GENROU.cache.df_in" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Parameters will be **converted** to per-unit in the system base after loading. This process have been done if `andes.run` is used for loading the data file.\n", + "\n", + "To inspect the converted parameters, check the `cache.df` parameter." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.721712Z", + "start_time": "2021-03-19T01:57:44.686492Z" + }, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idxunamebusgencoicoi2SnVnfnDMraxlxd1kpkwS10S12gammapgammaqxdxqxd2xq1xq2Td10Td20Tq10Tq20
uid
0GENROU_11.0GENROU_111NoneNone900.020.060.00.0117.000.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
1GENROU_21.0GENROU_222NoneNone900.020.060.00.0117.000.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
2GENROU_31.0GENROU_333NoneNone900.020.060.00.0111.150.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
3GENROU_41.0GENROU_444NoneNone900.020.060.00.0111.150.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
\n", + "
" + ], + "text/plain": [ + " idx u name bus gen coi coi2 Sn Vn fn D \\\n", + "uid \n", + "0 GENROU_1 1.0 GENROU_1 1 1 None None 900.0 20.0 60.0 0.0 \n", + "1 GENROU_2 1.0 GENROU_2 2 2 None None 900.0 20.0 60.0 0.0 \n", + "2 GENROU_3 1.0 GENROU_3 3 3 None None 900.0 20.0 60.0 0.0 \n", + "3 GENROU_4 1.0 GENROU_4 4 4 None None 900.0 20.0 60.0 0.0 \n", + "\n", + " M ra xl xd1 kp kw S10 S12 gammap gammaq xd \\\n", + "uid \n", + "0 117.00 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", + "1 117.00 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", + "2 111.15 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", + "3 111.15 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", + "\n", + " xq xd2 xq1 xq2 Td10 Td20 Tq10 Tq20 \n", + "uid \n", + "0 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", + "1 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", + "2 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", + "3 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 " + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.GENROU.cache.df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One will notice the converted parameters such as `M`, `xl`, and all other impedances." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**It is very important to notice that `cache.df` and `cache.df_in` are both views. Altering data in these views will NOT alter the underlying parameter values.**\n", + "\n", + "To alter values, see the example below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One may have noticed that `ss.GENROU.cache.df` and `ss.GENROU.as_df()` returns\n", + "the same dataframe. The difference is that the latter creates a new dataframe everytime it is\n", + "called, but the former caches the dataframe when it is initally accessed." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Altering parameters" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Parameters can be altered by calling the `alter` method on a model instance. \n", + "\n", + "We first look up the original value through `get`. \n", + "\n", + "Either `v` or `vin` can be passed to argument `attr` to retrieve the converted or the original data. Here we are retrieving the original input data. If `attr` is not provided, `get` returns the value after per-unit conversion, which is the value used for calculation, by default. " + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.732256Z", + "start_time": "2021-03-19T01:57:44.726208Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13.0" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.GENROU.get(\"M\", \"GENROU_1\", attr='vin')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To change the `M` of `GENROU_1` to `10`, do" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:44.740180Z", + "start_time": "2021-03-19T01:57:44.736736Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "ss.GENROU.alter(\"M\", \"GENROU_1\", 10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The value set through `alter` is always the data before per-unit conversion - just like it should have been in an input file. ANDES will perform the conversion and set `vin` and `v` correctly." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Parameters altered through `Model.alter()` can be saved as a new system using" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:46.442430Z", + "start_time": "2021-03-19T01:57:44.741232Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "xlsx file written to \"new_system.xlsx\"\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "andes.io.xlsx.write(ss, 'new_system.xlsx', overwrite=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### In-place update" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`alter()` can be used to change the value of `ConstService` to modify the\n", + "equation that depend on such `ConstService`. For example, the distributed PV\n", + "model `PVD1` implements a `ConstService` called `pref0` to store the initial\n", + "value of the power reference. An equation associated with variable `Pref`\n", + "enforces that `0 = Pref - pref0`.\n", + "\n", + "If one needs to modify `Pref`, it has to be done through modifying `pref0`.\n", + "Modifying `Pref` directly will not take any effect since the variable will be\n", + "overwritten by the solution of equations. \n", + "\n", + "To update `pref0` for a `PVD1` device with `idx = \"PVD_1\"`, one can do\n", + "\n", + "`ss.PVD1.alter('pref0', 'PVD_1', 0.005)`\n", + "\n", + "or, using keyword arguments in any order, \n", + "\n", + "`ss.PVD1.alter(src='pref0', idx='PVD_1', value=0.005)`\n", + "\n", + "If `PVD_1` is the first (i.e., 0-th in the Python indexing) in the idx list, this modification is equivalent to setting\n", + "\n", + "`ss.PVD1.pref0.v[0] = 0.005`.\n", + "\n", + "Since index `0` is given, the array `ss.PVD1.pref0.v` is updated in-place." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When one needs to modify the `pref0` of all `PVD1` devices to 0.005, one can do\n", + "\n", + "`ss.PVD1.alter('pref0', ss.PVD1.idx.v, 0.005)`\n", + "\n", + "This is equivalent to \n", + "\n", + "`ss.PVD1.pref0.v[:] = 0.005`\n", + "\n", + "Note the `[:]` in the above line. This is a slice operation so that the\n", + "assignment happens in-place.\n", + "\n", + "One must never do out-of-place assignment, i.e.,\n", + "\n", + "`ss.PVD1.pref0.v = 0.005` \n", + "\n", + "or \n", + "\n", + "`ss.PVD1.pref0.v = 0.005 * np.ones_line(ss.PVD1.pref0.v)`\n", + "\n", + "because the assignment will point `ss.PVD1.pref0.v` to a new array. Internally,\n", + "ANDES reuses the memory for all arrays, meaning that their addresses are assumed\n", + "to be constant. If one modifies `ss.PVD1.pref0.v` out of place, the previous\n", + "memory will no longer be accessible through `ss.PVD1.pref0.v`.\n", + "\n", + "On the safe side, one should modify variables using `alter()` or, at least,\n", + "always use in-place assignment to internal arrays." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Refreshing the view" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As mentioned, `cache.df` and `cache.df_in` are *cached* views and will not be automatically updated for inspection.\n", + "\n", + "This is generally not an issue if one performs the simulation after altering data. However, if one needs to inspect the data again, `cache.refresh()` needs to be called manually.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:46.458001Z", + "start_time": "2021-03-19T01:57:46.447362Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "ss.GENROU.cache.refresh()" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:46.492578Z", + "start_time": "2021-03-19T01:57:46.462694Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idxunamebusgencoicoi2SnVnfnDMraxlxd1kpkwS10S12gammapgammaqxdxqxd2xq1xq2Td10Td20Tq10Tq20
uid
0GENROU_11.0GENROU_111NoneNone900.020.060.00.010.000.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
1GENROU_21.0GENROU_222NoneNone900.020.060.00.013.000.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
2GENROU_31.0GENROU_333NoneNone900.020.060.00.012.350.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
3GENROU_41.0GENROU_444NoneNone900.020.060.00.012.350.00.060.30.00.00.00.01.01.01.81.70.250.550.258.00.030.40.05
\n", + "
" + ], + "text/plain": [ + " idx u name bus gen coi coi2 Sn Vn fn D \\\n", + "uid \n", + "0 GENROU_1 1.0 GENROU_1 1 1 None None 900.0 20.0 60.0 0.0 \n", + "1 GENROU_2 1.0 GENROU_2 2 2 None None 900.0 20.0 60.0 0.0 \n", + "2 GENROU_3 1.0 GENROU_3 3 3 None None 900.0 20.0 60.0 0.0 \n", + "3 GENROU_4 1.0 GENROU_4 4 4 None None 900.0 20.0 60.0 0.0 \n", + "\n", + " M ra xl xd1 kp kw S10 S12 gammap gammaq xd xq \\\n", + "uid \n", + "0 10.00 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", + "1 13.00 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", + "2 12.35 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", + "3 12.35 0.0 0.06 0.3 0.0 0.0 0.0 0.0 1.0 1.0 1.8 1.7 \n", + "\n", + " xd2 xq1 xq2 Td10 Td20 Tq10 Tq20 \n", + "uid \n", + "0 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", + "1 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", + "2 0.25 0.55 0.25 8.0 0.03 0.4 0.05 \n", + "3 0.25 0.55 0.25 8.0 0.03 0.4 0.05 " + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.GENROU.cache.df_in" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Alternatively, one can call the `as_df()` function to build a new dataframe *without overwriting* the cache:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idxunamebusgencoicoi2SnVnfnDMraxlxd1kpkwS10S12gammapgammaqxdxqxd2xq1xq2Td10Td20Tq10Tq20
uid
0GENROU_11.0GENROU_111NoneNone900.020.060.00.090.000.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
1GENROU_21.0GENROU_222NoneNone900.020.060.00.0117.000.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
2GENROU_31.0GENROU_333NoneNone900.020.060.00.0111.150.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
3GENROU_41.0GENROU_444NoneNone900.020.060.00.0111.150.00.0066670.0333330.00.00.00.01.01.00.20.1888890.0277780.0611110.0277788.00.030.40.05
\n", + "
" + ], + "text/plain": [ + " idx u name bus gen coi coi2 Sn Vn fn D \\\n", + "uid \n", + "0 GENROU_1 1.0 GENROU_1 1 1 None None 900.0 20.0 60.0 0.0 \n", + "1 GENROU_2 1.0 GENROU_2 2 2 None None 900.0 20.0 60.0 0.0 \n", + "2 GENROU_3 1.0 GENROU_3 3 3 None None 900.0 20.0 60.0 0.0 \n", + "3 GENROU_4 1.0 GENROU_4 4 4 None None 900.0 20.0 60.0 0.0 \n", + "\n", + " M ra xl xd1 kp kw S10 S12 gammap gammaq xd \\\n", + "uid \n", + "0 90.00 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", + "1 117.00 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", + "2 111.15 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", + "3 111.15 0.0 0.006667 0.033333 0.0 0.0 0.0 0.0 1.0 1.0 0.2 \n", + "\n", + " xq xd2 xq1 xq2 Td10 Td20 Tq10 Tq20 \n", + "uid \n", + "0 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", + "1 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", + "2 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 \n", + "3 0.188889 0.027778 0.061111 0.027778 8.0 0.03 0.4 0.05 " + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.GENROU.as_df()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Variables" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Snapshots" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One might also want to check the variable values in a similar way to that for a parameter. Certainly, a variable has a `v` attribute which stores values. \n", + "\n", + "**It is important to note that `v` only holds the values at the last program state.** Such program state could be the solution of power flow, the initialization of time-domain simulation, or the end of a simulation disturbances. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since we have only ran power flow for ``ss``, ``ss.Bus.v.v`` are the voltage magnitude solutions, where the first `v` is for \"voltage\", and the second `v` is the first `v`'s value attribute." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:46.497826Z", + "start_time": "2021-03-19T01:57:46.494209Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1. , 1. , 1. , 1. , 0.98337472,\n", + " 0.96908585, 0.9562181 , 0.95400018, 0.96856366, 0.98377143])" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.Bus.v.v" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Variables hold more than values. They have an attribute `a` for the addresses indexing into the corresponding type of array." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are two system-level arrays, `ss.dae.x` and `ss.dae.y` for the right-hand-side of the differential and algebraic equations, respectively. " + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:46.502680Z", + "start_time": "2021-03-19T01:57:46.499574Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "andes.core.var.Algeb" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(ss.Bus.v)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`ss.Bus.v` is an algebraic variable, thus `ss.Bus.v.a` holds the indices into ``ss.dae.g``." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:46.508151Z", + "start_time": "2021-03-19T01:57:46.504486Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1. , 1. , 1. , 1. , 0.98337472,\n", + " 0.96908585, 0.9562181 , 0.95400018, 0.96856366, 0.98377143])" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.dae.y[ss.Bus.v.a]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that these two values are the same." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Time series" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After a time-domain simulation, the time series of the variables can be retrieved through `ss.dae.ts`. Let's first run a simulation." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:48.088224Z", + "start_time": "2021-03-19T01:57:46.510076Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Initialization for dynamics completed in 0.0135 seconds.\n", + "Initialization was successful.\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ad91dbd6915348188a208f426ddacffc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/100 [00:00: Line.Line_8 status changed to 0 at t=2.0 sec.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Simulation to t=20.00 sec completed in 0.5226 seconds.\n", + "Outputs to \"kundur_out.lst\" and \"kundur_out.npz\".\n", + "Outputs written in 0.0165 seconds.\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.TDS.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:48.098566Z", + "start_time": "2021-03-19T01:57:48.094479Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.dae.ts" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`ss.dae.ts` has four commonly used attributes: `t` for time stamps, `xy` for variables (differential and then algebraic), `z` for discontinuous states, and `df` for the dataframe of all.\n", + "\n", + "- Each point in `ss.dae.ts.t` correspond to a row in `ss.dae.ts.xy`.\n", + "- Each column in `ss.dae.ts.xy` correspond to a variable, whose name can be located in `ss.dae.xy_name`, for all timestamps.\n", + "- `z` is not stored by default unless one enables it before simulation by setting `ss.TDS.config.store_z = 1`. \n", + "- `df` is not built by default but can be manually triggered after simulation by calling `ss.dae.ts.unpack(df=True)`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following are some statistics of the shapes of arrays:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:48.107524Z", + "start_time": "2021-03-19T01:57:48.103549Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(603,)" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.dae.ts.t.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:48.116573Z", + "start_time": "2021-03-19T01:57:48.110379Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(603, 201)" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.dae.ts.xy.shape # x-axis is for time stamps, and y-axis is for variables" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:48.125081Z", + "start_time": "2021-03-19T01:57:48.119155Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "201" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(ss.dae.xy_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Extracting Variable Data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's extract the data for rotor speed (variable `omega`) of `GENROU` generators.\n", + "The first step to extract variable data is to determine the type of the variable: differential or algebraic.\n", + "One can print the variable to see the type:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "State: GENROU.omega, a=[4 5 6 7], v=[1.00165687 1.00166417 1.00182915 1.00184744], e=[-0.00251535 -0.00375723 0.00131868 0.00182826]" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.GENROU.omega" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The output shows that `omega` is a state (differential variable), which should be looked up in `ss.dae.x`. For algebraic variables such as `ss.Bus.v`, they should be looked up in `ss.dae.y`.\n", + "\n", + "Therefore, all `omega` variables can be extracted as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "omega = ss.dae.ts.x[:, ss.GENROU.omega.a]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "where the `:` in the first axis will access such data for all time stamps, and\n", + "`ss.GENROU.omega.a` stores the addresses of all `omega` into `ss.dae.x`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To access all bus voltages (algebraic variable `v`) of the generators, one can use:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1. , 1. , 1. , 1. ],\n", + " [1. , 1. , 1. , 1. ],\n", + " [1. , 1. , 1. , 1. ],\n", + " ...,\n", + " [1.00240968, 1.00148908, 0.99526693, 1.00007159],\n", + " [1.00249935, 1.00159007, 0.99515528, 0.99997846],\n", + " [1.00259067, 1.0016924 , 0.99504062, 0.999883 ]])" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.dae.ts.y[:, ss.GENROU.v.a]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "These data correspond to the timestamps stored in `ss.dae.ts.t`. One can process such data as necessary. \n", + "\n", + "To show verify the extracted data, we plot them with `ss.TDS.plt.plot_data`." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/jinningwang/work/andes/andes/plot.py:822: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown\n", + " plt.show()\n" + ] + }, + { + "data": { + "text/plain": [ + "(
, )" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.TDS.plt.plot_data(ss.dae.ts.t, omega )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Note:** As of v1.2.6, `DAETimeSeries.get_data()` can be used to extract data for a variable. This eliminates the need to distinguish whether the variable is algebraic or a state variable." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on method get_data in module andes.variables.dae:\n", + "\n", + "get_data(base_vars: Union[andes.core.var.BaseVar, List[andes.core.var.BaseVar]], *, a=None, rhs: bool = False) method of andes.variables.dae.DAETimeSeries instance\n", + " Get time-series data, either for a variable or for the equation\n", + " associated with the variable.\n", + "\n", + " Parameters\n", + " ----------\n", + " base_var : BaseVar or a sequence of BaseVar(s)\n", + " The variable types and internal addresses are used for looking up\n", + " the data.\n", + " a : an array/list of int or None\n", + " Sub-indices into the address of `base_var`. Applied to each\n", + " variable.\n", + "\n", + " Returns\n", + " -------\n", + " np.ndarray\n", + "\n", + " A two-dimensional array. Each row corresponds to one time step. Each\n", + " column corresponds to a different different variable.\n", + "\n" + ] + } + ], + "source": [ + "help(ss.dae.ts.get_data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For example, following code is equivalent to the above code for extracting `omega`" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "omega2 = ss.dae.ts.get_data(ss.GENROU.omega)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Altering Fault duration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``Alter`` can be used for updating any model parameter. We show another example\n", + "of updating the duration of a fault. Using the `ieee14_fault.xlsx` test case, we have" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Working directory: \"/Users/jinningwang/work/andes/examples\"\n", + "> Loaded config from file \"/Users/jinningwang/.andes/andes.rc\"\n", + "> Reloaded generated Python code of module \"pycode\".\n", + "Generated code for is stale.\n", + "Numerical code generation (rapid incremental mode) started...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generating code for 1 models on 12 processes.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Saved generated pycode to \"/Users/jinningwang/.andes/pycode\"\n", + "> Reloaded generated Python code of module \"pycode\".\n", + "Generated numerical code for 1 models in 0.0874 seconds.\n", + "Parsing input file \"/Users/jinningwang/work/andes/andes/cases/ieee14/ieee14_fault.xlsx\"...\n", + "Input file parsed in 0.0244 seconds.\n", + "System internal structure set up in 0.0172 seconds.\n", + "-> System connectivity check results:\n", + " No islanded bus detected.\n", + " System is interconnected.\n", + " Each island has a slack bus correctly defined and enabled.\n", + "\n", + "-> Power flow calculation\n", + " Numba: On\n", + " Sparse solver: KLU\n", + " Solution method: NR method\n", + "Numba compilation initiated with caching.\n", + "Power flow initialized in 0.1611 seconds.\n", + "Numba compilation for power flow finished in 0.0995 seconds.\n", + "0: |F(x)| = 0.5605182134\n", + "1: |F(x)| = 0.006202200332\n", + "2: |F(x)| = 5.819382825e-06\n", + "3: |F(x)| = 6.964193111e-12\n", + "Converged in 4 iterations in 0.0020 seconds.\n", + "Report saved to \"ieee14_fault_out.txt\" in 0.0006 seconds.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-> Single process finished in 0.4411 seconds.\n" + ] + } + ], + "source": [ + "ss = andes.run(get_case(\"ieee14/ieee14_fault.xlsx\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Again, if you need to add devices, you should use ``ss = andes.load(..,\n", + "setup=False)`` and ``ss.setup()`` instead of ``andes.run()``.\n", + "\n", + "List the existing ``Fault`` devices:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idxunamebustftcxfrf
uid
011.0Fault_191.01.10.00010.0
\n", + "
" + ], + "text/plain": [ + " idx u name bus tf tc xf rf\n", + "uid \n", + "0 1 1.0 Fault_1 9 1.0 1.1 0.0001 0.0" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.Fault.as_df()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One fault on Bus 9 is applied at t=1.0 sec and cleared at t=1.1 sec. Suppose\n", + "that we want to clear the fault at t = 1.05 sec, we can do" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "ss.Fault.alter('tc', 1, 1.05) # arguments are `src`, `idx`, `value`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "where `tc` is the parameter to alter, `1` is the idx of the Fault to find, and\n", + "`1.05` is the new value. Inspect the Fault devices to see the updated value.\n", + "The simulation for the new system can be performed next." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idxunamebustftcxfrf
uid
011.0Fault_191.01.050.00010.0
\n", + "
" + ], + "text/plain": [ + " idx u name bus tf tc xf rf\n", + "uid \n", + "0 1 1.0 Fault_1 9 1.0 1.05 0.0001 0.0" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.Fault.as_df()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cleanup" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "ExecuteTime": { + "end_time": "2021-03-19T01:57:49.038948Z", + "start_time": "2021-03-19T01:57:48.127782Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\"/Users/jinningwang/work/andes/examples/kundur_full_out.txt\" removed.\n", + "\"/Users/jinningwang/work/andes/examples/kundur_out.txt\" removed.\n", + "\"/Users/jinningwang/work/andes/examples/kundur_out.npz\" removed.\n", + "\"/Users/jinningwang/work/andes/examples/ieee14_fault_out.txt\" removed.\n", + "\"/Users/jinningwang/work/andes/examples/kundur_out.lst\" removed.\n" + ] + } + ], + "source": [ + "!andes misc -C" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [], + "source": [ + "!rm new_system.xlsx" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + }, + "pycharm": { + "stem_cell": { + "cell_type": "raw", "metadata": { - "interpreter": { - "hash": "491bc57f30212ab10e081de3c9da13bbea8cb936a32794256be1d301e32fe2dd" - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.10" - }, - "pycharm": { - "stem_cell": { - "cell_type": "raw", - "metadata": { - "collapsed": false - }, - "source": [] - } - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - } + "collapsed": false + }, + "source": [] + } + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" }, - "nbformat": 4, - "nbformat_minor": 4 + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 } From 5e3cf164957cc64b385e5f3fbaa342f48a180db0 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Sun, 8 Jun 2025 18:51:22 -0400 Subject: [PATCH 11/11] Format references in docstring --- andes/models/area.py | 22 ++++++++++------------ andes/models/distributed/dgprct.py | 22 ++++++++++------------ andes/models/exciter/ac8b.py | 13 ++++++------- andes/models/exciter/esst1a.py | 16 ++++++---------- andes/models/exciter/ieeet3.py | 17 ++++++----------- andes/models/governor/gast.py | 11 ++++------- andes/models/governor/hygov.py | 6 +++--- andes/models/vcomp/ieeevc.py | 17 ++++++----------- 8 files changed, 51 insertions(+), 73 deletions(-) diff --git a/andes/models/area.py b/andes/models/area.py index 04d5d5c8c..5035436b7 100644 --- a/andes/models/area.py +++ b/andes/models/area.py @@ -73,12 +73,11 @@ class ACEc(ACEData, Model): Note: area idx is automatically retrieved from `bus`. - Reference: - - NERC, "Balancing and Frequency Control Reference Document, Chapter 1 Balancing Fundamentals: Bias - (B) vs. Frequency Response (Beta)", 2021. Available: - - https://www.nerc.com/comm/RSTC_Reliability_Guidelines/Reference_Document_NERC_Balancing_and_Frequency_Control.pdf + References + ----------- + 1. NERC, "Balancing and Frequency Control Reference Document, Chapter 1 Balancing Fundamentals: Bias + (B) vs. Frequency Response (Beta)", 2021. + https://www.nerc.com/comm/RSTC_Reliability_Guidelines/Reference_Document_NERC_Balancing_and_Frequency_Control.pdf """ def __init__(self, system, config): @@ -133,12 +132,11 @@ class ACE(ACEc): Note: area idx is automatically retrieved from `bus`. - Reference: - - NERC, "Balancing and Frequency Control Reference Document, Chapter 1 Balancing Fundamentals: ACE Review", - 2021. Available: - - https://www.nerc.com/comm/RSTC_Reliability_Guidelines/Reference_Document_NERC_Balancing_and_Frequency_Control.pdf + References + ----------- + 1. NERC, "Balancing and Frequency Control Reference Document, Chapter 1 Balancing Fundamentals: ACE Review", + 2021. + https://www.nerc.com/comm/RSTC_Reliability_Guidelines/Reference_Document_NERC_Balancing_and_Frequency_Control.pdf """ def __init__(self, system, config): diff --git a/andes/models/distributed/dgprct.py b/andes/models/distributed/dgprct.py index c4e8246af..a5dcf0828 100644 --- a/andes/models/distributed/dgprct.py +++ b/andes/models/distributed/dgprct.py @@ -526,12 +526,11 @@ class DGPRCT1(DGPRCTBaseData, DGPRCT1Model): `(vu1, vu2), Tvu1` [(1.10, 1.20), 1s]\n `(vu2, vu3), Tvu2` [(1.20, 2.00), 0.16s]\n - Reference: - - NERC. Bulk Power System Reliability Perspectives on the Adoption of IEEE 1547-2018. - March 2020. Available: - - https://www.nerc.com/comm/PC_Reliability_Guidelines_DL/Guideline_IEEE_1547-2018_BPS_Perspectives.pdf + References + ----------- + 1. NERC. Bulk Power System Reliability Perspectives on the Adoption of IEEE 1547-2018. + March 2020, + https://www.nerc.com/comm/PC_Reliability_Guidelines_DL/Guideline_IEEE_1547-2018_BPS_Perspectives.pdf """ def __init__(self, system, config): @@ -605,12 +604,11 @@ class DGPRCTExt(DGPRCTBaseData, DGPRCTExtModel): `(vu1, vu2), Tvu1` [(1.10, 1.20), 1s]\n `(vu2, vu3), Tvu2` [(1.20, 2.00), 0.16s]\n - Reference: - - NERC. Bulk Power System Reliability Perspectives on the Adoption of IEEE 1547-2018. - March 2020. Available: - - https://www.nerc.com/comm/PC_Reliability_Guidelines_DL/Guideline_IEEE_1547-2018_BPS_Perspectives.pdf + References + ----------- + 1. NERC. Bulk Power System Reliability Perspectives on the Adoption of IEEE 1547-2018. + March 2020. + https://www.nerc.com/comm/PC_Reliability_Guidelines_DL/Guideline_IEEE_1547-2018_BPS_Perspectives.pdf """ def __init__(self, system, config): diff --git a/andes/models/exciter/ac8b.py b/andes/models/exciter/ac8b.py index 869797d17..952635a62 100644 --- a/andes/models/exciter/ac8b.py +++ b/andes/models/exciter/ac8b.py @@ -206,13 +206,12 @@ class AC8B(AC8BData, AC8BModel): """ Exciter AC8B model. - Reference: [1]_, [2]_ - - .. [1] Powerworld, Exciter AC8B, [Online], Available: - https://www.powerworld.com/WebHelp/Content/TransientModels_HTML/Exciter%20AC8B.htm - - .. [2] NEPLAN, Exciters Models, [Online], Available: - https://www.neplan.ch/wp-content/uploads/2015/08/Nep_EXCITERS1.pdf + References + ----------- + 1. Powerworld, Exciter AC8B. + https://www.powerworld.com/WebHelp/Content/TransientModels_HTML/Exciter%20AC8B.htm + 2. NEPLAN, Exciters Models. + https://www.neplan.ch/wp-content/uploads/2015/08/Nep_EXCITERS1.pdf """ def __init__(self, system, config): AC8BData.__init__(self) diff --git a/andes/models/exciter/esst1a.py b/andes/models/exciter/esst1a.py index 06ead6485..933d54e7b 100644 --- a/andes/models/exciter/esst1a.py +++ b/andes/models/exciter/esst1a.py @@ -271,16 +271,12 @@ class ESST1A(ESST1AData, ESST1AModel): """ Exciter ESST1A model. - Reference: - - [1] PowerWorld, Exciter ESST1A, [Online], - - [2] NEPLAN, Exciters Models, [Online], - - Available: - https://www.powerworld.com/WebHelp/Content/TransientModels_HTML/Exciter%20ESST1A.htm - - https://www.neplan.ch/wp-content/uploads/2015/08/Nep_EXCITERS1.pdf + References + ----------- + 1. PowerWorld, Exciter ESST1A. + https://www.powerworld.com/WebHelp/Content/TransientModels_HTML/Exciter%20ESST1A.htm + 2. NEPLAN, Exciters Models. + https://www.neplan.ch/wp-content/uploads/2015/08/Nep_EXCITERS1.pdf """ def __init__(self, system, config): diff --git a/andes/models/exciter/ieeet3.py b/andes/models/exciter/ieeet3.py index e0a6652e6..bb4ac13e5 100644 --- a/andes/models/exciter/ieeet3.py +++ b/andes/models/exciter/ieeet3.py @@ -190,17 +190,12 @@ class IEEET3(IEEET3Data, IEEET3Model): """ Exciter IEEET3. - Reference: - - [1] PowerWorld, Exciter IEEET3, [Online], - - [2] NEPLAN, Exciters Models, [Online], - - Available: - - https://www.powerworld.com/WebHelp/Content/TransientModels_HTML/Exciter%20IEEET3.htm - - https://www.neplan.ch/wp-content/uploads/2015/08/Nep_EXCITERS1.pdf + References + ----------- + 1. PowerWorld, Exciter IEEET3. + https://www.powerworld.com/WebHelp/Content/TransientModels_HTML/Exciter%20IEEET3.htm + 2. NEPLAN, Exciters Models. + https://www.neplan.ch/wp-content/uploads/2015/08/Nep_EXCITERS1.pdf """ def __init__(self, system, config): diff --git a/andes/models/governor/gast.py b/andes/models/governor/gast.py index 76c3f1c64..5841aa45e 100644 --- a/andes/models/governor/gast.py +++ b/andes/models/governor/gast.py @@ -120,13 +120,10 @@ class GAST(GASTData, GASTModel): """ GAST turbine governor model. - Reference: - - [1] Neplan, TURBINE-GOVERNOR GAST, [Online], - - Available: - - https://www.neplan.ch/wp-content/uploads/2015/08/Nep_TURBINES_GOV.pdf + References + ----------- + 1. Neplan, TURBINE-GOVERNOR GAST. + http://www.neplan.ch/wp-content/uploads/2015/08/Nep_TURBINES_GOV.pdf """ def __init__(self, system, config): diff --git a/andes/models/governor/hygov.py b/andes/models/governor/hygov.py index 214818ad9..ec4b2a6f3 100644 --- a/andes/models/governor/hygov.py +++ b/andes/models/governor/hygov.py @@ -250,9 +250,9 @@ class HYGOV(HYGOVData, HYGOVModel): Implements the PSS/E HYGOV model without deadband. - Reference: - - [1] PSSE, Model Library, HYGOV + References + ----------- + 1. PSSE, Model Library, HYGOV. """ def __init__(self, system, config): diff --git a/andes/models/vcomp/ieeevc.py b/andes/models/vcomp/ieeevc.py index 9e3bb2eda..712aad6bc 100644 --- a/andes/models/vcomp/ieeevc.py +++ b/andes/models/vcomp/ieeevc.py @@ -101,17 +101,12 @@ class IEEEVC(IEEEVCData, IEEEVCModel): """ Voltage compensator IEEEVC model. - Reference: - - [1] PowerWorld, Voltage Compensator, IEEEVC, [Online], - - [2] NEPLAN, Exciters Models, [Online], - - Available: - - https://www.powerworld.com/WebHelp/Content/TransientModels_HTML/Voltage%20Compensator%20IEEEVC.htm?TocPath=%7C%7C%7CIEEEVC%7C_____0 - - https://www.neplan.ch/wp-content/uploads/2015/08/Nep_EXCITERS1.pdf + References + ----------- + 1. PowerWorld, Voltage Compensator, IEEEVC. + https://www.powerworld.com/WebHelp/Content/TransientModels_HTML/Voltage%20Compensator%20IEEEVC.htm?TocPath=%7C%7C%7CIEEEVC%7C_____0 + 2. NEPLAN, Exciters Models. + https://www.neplan.ch/wp-content/uploads/2015/08/Nep_EXCITERS1.pdf """ def __init__(self, system, config): IEEEVCData.__init__(self)