From a9ac7bc05f1eba12a1c9735dcb3f07f47753880a Mon Sep 17 00:00:00 2001 From: jinningwang Date: Fri, 4 Oct 2024 21:29:48 -0400 Subject: [PATCH 01/27] [WIP] ConnMan --- andes/core/connman.py | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 andes/core/connman.py diff --git a/andes/core/connman.py b/andes/core/connman.py new file mode 100644 index 000000000..063b4ba15 --- /dev/null +++ b/andes/core/connman.py @@ -0,0 +1,44 @@ +""" +Module for Connectivity Manager. +""" + +import logging + +logger = logging.getLogger(__name__) + + +class ConnMan: + """ + Define a Connectivity Manager class for System + """ + + def __init__(self, system=None): + """ + Initialize the connectivity manager. + + system: system object + """ + self.system = system + + def _act(self): + """ + Update the connectivity. + """ + if True: + self._disconnect() + else: + self._connect() + self.system.connectivity(info=True) + return None + + def _disconnect(self): + """ + Disconnect involved devices. + """ + pass + + def _connect(self): + """ + Connect involved devices. + """ + pass From 508fc8ee477f05847abcd18a16b0a1f7840d84ac Mon Sep 17 00:00:00 2001 From: jinningwang Date: Sat, 5 Oct 2024 12:39:30 -0400 Subject: [PATCH 02/27] [WIP] ConnMan --- andes/core/connman.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/andes/core/connman.py b/andes/core/connman.py index 063b4ba15..56c91b69d 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -19,11 +19,16 @@ def __init__(self, system=None): system: system object """ self.system = system + self.busu0 = system.Bus.u.v.copy() # a copy of Bus.u.v for internal use - def _act(self): + def act(self): """ Update the connectivity. """ + if not self.is_act: + logger.debug('Connectivity is not need to be updated.') + return None + if True: self._disconnect() else: From d4b81a14f65b4b15c1d019615321dc38bd1147fd Mon Sep 17 00:00:00 2001 From: jinningwang Date: Sat, 5 Oct 2024 14:09:15 -0400 Subject: [PATCH 03/27] Add a case for ConnMan testing purpose --- andes/cases/ieee14/ieee14_conn.xlsx | Bin 0 -> 25531 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 andes/cases/ieee14/ieee14_conn.xlsx diff --git a/andes/cases/ieee14/ieee14_conn.xlsx b/andes/cases/ieee14/ieee14_conn.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a7d073a21692059945434ad1c2b808e08c610e99 GIT binary patch literal 25531 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 ae-?C@CK9C>z literal 0 HcmV?d00001 From 8b4added3ab23425dee9ecd4ea081f4a05b5869a Mon Sep 17 00:00:00 2001 From: jinningwang Date: Sat, 5 Oct 2024 14:09:30 -0400 Subject: [PATCH 04/27] [WIP] ConnMan --- andes/core/__init__.py | 1 + andes/core/connman.py | 36 +++++++++++++++++++----------------- andes/system.py | 6 +++++- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/andes/core/__init__.py b/andes/core/__init__.py index e4155d027..45c67c388 100644 --- a/andes/core/__init__.py +++ b/andes/core/__init__.py @@ -17,3 +17,4 @@ from andes.core.var import (Algeb, BaseVar, ExtAlgeb, ExtState, ExtVar, # NOQA State,) from andes.core.symprocessor import SymProcessor # NOQA +from andes.core.connman import ConnMan # NOQA diff --git a/andes/core/connman.py b/andes/core/connman.py index 56c91b69d..e06d898a4 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -19,7 +19,22 @@ def __init__(self, system=None): system: system object """ self.system = system - self.busu0 = system.Bus.u.v.copy() # a copy of Bus.u.v for internal use + self.busu0 = None # placeholder for Bus.u.v + self.is_act = False # flag for act, True to act + + def init(self): + """ + Initialize the connectivity. + """ + self.busu0 = self.system.Bus.u.v.copy() + return None + + def record(self): + """ + Record the bus connectivity in-place. + """ + self.busu0[...] = self.system.Bus.u + return None def act(self): """ @@ -29,21 +44,8 @@ def act(self): logger.debug('Connectivity is not need to be updated.') return None - if True: - self._disconnect() - else: - self._connect() + # --- action --- + pass + self.system.connectivity(info=True) return None - - def _disconnect(self): - """ - Disconnect involved devices. - """ - pass - - def _connect(self): - """ - Connect involved devices. - """ - pass diff --git a/andes/system.py b/andes/system.py index 2497fc1a6..00682baac 100644 --- a/andes/system.py +++ b/andes/system.py @@ -23,7 +23,7 @@ from typing import Dict, Optional, Tuple, Union import andes.io -from andes.core import AntiWindup, Config, Model +from andes.core import AntiWindup, Config, Model, ConnMan from andes.io.streaming import Streaming from andes.models import file_classes from andes.models.group import GroupBase @@ -193,6 +193,7 @@ def __init__(self, self.files = FileMan(case=case, **self.options) # file path manager self.dae = DAE(system=self) # numerical DAE storage self.streaming = Streaming(self) # Dime2 streaming + self.conn = ConnMan(system=self) # connectivity manager # dynamic imports of groups, models and routines self.import_groups() @@ -488,6 +489,9 @@ def setup(self): self.store_sparse_pattern(self.exist.pflow) self.store_adder_setter(self.exist.pflow) + # init connectivity manager + self.conn.init() + if ret is True: self.is_setup = True # set `is_setup` if no error occurred else: From 7e330c27b9f0abd45c9e4dc12108c41a76959fd0 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Fri, 11 Oct 2024 16:07:56 -0400 Subject: [PATCH 05/27] Add a wrapper to check_conn before a routine init --- andes/routines/base.py | 11 +++++++++++ andes/routines/pflow.py | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/andes/routines/base.py b/andes/routines/base.py index d27f0d942..c716417e9 100644 --- a/andes/routines/base.py +++ b/andes/routines/base.py @@ -7,6 +7,17 @@ from collections import OrderedDict +def check_conn(func): + """ + Wrapper function to check connectivity. + """ + + def wrapper(self, *args, **kwargs): + self.system.conn.act() + return func(self, *args, **kwargs) + return wrapper + + class BaseRoutine: """ Base routine class. diff --git a/andes/routines/pflow.py b/andes/routines/pflow.py index 54d01c974..c85f7f23d 100644 --- a/andes/routines/pflow.py +++ b/andes/routines/pflow.py @@ -6,7 +6,7 @@ from collections import OrderedDict from andes.utils.misc import elapsed -from andes.routines.base import BaseRoutine +from andes.routines.base import BaseRoutine, check_conn from andes.variables.report import Report from andes.shared import np, matrix, sparse, newton_krylov @@ -63,6 +63,7 @@ def __init__(self, system=None, config=None): self.x_sol = None self.y_sol = None + @check_conn def init(self): """ Initialize variables for power flow. From fccff4319c6bdfc7424835d19e8cb44bed402977 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Fri, 11 Oct 2024 16:08:28 -0400 Subject: [PATCH 06/27] Add a ieee14 case for ConnMan debugging --- andes/cases/ieee14/ieee14_conn.xlsx | Bin 25531 -> 25516 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/andes/cases/ieee14/ieee14_conn.xlsx b/andes/cases/ieee14/ieee14_conn.xlsx index a7d073a21692059945434ad1c2b808e08c610e99..ecc2b9b4eef94741b30735f597f64147dd84c319 100644 GIT binary patch delta 3374 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 delta 3387 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 TBTrqrA|TpRhVd)rPr!cwg~BKp From 413ead0f3e60a570f1ffbd0de50304f890488efc Mon Sep 17 00:00:00 2001 From: jinningwang Date: Fri, 11 Oct 2024 16:08:48 -0400 Subject: [PATCH 07/27] Draft an implementation of ConnMan --- andes/core/connman.py | 100 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/andes/core/connman.py b/andes/core/connman.py index e06d898a4..2cf2b70e3 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -3,49 +3,133 @@ """ import logging +from collections import OrderedDict + +from andes.utils.func import list_flatten +from andes.shared import np logger = logging.getLogger(__name__) +# connectivity dependencies for each model +# TODO: this is not an exhaustive list +# TODO: DC Topologies are not included yet +bus_deps = OrderedDict([ + ('ACLine', ['bus1', 'bus2']), + ('ACShort', ['bus1', 'bus2']), + ('FreqMeasurement', ['bus']), + ('Interface', ['bus']), + ('Motor', ['bus']), + ('PhasorMeasurement', ['bus']), + ('StaticACDC', ['bus']), + ('StaticGen', ['bus']), + ('StaticLoad', ['bus']), + ('StaticShunt', ['bus']), +]) + + class ConnMan: """ - Define a Connectivity Manager class for System + Define a Connectivity Manager class for System. + + Connectivity Manager is used to automatically **turn off** + attached devices when a `Bus` is turned off. + + Attributes + ---------- + system: system object + System object to manage the connectivity. + busu0: ndarray + Last recorded bus connection status. + is_changed: bool + Flag to indicate if bus connectivity is changed. + changes: dict + Dictionary to record bus connectivity changes ('on' and 'off'). """ def __init__(self, system=None): """ Initialize the connectivity manager. + Parameters + ---------- system: system object + System object to manage the connectivity. """ self.system = system self.busu0 = None # placeholder for Bus.u.v - self.is_act = False # flag for act, True to act + self.is_changed = False # flag to indicate if bus connectivity is changed + self.changes = {'on': None, 'off': None} # dict of bus connectivity changes def init(self): """ Initialize the connectivity. + + `ConnMan` is initialized in `System.setup()`, where all buses are considered online + by default. This method records the initial bus connectivity. """ - self.busu0 = self.system.Bus.u.v.copy() - return None + self.busu0 = np.ones(self.system.Bus.n, dtype=bool) + # NOTE: 'on' means the bus is previous offline and now online + # 'off' means the bus is previous online and now offline + # The bool value for each bus indicates if the bus is 'on' or 'off' + self.changes['on'] = np.zeros(self.system.Bus.n, dtype=bool) + self.changes['off'] = np.logical_and(self.busu0 == 1, self.system.Bus.u.v == 0) + + if np.any(self.changes['off']): + self.is_changed = True + return self.changes def record(self): """ Record the bus connectivity in-place. + + This method should be called if `Bus.set()` or `Bus.alter()` is called. """ - self.busu0[...] = self.system.Bus.u - return None + self.changes['on'][...] = np.logical_and(self.busu0 == 0, self.system.Bus.u.v == 1) + self.changes['off'][...] = np.logical_and(self.busu0 == 1, self.system.Bus.u.v == 0) + + if np.any(self.changes['on']): + onbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["on"])[0]] + logger.warning(f'Bus turned on: {onbus_idx}') + + if np.any(self.changes['off']): + offbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["off"])[0]] + logger.warning(f'Bus turned off: {offbus_idx}') + + # update busu0 + self.busu0[...] = self.system.Bus.u.v + return self.changes def act(self): """ Update the connectivity. """ - if not self.is_act: + if not self.is_changed: logger.debug('Connectivity is not need to be updated.') return None # --- action --- - pass + logger.warning('Entering connectivity update.') + + offbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["off"])[0]] + for grp_name, src_list in bus_deps.items(): + devices = [] + for src in src_list: + grp_devs = self.system.__dict__[grp_name].find_idx(keys=src, values=offbus_idx, + allow_none=True, allow_all=True, + default=None) + grp_devs_flat = list_flatten(grp_devs) + if grp_devs_flat != [None]: + devices.append(grp_devs_flat) + + devices_flat = list_flatten(devices) + + if len(devices_flat) > 0: + self.system.__dict__[grp_name].set(src='u', attr='v', + idx=devices_flat, value=0) + logger.warning(f'In <{grp_name}>, turn off {devices_flat}') + + self.is_changed = False # reset the action flag self.system.connectivity(info=True) return None From c3475d81f32a4ade5bdd9dc853d10171053b13fc Mon Sep 17 00:00:00 2001 From: jinningwang Date: Fri, 11 Oct 2024 16:46:16 -0400 Subject: [PATCH 08/27] Rename wrapper check_conn as check_conn_before_init --- andes/routines/base.py | 18 ++++++++++++++++-- andes/routines/eig.py | 3 ++- andes/routines/pflow.py | 4 ++-- andes/routines/tds.py | 3 ++- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/andes/routines/base.py b/andes/routines/base.py index c716417e9..e3e637186 100644 --- a/andes/routines/base.py +++ b/andes/routines/base.py @@ -7,9 +7,23 @@ from collections import OrderedDict -def check_conn(func): +def check_conn_before_init(func): """ - Wrapper function to check connectivity. + A decorator that ensures the connection is active before calling the `init` or `run` + method of a `BaseRoutine` derived class. + + This decorator calls the `act` method on `self.system.conn` to ensure the connection + is active before proceeding with the initialization. + + Parameters + ---------- + func : function + The `init` method of a `BaseRoutine` derived class. + + Returns + ------- + function + The wrapped function with connection check. """ def wrapper(self, *args, **kwargs): diff --git a/andes/routines/eig.py b/andes/routines/eig.py index 94363688d..9f0b04787 100644 --- a/andes/routines/eig.py +++ b/andes/routines/eig.py @@ -12,7 +12,7 @@ from andes.io.txt import dump_data from andes.plot import set_latex, set_style -from andes.routines.base import BaseRoutine +from andes.routines.base import BaseRoutine, check_conn_before_init from andes.shared import div, matrix, plt, sparse, spdiag, spmatrix from andes.utils.misc import elapsed from andes.variables.report import report_info @@ -492,6 +492,7 @@ def _pre_check(self): return status + @check_conn_before_init def run(self, **kwargs): """ Run small-signal stability analysis. diff --git a/andes/routines/pflow.py b/andes/routines/pflow.py index c85f7f23d..452da9133 100644 --- a/andes/routines/pflow.py +++ b/andes/routines/pflow.py @@ -6,7 +6,7 @@ from collections import OrderedDict from andes.utils.misc import elapsed -from andes.routines.base import BaseRoutine, check_conn +from andes.routines.base import BaseRoutine, check_conn_before_init from andes.variables.report import Report from andes.shared import np, matrix, sparse, newton_krylov @@ -63,7 +63,7 @@ def __init__(self, system=None, config=None): self.x_sol = None self.y_sol = None - @check_conn + @check_conn_before_init def init(self): """ Initialize variables for power flow. diff --git a/andes/routines/tds.py b/andes/routines/tds.py index d55da6f8c..06523c8a7 100644 --- a/andes/routines/tds.py +++ b/andes/routines/tds.py @@ -9,7 +9,7 @@ import time from collections import OrderedDict -from andes.routines.base import BaseRoutine +from andes.routines.base import BaseRoutine, check_conn_before_init from andes.routines.daeint import Trapezoid, method_map from andes.routines.criteria import deltadelta from andes.shared import matrix, np, pd, spdiag, tqdm, tqdm_nb @@ -174,6 +174,7 @@ def __init__(self, system=None, config=None): self.method = Trapezoid() self.set_method(self.config.method) + @check_conn_before_init def init(self): """ Initialize the status, storage and values for TDS. From 9c8793928e14ab6ae469e1600b8ee66d6a2bff6e Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 09:57:38 -0400 Subject: [PATCH 09/27] Update connman logging messages --- andes/core/connman.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/andes/core/connman.py b/andes/core/connman.py index 2cf2b70e3..8d7cbc339 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -110,6 +110,7 @@ def act(self): # --- action --- logger.warning('Entering connectivity update.') + logger.warning('-> System connectivity update results:') offbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["off"])[0]] for grp_name, src_list in bus_deps.items(): @@ -126,7 +127,7 @@ def act(self): if len(devices_flat) > 0: self.system.__dict__[grp_name].set(src='u', attr='v', - idx=devices_flat, value=0) + idx=devices_flat, value=0) logger.warning(f'In <{grp_name}>, turn off {devices_flat}') self.is_changed = False # reset the action flag From 64bbfc16c2aee7d4e332326f5f856e89c1cf4b4a Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 10:02:56 -0400 Subject: [PATCH 10/27] Update develoepr notes --- andes/core/connman.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/andes/core/connman.py b/andes/core/connman.py index 8d7cbc339..cfc76fc45 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -69,9 +69,9 @@ def init(self): by default. This method records the initial bus connectivity. """ self.busu0 = np.ones(self.system.Bus.n, dtype=bool) - # NOTE: 'on' means the bus is previous offline and now online + # NOTE: 'on' means th or 'off'e bus is previous offline and now online # 'off' means the bus is previous online and now offline - # The bool value for each bus indicates if the bus is 'on' or 'off' + # The bool value for each bus indicates if the bus is 'on' self.changes['on'] = np.zeros(self.system.Bus.n, dtype=bool) self.changes['off'] = np.logical_and(self.busu0 == 1, self.system.Bus.u.v == 0) From 972a7489123c4c5ae8066b75aa1807d266664530 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 10:03:37 -0400 Subject: [PATCH 11/27] Act connectivity manager in system.setup --- andes/system.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/andes/system.py b/andes/system.py index 00682baac..b85c8a345 100644 --- a/andes/system.py +++ b/andes/system.py @@ -491,6 +491,8 @@ def setup(self): # init connectivity manager self.conn.init() + # act on connectivity changes + self.conn.act() if ret is True: self.is_setup = True # set `is_setup` if no error occurred From 6889afeaac0b4a44d3eb167889a4b33e2704b6ad Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 11:02:36 -0400 Subject: [PATCH 12/27] Update ConnMan logging messgaes --- andes/core/connman.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/andes/core/connman.py b/andes/core/connman.py index cfc76fc45..e95eb3f5f 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -110,9 +110,10 @@ def act(self): # --- action --- logger.warning('Entering connectivity update.') - logger.warning('-> System connectivity update results:') - offbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["off"])[0]] + logger.warning(f'Following bus(es) are turned off: {offbus_idx}') + + logger.warning('-> System connectivity update results:') for grp_name, src_list in bus_deps.items(): devices = [] for src in src_list: From 7b5c46aa3cde578c904b5d500618c6e5db03979f Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 11:34:40 -0400 Subject: [PATCH 13/27] Rename ConnMan internal flag is_changed to is_off for clarification --- andes/core/connman.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/andes/core/connman.py b/andes/core/connman.py index e95eb3f5f..ef1ef104d 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -58,7 +58,7 @@ def __init__(self, system=None): """ self.system = system self.busu0 = None # placeholder for Bus.u.v - self.is_changed = False # flag to indicate if bus connectivity is changed + self.is_off = False # flag to indicate if any bus is turned off self.changes = {'on': None, 'off': None} # dict of bus connectivity changes def init(self): @@ -68,15 +68,15 @@ def init(self): `ConnMan` is initialized in `System.setup()`, where all buses are considered online by default. This method records the initial bus connectivity. """ - self.busu0 = np.ones(self.system.Bus.n, dtype=bool) + self.busu0 = np.ones(self.system.Bus.n, dtype=int) # NOTE: 'on' means th or 'off'e bus is previous offline and now online # 'off' means the bus is previous online and now offline # The bool value for each bus indicates if the bus is 'on' - self.changes['on'] = np.zeros(self.system.Bus.n, dtype=bool) - self.changes['off'] = np.logical_and(self.busu0 == 1, self.system.Bus.u.v == 0) + self.changes['on'] = np.zeros(self.system.Bus.n, dtype=int) + self.changes['off'] = np.logical_and(self.busu0 == 1, self.system.Bus.u.v == 0).astype(int) if np.any(self.changes['off']): - self.is_changed = True + self.is_off = True return self.changes def record(self): @@ -91,10 +91,12 @@ def record(self): if np.any(self.changes['on']): onbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["on"])[0]] logger.warning(f'Bus turned on: {onbus_idx}') + logger.warning('Note that turning on bus(es) does not trigger connectivity update.') if np.any(self.changes['off']): offbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["off"])[0]] logger.warning(f'Bus turned off: {offbus_idx}') + self.is_off = True # update busu0 self.busu0[...] = self.system.Bus.u.v @@ -104,7 +106,7 @@ def act(self): """ Update the connectivity. """ - if not self.is_changed: + if not self.is_off: logger.debug('Connectivity is not need to be updated.') return None @@ -131,7 +133,7 @@ def act(self): idx=devices_flat, value=0) logger.warning(f'In <{grp_name}>, turn off {devices_flat}') - self.is_changed = False # reset the action flag + self.is_off = False # reset the action flag self.system.connectivity(info=True) return None From a9abaab8c09a877144fb69c9f17098a2169c8513 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 11:47:34 -0400 Subject: [PATCH 14/27] Rename ConnMan internal flag is_off to is_needed for clarification --- andes/core/connman.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/andes/core/connman.py b/andes/core/connman.py index ef1ef104d..c55e16190 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -57,8 +57,8 @@ def __init__(self, system=None): System object to manage the connectivity. """ self.system = system - self.busu0 = None # placeholder for Bus.u.v - self.is_off = False # flag to indicate if any bus is turned off + self.busu0 = None # placeholder for Bus.u.v + self.is_needed = False # flag to indicate if check is needed self.changes = {'on': None, 'off': None} # dict of bus connectivity changes def init(self): @@ -76,7 +76,7 @@ def init(self): self.changes['off'] = np.logical_and(self.busu0 == 1, self.system.Bus.u.v == 0).astype(int) if np.any(self.changes['off']): - self.is_off = True + self.is_needed = True return self.changes def record(self): @@ -96,7 +96,7 @@ def record(self): if np.any(self.changes['off']): offbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["off"])[0]] logger.warning(f'Bus turned off: {offbus_idx}') - self.is_off = True + self.is_needed = True # update busu0 self.busu0[...] = self.system.Bus.u.v @@ -106,13 +106,18 @@ def act(self): """ Update the connectivity. """ - if not self.is_off: - logger.debug('Connectivity is not need to be updated.') - return None + if not self.is_needed: + logger.debug('No need to update connectivity.') + return True # --- action --- - logger.warning('Entering connectivity update.') offbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["off"])[0]] + + # skip if no bus is turned off + if len(offbus_idx) == 0: + return True + + logger.warning('Entering connectivity update.') logger.warning(f'Following bus(es) are turned off: {offbus_idx}') logger.warning('-> System connectivity update results:') @@ -133,7 +138,6 @@ def act(self): idx=devices_flat, value=0) logger.warning(f'In <{grp_name}>, turn off {devices_flat}') - self.is_off = False # reset the action flag - + self.is_needed = False # reset the action flag self.system.connectivity(info=True) - return None + return True From a671c62e5f123e9cd368c7ddf5458a81c3ef33bf Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 13:13:57 -0400 Subject: [PATCH 15/27] In ConnMan, act in init --- andes/core/connman.py | 5 ++++- andes/system.py | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/andes/core/connman.py b/andes/core/connman.py index c55e16190..c3344314b 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -77,7 +77,10 @@ def init(self): if np.any(self.changes['off']): self.is_needed = True - return self.changes + + self.act() + + return True def record(self): """ diff --git a/andes/system.py b/andes/system.py index b85c8a345..00682baac 100644 --- a/andes/system.py +++ b/andes/system.py @@ -491,8 +491,6 @@ def setup(self): # init connectivity manager self.conn.init() - # act on connectivity changes - self.conn.act() if ret is True: self.is_setup = True # set `is_setup` if no error occurred From 321f6b3bdeed751463fb08aa4f2a94d606d3a00f Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 13:14:29 -0400 Subject: [PATCH 16/27] Overwrite Bus methods set and alter for ConnMan logging --- andes/models/bus.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/andes/models/bus.py b/andes/models/bus.py index 643d172f2..ae7633e84 100644 --- a/andes/models/bus.py +++ b/andes/models/bus.py @@ -124,3 +124,25 @@ def __init__(self, system=None, config=None): '(1-flat_start)*a0' self.v.v_str = 'flat_start*1 + ' \ '(1-flat_start)*v0' + + def set(self, **kwargs): + super().set(**kwargs) + _conn_status(self, **kwargs) + + def alter(self, **kwargs): + super().alter(**kwargs) + _conn_status(self, **kwargs) + +def _conn_status(self, **kwargs): + """ + Helper function to determine if connectivity update is needed. + """ + src = kwargs.get('src', None) + attr = kwargs.get('attr', None) + if src == 'u' and attr == 'v': + if self.system.is_setup: + self.system.conn.record() + if not self.system.TDS.initialized: + if self.system.PFlow.converged: + logger.warning('Bus connectivity is updated, resolve PFlow before running EIG or TDS!') + self.system.PFlow.converged = False From 9f2f00587c8c7c3cfcadf5ffc3a0579107276e20 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 13:14:45 -0400 Subject: [PATCH 17/27] Add a demo notebook for ConnMan --- examples/demonstration/ConnMan.ipynb | 745 +++++++++++++++++++++++++++ 1 file changed, 745 insertions(+) create mode 100644 examples/demonstration/ConnMan.ipynb diff --git a/examples/demonstration/ConnMan.ipynb b/examples/demonstration/ConnMan.ipynb new file mode 100644 index 000000000..20ce7c628 --- /dev/null +++ b/examples/demonstration/ConnMan.ipynb @@ -0,0 +1,745 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Connectivity Manager" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In version 1.9.3, `ConnMan` is introduced to manage the bus related connectivity changes.\n", + "\n", + "It is an attribute `conn` of the `System` instance." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import andes" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "andes.config_logger(stream_level=20)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Scenario 1\n", + "\n", + "For buses that are turned off by case file, the connectivity is updated in the `System.setup()`.\n", + "\n", + "We can see that the connected devices to off-line buses are turned off." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Working directory: \"/Users/jinningwang/work/andes/examples/demonstration\"\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.1041 seconds.\n", + "Parsing input file \"/Users/jinningwang/work/andes/andes/cases/ieee14/ieee14_conn.xlsx\"...\n", + "Input file parsed in 0.1834 seconds.\n", + "Entering connectivity update.\n", + "Following bus(es) are turned off: [15]\n", + "-> System connectivity update results:\n", + "In , turn off ['Line_21']\n", + "In , turn off [6]\n", + "In , turn off ['PQ_12']\n", + "In , turn off ['Shunt_3']\n", + "-> System connectivity check results:\n", + " 1 islanded bus detected.\n", + " System is interconnected.\n", + " Each island has a slack bus correctly defined and enabled.\n", + "System internal structure set up in 0.0192 seconds.\n" + ] + } + ], + "source": [ + "ss = andes.load(andes.get_case('ieee14/ieee14_conn.xlsx'),\n", + " setup=True, no_output=True, default_config=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "-> System connectivity check results:\n", + " 1 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.0031 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.0029 seconds.\n" + ] + } + ], + "source": [ + "_ = ss.PFlow.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As expected, there is no power injection from the turned off devices.\n", + "\n", + "Note: `Line.a1.e` is the active power injection at the from_bus (`bus1`)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.Line.get(src='a1', attr='e', idx=['Line_21'])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.01])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# TODO: debug: the active output is not zero, which is not expected\n", + "ss.StaticGen.get(src='p', attr='v', idx=[6])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similarly, `PQ.a.e` is the active power injection at the connected bus." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.])" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.PQ.get(src='a', attr='e', idx=['PQ_12'])" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Initialization for dynamics completed in 0.0178 seconds.\n", + "Initialization was successful.\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8fb32cdaa5774e71851a0ab490bdbd7c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/100 [00:00 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.2173 seconds.\n", + "Parsing input file \"/Users/jinningwang/work/andes/andes/cases/ieee14/ieee14_conn.xlsx\"...\n", + "Input file parsed in 0.0528 seconds.\n" + ] + } + ], + "source": [ + "ss = andes.load(andes.get_case('ieee14/ieee14_conn.xlsx'),\n", + " setup=False, no_output=True, default_config=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Turn on the bus manually." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "ss.Bus.set(src='u', attr='v', idx=15, value=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "System internal structure set up in 0.0170 seconds.\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.setup()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "-> 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.0028 seconds.\n", + "0: |F(x)| = 0.7800160444\n", + "1: |F(x)| = 0.03157368515\n", + "2: |F(x)| = 4.343734372e-05\n", + "3: |F(x)| = 8.270892304e-11\n", + "Converged in 4 iterations in 0.0027 seconds.\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.PFlow.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we change the bus parameters manually" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "ss.Bus.set(src='name', attr='v', idx=15, value='BUSTEST')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This will not set the connectivity action flag." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.conn.is_needed" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In contrast, if changes are made to src `u` and its attribute `v`, the connectivity action flag is set." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Bus turned off: [15]\n", + "Bus connectivity is updated, resolve PFlow before running EIG or TDS!\n" + ] + } + ], + "source": [ + "ss.Bus.set(src='u', attr='v', idx=15, value=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.conn.is_needed" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Remember to run the `PFlow` again if updates are made to the bus online status." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "-> 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", + "Entering connectivity update.\n", + "Following bus(es) are turned off: [15]\n", + "-> System connectivity update results:\n", + "In , turn off ['Line_21']\n", + "In , turn off [6]\n", + "In , turn off ['PQ_12']\n", + "In , turn off ['Shunt_3']\n", + "-> System connectivity check results:\n", + " 1 islanded bus detected.\n", + " System is interconnected.\n", + " Each island has a slack bus correctly defined and enabled.\n", + "Power flow initialized in 0.0032 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.0026 seconds.\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.PFlow.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Initialization for dynamics completed in 0.0363 seconds.\n", + "Initialization was successful.\n", + "\n", + "-> Eigenvalue Analysis:\n", + "4 states are associated with zero time constants. \n", + " Positive 0\n", + " Zeros 1\n", + " Negative 62\n", + "Eigenvalue analysis finished in 0.0016 seconds.\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ss.EIG.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "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" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "aa6f49d483b047e9a1a648aa8413a6c3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/100 [00:00 **NOTE:** Turn on a bus will not automatically turn on the connected devices." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Bus turned on: [15]\n", + "Note that turning on bus(es) does not trigger connectivity update.\n" + ] + } + ], + "source": [ + "ss.Bus.set(src='u', attr='v', idx=15, value=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6e21b8c941fe484ea49e166f4fddaae3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/100 [00:00 Date: Mon, 28 Oct 2024 13:14:53 -0400 Subject: [PATCH 18/27] Update release notes --- docs/source/release-notes.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 75602f755..86045aa95 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -11,6 +11,15 @@ v1.9 Notes v1.9.3 (2024-04-XX) ------------------- +Development of connectivity manager `ConnMan`: + +- Add case `ieee14_conn.xlsx` for demonstration. +- Add `ConnMan` class to manage connectivity. +- Add `ConnMan` to `System` to as an attribute `conn`. +- Add a demo notebook for `ConnMan`. + +Other changes: + - In the ``dae`` module, change `self.t.itemset` to array assignment to ensure compatibility with NumPy 2.0. - Follow RTD's deprecation of Sphinx context injection at build time - In symbolic processor, most variables are assumed to be real, except some From ebe331e8a0a6ba36db4b3404493e8cda989c1114 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 13:16:34 -0400 Subject: [PATCH 19/27] Format --- andes/models/bus.py | 1 + 1 file changed, 1 insertion(+) diff --git a/andes/models/bus.py b/andes/models/bus.py index ae7633e84..32023c6d8 100644 --- a/andes/models/bus.py +++ b/andes/models/bus.py @@ -133,6 +133,7 @@ def alter(self, **kwargs): super().alter(**kwargs) _conn_status(self, **kwargs) + def _conn_status(self, **kwargs): """ Helper function to determine if connectivity update is needed. From 04d689997ef3777661dc8b734dfaed0c39bcce57 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 13:26:51 -0400 Subject: [PATCH 20/27] Typo --- andes/core/connman.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/andes/core/connman.py b/andes/core/connman.py index c3344314b..5afa8688c 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -41,8 +41,8 @@ class ConnMan: System object to manage the connectivity. busu0: ndarray Last recorded bus connection status. - is_changed: bool - Flag to indicate if bus connectivity is changed. + is_needed: bool + Flag to indicate if connectivity update is needed. changes: dict Dictionary to record bus connectivity changes ('on' and 'off'). """ From 282d3a36c13dc1e45da4686ccf680036fff25936 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 14:38:09 -0400 Subject: [PATCH 21/27] Refactor Bus.set to fix parameters passing error --- andes/models/bus.py | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/andes/models/bus.py b/andes/models/bus.py index 32023c6d8..af6fde985 100644 --- a/andes/models/bus.py +++ b/andes/models/bus.py @@ -125,25 +125,31 @@ def __init__(self, system=None, config=None): self.v.v_str = 'flat_start*1 + ' \ '(1-flat_start)*v0' - def set(self, **kwargs): - super().set(**kwargs) - _conn_status(self, **kwargs) + def set(self, src, idx, attr, value): + super().set(src=src, idx=idx, attr=attr, value=value) + _conn_status(system=self.system, src=src, attr=attr) - def alter(self, **kwargs): - super().alter(**kwargs) - _conn_status(self, **kwargs) - -def _conn_status(self, **kwargs): +def _conn_status(system, src, attr): """ Helper function to determine if connectivity update is needed. + + Parameters + ---------- + system : System + The system object. + src : str + Name of the model property + attr : str + The internal attribute of the property to get. """ - src = kwargs.get('src', None) - attr = kwargs.get('attr', None) + # Check if connectivity update is required if src == 'u' and attr == 'v': - if self.system.is_setup: - self.system.conn.record() - if not self.system.TDS.initialized: - if self.system.PFlow.converged: - logger.warning('Bus connectivity is updated, resolve PFlow before running EIG or TDS!') - self.system.PFlow.converged = False + if system.is_setup: + system.conn.record() # Record connectivity once setup is confirmed + + if not system.TDS.initialized: + # Log a warning if Power Flow needs resolution before EIG or TDS + if system.PFlow.converged: + logger.warning('Bus connectivity is touched, resolve PFlow before running EIG or TDS!') + system.PFlow.converged = False # Flag Power Flow as not converged From 887fff36c42924021f218eaeedbb60c0ba0d7513 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 14:43:43 -0400 Subject: [PATCH 22/27] Update develoepr notes in ConnMan --- andes/core/connman.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/andes/core/connman.py b/andes/core/connman.py index 5afa8688c..714a18857 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -11,9 +11,11 @@ logger = logging.getLogger(__name__) -# connectivity dependencies for each model -# TODO: this is not an exhaustive list -# TODO: DC Topologies are not included yet +# connectivity dependencies of `Bus` +# NOTE: only include PFlow models and measurements models +# cause online status of dynamic models are expected to be handled by their +# corresponding static models +# TODO: DC Topologies are not included yet, `Node`, etc bus_deps = OrderedDict([ ('ACLine', ['bus1', 'bus2']), ('ACShort', ['bus1', 'bus2']), From 712bbc862d277bf2d5b243c8b0fa94ea43965e08 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Mon, 28 Oct 2024 14:43:53 -0400 Subject: [PATCH 23/27] Add tests for ConnMan --- tests/test_conn.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/test_conn.py diff --git a/tests/test_conn.py b/tests/test_conn.py new file mode 100644 index 000000000..71297641d --- /dev/null +++ b/tests/test_conn.py @@ -0,0 +1,75 @@ +import unittest + +import numpy as np + +import andes + + +class TestConnMan(unittest.TestCase): + """ + Test class `ConnMan`. + """ + def setUp(self) -> None: + self.ss = andes.load(andes.get_case("ieee14/ieee14_conn.xlsx"), + setup=True, default_config=True, no_output=True, + ) + + def test_conn_init(self): + """ + Test `ConnMan` initialization. + """ + # normally, flag `is_needed` should be False after successful init + self.assertFalse(self.ss.conn.is_needed) + + self.assertIsInstance(self.ss.conn.busu0, np.ndarray) + self.assertEqual(self.ss.conn.busu0.shape, (self.ss.Bus.n,)) + + self.assertIsInstance(self.ss.conn.changes, dict) + self.assertEqual(self.ss.conn.changes['on'].shape, (self.ss.Bus.n,)) + self.assertEqual(self.ss.conn.changes['off'].shape, (self.ss.Bus.n,)) + + def test_turn_off(self): + """ + Test if connected devices are turned off. + """ + # assert there is an offline bus + self.assertEqual(self.ss.Bus.get(src='u', attr='v', idx=15), 0) + + # assert connected devices are turned off + self.assertEqual(self.ss.Line.get(src='u', attr='v', idx='Line_21'), 0) + self.assertEqual(self.ss.StaticGen.get(src='u', attr='v', idx=6), 0) + self.assertEqual(self.ss.StaticLoad.get(src='u', attr='v', idx='PQ_12'), 0) + self.assertEqual(self.ss.StaticShunt.get(src='u', attr='v', idx='Shunt_3'), 0) + + def test_turn_on(self): + """ + Test if connected devices are not turned on. + """ + # turn on the bus + self.ss.Bus.set(src='u', attr='v', idx=15, value=1) + + # assert flag `is_needed` is False, since only diff in `changes['off']` will trigger action + self.assertFalse(self.ss.conn.is_needed) + + # assert connected devices are not turned on, since we don't turn on devices automatically + self.assertEqual(self.ss.Line.get(src='u', attr='v', idx='Line_21'), 0) + self.assertEqual(self.ss.StaticGen.get(src='u', attr='v', idx=6), 0) + self.assertEqual(self.ss.StaticLoad.get(src='u', attr='v', idx='PQ_12'), 0) + self.assertEqual(self.ss.StaticShunt.get(src='u', attr='v', idx='Shunt_3'), 0) + + def test_turn_off_after_setup(self): + """ + Test if `ConnMan` works after solving PFlow. + """ + ss = andes.load(andes.get_case('ieee14/ieee14_conn.xlsx'), + setup=False, no_output=True, default_config=True) + ss.Bus.set(src='u', attr='v', idx=15, value=1) + ss.setup() + + ss.PFlow.run() + self.assertTrue(ss.PFlow.converged) + + # turn off a bus + ss.Bus.alter(src='u', idx=15, value=0) + # flag PFlow.converged should be reset as False by `Bus.set()` + self.assertFalse(ss.PFlow.converged) From ff49d15303f2824663b3e890a58efa45bcdcff1c Mon Sep 17 00:00:00 2001 From: jinningwang Date: Sat, 23 Nov 2024 11:10:49 -0500 Subject: [PATCH 24/27] Rename helper func _conn_status to _check_conn_status --- andes/models/bus.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/andes/models/bus.py b/andes/models/bus.py index af6fde985..063c299dd 100644 --- a/andes/models/bus.py +++ b/andes/models/bus.py @@ -127,10 +127,10 @@ def __init__(self, system=None, config=None): def set(self, src, idx, attr, value): super().set(src=src, idx=idx, attr=attr, value=value) - _conn_status(system=self.system, src=src, attr=attr) + _check_conn_status(system=self.system, src=src, attr=attr) -def _conn_status(system, src, attr): +def _check_conn_status(system, src, attr): """ Helper function to determine if connectivity update is needed. From 396cc35cc902386bff7c8d00ebc4f33727a1bf14 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Sat, 23 Nov 2024 12:10:03 -0500 Subject: [PATCH 25/27] Clarify scheme in docstring of class connman --- andes/core/connman.py | 32 ++++++++++++++++++++++---------- tests/test_conn.py | 30 +++++++++++------------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/andes/core/connman.py b/andes/core/connman.py index 714a18857..d688b2d4d 100644 --- a/andes/core/connman.py +++ b/andes/core/connman.py @@ -35,7 +35,8 @@ class ConnMan: Define a Connectivity Manager class for System. Connectivity Manager is used to automatically **turn off** - attached devices when a `Bus` is turned off. + attached devices when a ``Bus`` is turned off **after** system + setup and **before** TDS initializtion. Attributes ---------- @@ -47,6 +48,8 @@ class ConnMan: Flag to indicate if connectivity update is needed. changes: dict Dictionary to record bus connectivity changes ('on' and 'off'). + 'on' means the bus is previous offline and now online. + 'off' means the bus is previous online and now offline. """ def __init__(self, system=None): @@ -70,10 +73,8 @@ def init(self): `ConnMan` is initialized in `System.setup()`, where all buses are considered online by default. This method records the initial bus connectivity. """ + # NOTE: here, we expect all buses are online before the system setup self.busu0 = np.ones(self.system.Bus.n, dtype=int) - # NOTE: 'on' means th or 'off'e bus is previous offline and now online - # 'off' means the bus is previous online and now offline - # The bool value for each bus indicates if the bus is 'on' self.changes['on'] = np.zeros(self.system.Bus.n, dtype=int) self.changes['off'] = np.logical_and(self.busu0 == 1, self.system.Bus.u.v == 0).astype(int) @@ -84,27 +85,34 @@ def init(self): return True + def _update(self): + """ + Helper function for in-place update of bus connectivity. + """ + self.changes['on'][...] = np.logical_and(self.busu0 == 0, self.system.Bus.u.v == 1) + self.changes['off'][...] = np.logical_and(self.busu0 == 1, self.system.Bus.u.v == 0) + self.busu0[...] = self.system.Bus.u.v + def record(self): """ Record the bus connectivity in-place. This method should be called if `Bus.set()` or `Bus.alter()` is called. """ - self.changes['on'][...] = np.logical_and(self.busu0 == 0, self.system.Bus.u.v == 1) - self.changes['off'][...] = np.logical_and(self.busu0 == 1, self.system.Bus.u.v == 0) + self._update() if np.any(self.changes['on']): onbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["on"])[0]] logger.warning(f'Bus turned on: {onbus_idx}') - logger.warning('Note that turning on bus(es) does not trigger connectivity update.') + self.is_needed = True + if len(onbus_idx) > 0: + raise NotImplementedError('Turning on bus after system setup is not supported yet!') if np.any(self.changes['off']): offbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["off"])[0]] logger.warning(f'Bus turned off: {offbus_idx}') self.is_needed = True - # update busu0 - self.busu0[...] = self.system.Bus.u.v return self.changes def act(self): @@ -115,6 +123,9 @@ def act(self): logger.debug('No need to update connectivity.') return True + if self.system.TDS.initialized: + raise NotImplementedError('Bus connectivity update during TDS is not supported yet!') + # --- action --- offbus_idx = [self.system.Bus.idx.v[i] for i in np.nonzero(self.changes["off"])[0]] @@ -143,6 +154,7 @@ def act(self): idx=devices_flat, value=0) logger.warning(f'In <{grp_name}>, turn off {devices_flat}') - self.is_needed = False # reset the action flag + self.is_needed = False # reset the action flag + self._update() # update but not record self.system.connectivity(info=True) return True diff --git a/tests/test_conn.py b/tests/test_conn.py index 71297641d..1e5db8686 100644 --- a/tests/test_conn.py +++ b/tests/test_conn.py @@ -9,10 +9,10 @@ class TestConnMan(unittest.TestCase): """ Test class `ConnMan`. """ + def setUp(self) -> None: self.ss = andes.load(andes.get_case("ieee14/ieee14_conn.xlsx"), - setup=True, default_config=True, no_output=True, - ) + setup=True, default_config=True, no_output=True) def test_conn_init(self): """ @@ -41,23 +41,7 @@ def test_turn_off(self): self.assertEqual(self.ss.StaticLoad.get(src='u', attr='v', idx='PQ_12'), 0) self.assertEqual(self.ss.StaticShunt.get(src='u', attr='v', idx='Shunt_3'), 0) - def test_turn_on(self): - """ - Test if connected devices are not turned on. - """ - # turn on the bus - self.ss.Bus.set(src='u', attr='v', idx=15, value=1) - - # assert flag `is_needed` is False, since only diff in `changes['off']` will trigger action - self.assertFalse(self.ss.conn.is_needed) - - # assert connected devices are not turned on, since we don't turn on devices automatically - self.assertEqual(self.ss.Line.get(src='u', attr='v', idx='Line_21'), 0) - self.assertEqual(self.ss.StaticGen.get(src='u', attr='v', idx=6), 0) - self.assertEqual(self.ss.StaticLoad.get(src='u', attr='v', idx='PQ_12'), 0) - self.assertEqual(self.ss.StaticShunt.get(src='u', attr='v', idx='Shunt_3'), 0) - - def test_turn_off_after_setup(self): + def test_turn_off_after_pflow(self): """ Test if `ConnMan` works after solving PFlow. """ @@ -73,3 +57,11 @@ def test_turn_off_after_setup(self): ss.Bus.alter(src='u', idx=15, value=0) # flag PFlow.converged should be reset as False by `Bus.set()` self.assertFalse(ss.PFlow.converged) + self.assertTrue(ss.conn.is_needed) + + def test_turn_on_after_setup(self): + """ + Test if raise NotImplementedError when turning on a bus after system setup. + """ + with self.assertRaises(NotImplementedError): + self.ss.Bus.set(src='u', attr='v', idx=15, value=1) From e522a8f94ea6b5c308b07db747d011a79b3a68f5 Mon Sep 17 00:00:00 2001 From: jinningwang Date: Sat, 23 Nov 2024 12:10:12 -0500 Subject: [PATCH 26/27] Rerun demo of ConnMan --- examples/demonstration/ConnMan.ipynb | 156 +++++++++++---------------- 1 file changed, 63 insertions(+), 93 deletions(-) diff --git a/examples/demonstration/ConnMan.ipynb b/examples/demonstration/ConnMan.ipynb index 20ce7c628..7d59bf3cd 100644 --- a/examples/demonstration/ConnMan.ipynb +++ b/examples/demonstration/ConnMan.ipynb @@ -73,9 +73,9 @@ "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.1041 seconds.\n", + "Generated numerical code for 1 models in 0.1019 seconds.\n", "Parsing input file \"/Users/jinningwang/work/andes/andes/cases/ieee14/ieee14_conn.xlsx\"...\n", - "Input file parsed in 0.1834 seconds.\n", + "Input file parsed in 0.2000 seconds.\n", "Entering connectivity update.\n", "Following bus(es) are turned off: [15]\n", "-> System connectivity update results:\n", @@ -87,7 +87,7 @@ " 1 islanded bus detected.\n", " System is interconnected.\n", " Each island has a slack bus correctly defined and enabled.\n", - "System internal structure set up in 0.0192 seconds.\n" + "System internal structure set up in 0.0207 seconds.\n" ] } ], @@ -114,12 +114,12 @@ " Numba: Off\n", " Sparse solver: KLU\n", " Solution method: NR method\n", - "Power flow initialized in 0.0031 seconds.\n", + "Power flow initialized in 0.0109 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.0029 seconds.\n" + "Converged in 4 iterations in 0.0106 seconds.\n" ] } ], @@ -156,6 +156,13 @@ "ss.Line.get(src='a1', attr='e', idx=['Line_21'])" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`PV.a.e` is the active power injection at the connected bus." + ] + }, { "cell_type": "code", "execution_count": 6, @@ -164,7 +171,7 @@ { "data": { "text/plain": [ - "array([0.01])" + "array([-0.])" ] }, "execution_count": 6, @@ -173,8 +180,7 @@ } ], "source": [ - "# TODO: debug: the active output is not zero, which is not expected\n", - "ss.StaticGen.get(src='p', attr='v', idx=[6])" + "ss.PV.get(src='a', attr='e', idx=[6])" ] }, { @@ -213,14 +219,14 @@ "name": "stderr", "output_type": "stream", "text": [ - "Initialization for dynamics completed in 0.0178 seconds.\n", + "Initialization for dynamics completed in 0.0181 seconds.\n", "Initialization was successful.\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "8fb32cdaa5774e71851a0ab490bdbd7c", + "model_id": "d2b6e221b5034f9a914bd9fd1bbc5c70", "version_major": 2, "version_minor": 0 }, @@ -235,7 +241,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Simulation to t=20.00 sec completed in 0.2637 seconds.\n" + "Simulation to t=20.00 sec completed in 0.2641 seconds.\n" ] }, { @@ -286,7 +292,7 @@ "source": [ "## Scenario 2\n", "\n", - "After running power flow, bus connection status are changed." + "Between successful PFlow solve and TDS initialization, turn off bus(es)." ] }, { @@ -324,9 +330,9 @@ "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.2173 seconds.\n", + "Generated numerical code for 1 models in 0.0971 seconds.\n", "Parsing input file \"/Users/jinningwang/work/andes/andes/cases/ieee14/ieee14_conn.xlsx\"...\n", - "Input file parsed in 0.0528 seconds.\n" + "Input file parsed in 0.0676 seconds.\n" ] } ], @@ -339,7 +345,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Turn on the bus manually." + "First, let's turn on the bus manually.\n", + "\n", + "> Note: it will ***not*** touch the connectivity manager to update bus online status before system setup." ] }, { @@ -360,7 +368,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "System internal structure set up in 0.0170 seconds.\n" + "System internal structure set up in 0.0202 seconds.\n" ] }, { @@ -396,12 +404,12 @@ " Numba: Off\n", " Sparse solver: KLU\n", " Solution method: NR method\n", - "Power flow initialized in 0.0028 seconds.\n", + "Power flow initialized in 0.0179 seconds.\n", "0: |F(x)| = 0.7800160444\n", "1: |F(x)| = 0.03157368515\n", "2: |F(x)| = 4.343734372e-05\n", "3: |F(x)| = 8.270892304e-11\n", - "Converged in 4 iterations in 0.0027 seconds.\n" + "Converged in 4 iterations in 0.0045 seconds.\n" ] }, { @@ -423,7 +431,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, we change the bus parameters manually" + "Next, let's make a change on the parameter `Bus.name`." ] }, { @@ -439,7 +447,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This will not set the connectivity action flag." + "Such a change will not set the connectivity action flag." ] }, { @@ -466,7 +474,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In contrast, if changes are made to src `u` and its attribute `v`, the connectivity action flag is set." + "In contrast, if changes are made to `Bus.u.v`, the connectivity action flag is set." ] }, { @@ -479,12 +487,31 @@ "output_type": "stream", "text": [ "Bus turned off: [15]\n", - "Bus connectivity is updated, resolve PFlow before running EIG or TDS!\n" + "Bus connectivity is touched, resolve PFlow before running EIG or TDS!\n" ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "ss.Bus.set(src='u', attr='v', idx=15, value=0)" + "ss.Bus.set(src='u', attr='v', idx=15, value=0)\n", + "ss.conn.is_needed" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Remember to run the `PFlow` again if updates are made to the bus online status,\n", + "since this will reset the `PFlow` convergence flag." ] }, { @@ -495,7 +522,7 @@ { "data": { "text/plain": [ - "True" + "False" ] }, "execution_count": 17, @@ -504,14 +531,7 @@ } ], "source": [ - "ss.conn.is_needed" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Remember to run the `PFlow` again if updates are made to the bus online status." + "ss.PFlow.converged" ] }, { @@ -543,12 +563,12 @@ " 1 islanded bus detected.\n", " System is interconnected.\n", " Each island has a slack bus correctly defined and enabled.\n", - "Power flow initialized in 0.0032 seconds.\n", + "Power flow initialized in 0.0051 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.0026 seconds.\n" + "Converged in 4 iterations in 0.0096 seconds.\n" ] }, { @@ -575,7 +595,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Initialization for dynamics completed in 0.0363 seconds.\n", + "Initialization for dynamics completed in 0.0208 seconds.\n", "Initialization was successful.\n", "\n", "-> Eigenvalue Analysis:\n", @@ -583,7 +603,7 @@ " Positive 0\n", " Zeros 1\n", " Negative 62\n", - "Eigenvalue analysis finished in 0.0016 seconds.\n" + "Eigenvalue analysis finished in 0.0017 seconds.\n" ] }, { @@ -620,7 +640,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "aa6f49d483b047e9a1a648aa8413a6c3", + "model_id": "196c92bd10734959b35aa9a27743f2d0", "version_major": 2, "version_minor": 0 }, @@ -635,7 +655,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Simulation to t=20.00 sec completed in 0.2496 seconds.\n" + "Simulation to t=20.00 sec completed in 0.2661 seconds.\n" ] }, { @@ -661,63 +681,13 @@ ] }, { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Bus turned on: [15]\n", - "Note that turning on bus(es) does not trigger connectivity update.\n" - ] - } - ], - "source": [ - "ss.Bus.set(src='u', attr='v', idx=15, value=1)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "6e21b8c941fe484ea49e166f4fddaae3", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - " 0%| | 0/100 [00:00 Date: Sat, 23 Nov 2024 12:15:13 -0500 Subject: [PATCH 27/27] Specify multiprocess version lower than 0.70.17 as it does not support Linux --- docs/source/release-notes.rst | 1 + requirements.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 86045aa95..64d2992a9 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -31,6 +31,7 @@ Other changes: - Add parameter `allow_all=False` to `ModelData.find_idx()` `GroupBase.find_idx()` to allow searching all matches. - Add method `GroupBase.get_all_idxes()` to get all indices of a group. - Enhanced three-winding transformer parsing in PSS/E raw files by assigning the equivalent star bus `area`, `owner`, and `zone` using the high-voltage bus values. +- Specify `multiprocess <=0.70.16` in requirements as 0.70.17 does not support Linux. v1.9.2 (2024-03-25) ------------------- diff --git a/requirements.txt b/requirements.txt index b7f2ef4c5..e2474fd24 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,3 +15,4 @@ chardet psutil texttable numba +multiprocess <=0.70.16