diff --git a/andes/cases/ieee14/ieee14_exac4.xlsx b/andes/cases/ieee14/ieee14_exac4.xlsx new file mode 100644 index 000000000..e210a2273 Binary files /dev/null and b/andes/cases/ieee14/ieee14_exac4.xlsx differ diff --git a/andes/cases/kundur/kundur_islands.xlsx b/andes/cases/kundur/kundur_islands.xlsx new file mode 100644 index 000000000..fcb37d372 Binary files /dev/null and b/andes/cases/kundur/kundur_islands.xlsx differ diff --git a/andes/system.py b/andes/system.py index ec25dcf67..d402b970f 100644 --- a/andes/system.py +++ b/andes/system.py @@ -1029,6 +1029,8 @@ def connectivity(self, info=True): self.Bus.n_islanded_buses = 0 self.Bus.islanded_buses = list() self.Bus.island_sets = list() + self.Bus.nosw_island = list() + self.Bus.msw_island = list() self.Bus.islands = list() n = self.Bus.n @@ -1101,6 +1103,21 @@ def connectivity(self, info=True): cons = temp[enum, :] + # --- check if all areas have a slack generator --- + if len(self.Bus.island_sets) > 0: + for idx, island in enumerate(self.Bus.island_sets): + nosw = 1 + slack_bus_uid = self.Bus.idx2uid(self.Slack.bus.v) + slack_u = self.Slack.u.v + for u, item in zip(slack_u, slack_bus_uid): + if (u == 1) and (item in island): + nosw -= 1 + if nosw == 1: + self.Bus.nosw_island.append(idx) + elif nosw < 0: + self.Bus.msw_island.append(idx) + + # --- Post processing --- # extend islanded buses, each in a list if len(self.Bus.islanded_buses) > 0: self.Bus.islands.extend([[item] for item in self.Bus.islanded_buses]) @@ -1118,18 +1135,38 @@ def summary(self): Print out system summary. """ + island_sets = self.Bus.island_sets + nosw_island = self.Bus.nosw_island + msw_island = self.Bus.msw_island + n_islanded_buses = self.Bus.n_islanded_buses + logger.info("-> System connectivity check results:") - if self.Bus.n_islanded_buses == 0: + if n_islanded_buses == 0: logger.info(" No islanded bus detected.") else: - logger.info(" %d islanded bus detected.", self.Bus.n_islanded_buses) - logger.debug(" Islanded buses: %s", self.Bus.islanded_buses) + logger.info(" %d islanded bus detected.", n_islanded_buses) + logger.debug(" Islanded Bus indices (0-based): %s", self.Bus.islanded_buses) - if len(self.Bus.island_sets) == 0: + if len(island_sets) == 0: logger.info(" No islanded areas detected.") else: - logger.info(" %d islanded areas detected.", len(self.Bus.island_sets)) - logger.debug(" Buses in islanded areas: %s", self.Bus.island_sets) + logger.info(" %d islanded areas detected.", len(island_sets)) + logger.debug(" Bus indices in islanded areas (0-based): %s", island_sets) + + if len(nosw_island) > 0: + logger.warning(' Slack generator is not defined/enabled for %d island(s).', + len(nosw_island)) + logger.debug(" Bus indices in no-Slack areas (0-based): %s", + [island_sets[item] for item in nosw_island]) + + if len(msw_island) > 0: + logger.warning(' Multiple slack generators are defined/enabled for %d island(s).', + len(msw_island)) + logger.debug(" Bus indices in multiple-Slack areas (0-based): %s", + [island_sets[item] for item in msw_island]) + + if len(self.Bus.nosw_island) == 0 and len(self.Bus.msw_island) == 0: + logger.info(' Each island has a slack bus correctly defined and enabled.') def _v_to_dae(self, v_code, model): """ diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 32fbb3554..da6295a11 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -9,11 +9,13 @@ The APIs before v3.0.0 are in beta and may change without prior notice. v1.3 Notes ---------- -v1.3.0 +v1.3.0 (2021-02-20) ``````````````````` - Allow `State` variable set `check_init=False` to skip initialization test. One use case is for integrators with non-zero inputs (such as state-of-charge integration). +- Solves power flow for systems with multiple areas, each with + one Slack generator. - Added `Jumper` for connecting two buses with zero impedance. - `REGCA1` and synchronous generators can take power ratio parameters `gammap` and `gammaq`.