diff --git a/.gitignore b/.gitignore index 4c4e2877..b2e60a78 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ org.eclipse.jdt.groovy.core.prefs # AMPL computation result files **/AMPL_runner_*.err **/AMPL_runner_*.out + +# Generated readthedocs pages +build-docs/ diff --git a/docs/.readthedocs.yaml b/docs/.readthedocs.yaml new file mode 100644 index 00000000..934aaace --- /dev/null +++ b/docs/.readthedocs.yaml @@ -0,0 +1,13 @@ +version: 2 + +build: + os: ubuntu-20.04 + tools: + python: "3.9" + +sphinx: + configuration: docs/conf.py + +python: + install: + - requirements: docs/requirements.txt \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..4d15385e --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,27 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + + +clean: + @echo "Removing $(SOURCEDIR)/reference/api" + @rm -rf "$(SOURCEDIR)/reference/api" + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..baa539a9 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,63 @@ +These are the documentation sources for PowSyBl optimizer. + +Please keep them up to date with your developments. +They are published on powsybl.readthedocs.io/ and pull requests are built and previewed automatically. + +When modifying the website content, you can easily preview the result on your PC. + +**First option - in a terminal, navigate to the root of the project and run the following commands:** + +~~~ +pip install -r docs/requirements.txt +sphinx-build -a docs ./build-docs +~~~ + +**Second option - run the following commands directly from your IDE GUI** + +~~~bash +pip install -r requirements.txt +~~~ + +~~~bash +sphinx-build -a . ../build-docs +~~~ + +**Preview the result** + +Then open `build-docs/index.html` in your browser. + +If you want to add links to another documentation, add the corresponding repository to the `conf.py` file. +To automatically get the version specified in the `pom.xml`, please use the same naming as the version: if you define the +Groovy version with ``, then use `groovy` as a key. The specified URL should start with `https://` and end with `latest/` (the final `/` is mandatory). +For example, to add a link to the documentation of Sphinx, you need to add the following lines: +~~~python +# This parameter might already be present, just add the new value +intersphinx_mapping = { + "sphinx": ("https://www.sphinx-doc.org/en/master/", None), +} +~~~ + +Then in your documentation file, you can add links to powsybl-optimizer documentation. If you want to link to a whole page, +use one of the following examples: +~~~Markdown +- {doc}`sphinx:usage/extensions/intersphinx` +- {doc}`Intersphinx ` +- [Intersphinx](inv:sphinx:std:doc#usage/extensions/intersphinx). +~~~ + +If you want to link a specific part of a page, use one of those examples: +~~~Markdown +- [Intersphinx roles](inv:#ref-role). +- [Intersphinx roles](inv:sphinx#ref-role). +- [Intersphinx roles](inv:sphinx:std:label:#ref-role). +- [Intersphinx roles](inv:sphinx:*:*:#ref-role). +~~~ +*Note: for the last examples to work, there need to be a corresponding reference in the external documentation. +For those examples, `(ref-role)=` has been added right before the corresponding title +in the [Cross-referencing syntax page](inv:sphinx:std:doc#usage/referencing). Another way to make it work is to use the `autosectionlabel` module in Sphinx to +automatically generate anchors for each title.* + +*NoteĀ²: if the build fails, try with the `-E` option to clear the cache:* +~~~bash +sphinx-build -a -E docs ./build-docs +~~~ diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 00000000..2ea6b5c3 Binary files /dev/null and b/docs/_static/favicon.ico differ diff --git a/docs/_static/logos/logo_lfe_powsybl.svg b/docs/_static/logos/logo_lfe_powsybl.svg new file mode 100644 index 00000000..37450078 --- /dev/null +++ b/docs/_static/logos/logo_lfe_powsybl.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/styles/styles.css b/docs/_static/styles/styles.css new file mode 100644 index 00000000..f3340e11 --- /dev/null +++ b/docs/_static/styles/styles.css @@ -0,0 +1,6 @@ +/* The following code ensures font is not too big (Furo theme can make font too big on large screens) */ +@media(min-width:97em) { + html { + font-size: 100% + } +} \ No newline at end of file diff --git a/docs/_templates/page.html b/docs/_templates/page.html new file mode 100644 index 00000000..4652e049 --- /dev/null +++ b/docs/_templates/page.html @@ -0,0 +1,101 @@ + + +{% extends "furo/page.html" %} + +{% block footer %} + +
+
+ {%- if show_copyright %} + + {%- endif %} + {% trans %}Made with {% endtrans -%} + {%- if show_sphinx -%} + {% trans %}Sphinx and {% endtrans -%} + @pradyunsg's + {% endif -%} + {% trans %} + Furo + {% endtrans %} + {%- if last_updated -%} +
+ {% trans last_updated=last_updated|e -%} + Last updated on {{ last_updated }} + {%- endtrans -%} +
+ {%- endif %} +
+
+ {% if theme_footer_icons or READTHEDOCS -%} +
+ {% if theme_footer_icons -%} + {% for icon_dict in theme_footer_icons -%} + + {{- icon_dict.html -}} + + {% endfor %} + {%- endif %} +
+ {%- endif %} +
+
+{% endblock footer %} diff --git a/docs/_templates/sidebar/brand.html b/docs/_templates/sidebar/brand.html new file mode 100644 index 00000000..8aef92a7 --- /dev/null +++ b/docs/_templates/sidebar/brand.html @@ -0,0 +1,25 @@ +{#- +Overrides furo's brand.html to customize links: +- The logo links to a custom page (set sidebar_logo_href option in html_context) +- The title links to the subproject's main page +-#} + +{% if not theme_sidebar_hide_name %} + +{%- endif %} \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..5fbe73ae --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,168 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import datetime +import os +import re +import sys + +# Path to python sources, for doc generation on readthedocs +source_path = os.path.abspath('..') +sys.path.insert(0, source_path) +print(f'appended {source_path}') + + +# -- Project information ----------------------------------------------------- + +# Only those 4 parameters have to be modified for each specific repository +project = 'powsybl-optimizer' +module_name = "powsybl-optimizer" +github_repository = "https://github.com/powsybl/powsybl-optimizer/" + +# Build year for the copyright +copyright_year = f'2018-{ datetime.datetime.now().year }' + +# Find the version and release information. +# We have a single source of truth for our version number: the project's pom.xml file. +# This next bit of code reads from it. +file_with_version = os.path.join(source_path, "pom.xml") +with open(file_with_version) as f: + next_line_contains_version = False + for line in f: + if next_line_contains_version == False: + m = re.match(r'^ {4}\' + module_name + r'\<\/artifactId\>', line) + if m: + next_line_contains_version = True + else: + m = re.match(r'^ {4}\(.*)\<\/version\>', line) + if m: + __version__ = m.group(1) + # The short X.Y version. + version = ".".join(__version__.split(".")[:2]) + # The full version, including alpha/beta/rc tags. + release = __version__ + break + else: # AKA no-break + version = release = "dev" + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['sphinx.ext.autodoc', + 'sphinx.ext.autosectionlabel', + 'sphinx.ext.autosummary', + 'sphinx.ext.viewcode', + 'sphinx.ext.doctest', + 'sphinx.ext.napoleon', + 'sphinx.ext.todo', + 'sphinx.ext.intersphinx', + 'sphinx_tabs.tabs', + 'myst_parser', + # Extension used to add a "copy" button on code blocks + 'sphinx_copybutton'] +myst_enable_extensions = [ + "amsmath", + "colon_fence", + "dollarmath", + "attrs_inline" +] +myst_heading_anchors = 6 + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# Reference sections generation +autosectionlabel_prefix_document = True +autosectionlabel_maxdepth = 2 + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "furo" + +html_title = f"{project} v{release}" + +html_logo = '_static/logos/logo_lfe_powsybl.svg' +html_favicon = "_static/favicon.ico" + +html_context = { + "copyright_year": copyright_year, + "sidebar_logo_href": "https://powsybl.readthedocs.io/", + "github_repository": github_repository +} + +html_theme_options = { + # the following 3 lines enable edit button + "source_repository": github_repository, + "source_branch": "main", + "source_directory": "docs/", +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] +html_css_files = ['styles/styles.css'] + +todo_include_todos = True + +# Links to external documentations +intersphinx_mapping = { + "powsyblcore": ("https://powsybl.readthedocs.io/projects/powsybl-core/en/latest/", None), +} +intersphinx_disabled_reftypes = ["*"] + +# Generate one file per method +autosummary_generate = True + + +# -- Dependencies versions --------------------------------------------------- +# This part will automatically look in the pom.xml to find versions corresponding to the dependencies whose +# documentation is used in the present one, except if it's a SNAPSHOT version or if a specific version has been chosen +# in intersphinx_mapping + +# Get the URL without the default version +def extract_base_url(url): + default_version = "latest" + + m = re.match(r'(^https\:\/\/.*)' + default_version + r'\/$', url) + if m: + return m.group(1) + +# Replace the default version in the URL with the version from the pom.xml +def replace_versions(intersphinx_mapping, file): + with open(file) as f: + for line in f: + m = re.match(r'^ {8}\<(.*)\.version\>(.*)\<\/(.*)\.version\>', line) + if m and m.group(1) == m.group(3): + dependency = m.group(1) + version = m.group(2) + if "SNAPSHOT" not in version and dependency in intersphinx_mapping: + url_start = extract_base_url(intersphinx_mapping[dependency][0]) + if url_start: + intersphinx_mapping[dependency] = (url_start + version + "/", None) + if "" in line: + break + return intersphinx_mapping + +intersphinx_mapping = replace_versions(intersphinx_mapping, file_with_version) diff --git a/docs/gettingStarted.md b/docs/gettingStarted.md new file mode 100644 index 00000000..21862fe1 --- /dev/null +++ b/docs/gettingStarted.md @@ -0,0 +1,23 @@ +# Getting started + +## AMPL +For this project, you must have [AMPL](https://ampl.com/) installed on your machine. +AMPL is a proprietary tool that works as an optimization modelling language, +and it can be interfaced with many solvers. + +To run OpenReac, you must add in your `~/.itools/config.yml` an ampl section like this: +```yaml +ampl: + # Change to the ampl folder path that contains the ampl executable + homeDir: /home/user/ampl +``` + +## Non-linear optimization solver + +To run the model implemented in AMPL, you'll need a non-linear optimization solver. +By default, the AMPL code is configured to run Knitro, which is a proprietary non-linear solver, but you +are free to configure a different one. + +If you chose to run Knitro, you must have `knitroampl` in your path, after the installation +of the solver is done and that you got a valid licence. + diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..19e0c043 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,19 @@ +![PowSyBl Logo](_static/logos/logo_lfe_powsybl.svg) +# PowSyBl optimizer + +PowSyBl (Power System Blocks) is an open source library written in Java, +dedicated to electrical grid modeling, simulation and visualisation, licensed under the [Mozilla Public License version 2.0](https://www.mozilla.org/en-US/MPL/2.0/). +It is part of [LF Energy](https://www.lfenergy.org/), an open source foundation focused on the energy sector, hosted within The Linux Foundation. + +OpenReac is a reactive optimal power flow that gives a set of hypotheses for voltage and reactive controls by network equipment such as generators, shunt compensators and transformers. OpenReac can be used for network planning or in operation as well. + +```{toctree} +--- +maxdepth: 2 +hidden: true +gettingStarted.md +optimizer/index +--- + +``` + diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..96ed7097 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd \ No newline at end of file diff --git a/docs/optimizer/_static/img/reactive-diagram.png b/docs/optimizer/_static/img/reactive-diagram.png new file mode 100644 index 00000000..5d3fe2a3 Binary files /dev/null and b/docs/optimizer/_static/img/reactive-diagram.png differ diff --git a/docs/optimizer/acOptimalPowerflow.md b/docs/optimizer/acOptimalPowerflow.md new file mode 100644 index 00000000..13de4692 --- /dev/null +++ b/docs/optimizer/acOptimalPowerflow.md @@ -0,0 +1,88 @@ +# Alternative current optimal power flow + +## Generalities + +The goal of the reactive ACOPF is to compute voltage values on each bus, as well as control values for reactive equipment and controllers of the grid. +Then, the following values will be variable in the optimization: +- $\boldsymbol{V_i}$ and $\boldsymbol{\theta_i}$ the voltage magnitude and phase of bus $i$. +- $\boldsymbol{P_{i,g}}$ (resp. $\boldsymbol{Q_{i,g}}$) the active (resp. reactive) power produced by variable generator $g$ of bus $i$. +- $\boldsymbol{Q_{i,vsc}}$ the reactive power produced by voltage source converter stations $vsc$ of bus $i$. +- $\boldsymbol{b_{i,g}}$ (resp. $\boldsymbol{b_{i,svc}}$) the susceptance of shunt $s$ (resp. of static var compensator $svc$) of bus $i$. +- $\boldsymbol{\rho_{ij}}$ the transformer ratio of the ratio tap changer on branch $ij$, +specified as variable by the user (see [3.2](#32-configuration-of-the-run)). + +Please note that: +- Units with active power specified in `ampl_network_generators.txt` that is less than the configurable parameter `Pnull` **are excluded from the optimization**, + even if the user designates these generators as fixed in the parameter file `param_generators_reactive.txt` (see [3.2](#32-configuration-of-the-run)). + Therefore, when the optimization results are exported, **these generators are exported with a reactive power target of $0$**. +- **Neither current limits nor power limits** on branches are considered in the optimization. + +## Constraints + +The constraints of the optimization problem depend on parameters specified by the user (see [3.2](#32-configuration-of-the-run)). +In particular, the user can indicate which buses will have associated **reactive slacks** $\boldsymbol{\sigma_{i}^{Q,+}}$ and $\boldsymbol{\sigma_{i}^{Q,-}}$ +, expressing the excess (resp. shortfall) of reactive power produced in bus $i$, and used to ensure reactive power balance. +To do so, these buses must be specified in parameter file `param_buses_with_reactive_slack.txt`, and `buses_with_reactive_slacks` must be set to $\text{CONFIGURED}$. + +The ACOPF involves the following constraints, in addition to the slack constraint $(1)$ introduced in [5](#5-slack-bus--main-connex-component): + +$$\sum\limits_{j\in v(i)} \boldsymbol{p_{ij}} = P_i^{in} - \sum\limits_{g}\boldsymbol{P_{i,g}}, \quad i\in\text{BUSCC} \quad (5)$$ + +$$\sum\limits_{j\in v(i)} \boldsymbol{q_{ij}} = Q_i^{in} - \boldsymbol{\sigma_{i}^{Q,+}} + \boldsymbol{\sigma_{Q_i}^{-}} - \sum\limits_{g}\boldsymbol{Q_{i,g}} - \sum\limits_{vsc}\boldsymbol{Q_{i,vsc}} - \sum\limits_{s}\boldsymbol{b_{i,s}}{V_i}^2 - \sum\limits_{svc}\boldsymbol{b_{i,svc}}{V_i}^2, \quad i\in\text{BUSCC} \quad (6)$$ + +where: +- $\boldsymbol{p_{ij}}$ (resp. $\boldsymbol{q_{ij}}$) is the active (resp. reactive) power leaving bus $i$ on branch $ij$, + calculated as defined in the [PowSyBl documentation](https://powsybl.readthedocs.io/projects/powsybl-open-loadflow). + Those are variables because they depend on $\boldsymbol{V_i}$, $\boldsymbol{V_j}$, $\boldsymbol{\theta_i}$, $\boldsymbol{\theta_j}$ and $\boldsymbol{\rho_{ij}}$. +- $P_i^{in}$ is the constant active power injected or consumed in bus $i$ by batteries, loads, VSC stations and LCC stations. +- $Q_i^{in}$ is the constant reactive power injected or consumed in bus $i$, by fixed generators and fixed shunts (see [3.2](#32-configuration-of-the-run)), batteries, loads and LCC stations. + +In order to bound the variables described in [7.1](#71-generalities), the limits specified in the files of network data (see [3.1](#31-network-data)) +are used. We specify the following special treatments: +- The voltage magnitude $\boldsymbol{V_i}$ lies between the corrected voltage limits described in [4.1](#41-voltage-level-limits-consistency). +- The reactive power $\boldsymbol{Q_{i,g}}$ produced by unit $g$ lies between the corrected limits described in [4.4](#44-pq-units-domain). +- The active power $\boldsymbol{P_{i,g}}$ also lies between the corrected limits described in [4.4](#44-pq-units-domain), +but these bounds are only considered when the configurable parameter $\alpha$ is different than $1$ (default value). +Otherwise, all active powers evolve proportionally to their initial point $P_{i,g}^t$ (specified in `ampl_network_generators.txt`): +$\boldsymbol{P_{i,g}} = P_{i,g}^t + \boldsymbol{\gamma} (P_{g}^{max,c} - P_{i,g}^t)$, where $\boldsymbol{\gamma}$ is optimized and lies in $\[-1;1\]$. +- The reactive power $\boldsymbol{Q_{i,vsc}}$ produced by voltage source converter station $vsc$ is included in $\[\min(qP_{vsc}, qp_{vsc}, qp_{vsc}^0)$; $\max(QP_{vsc}, Qp_{vsc}, Qp_{vsc}^0)\]$. +**The bounds are therefore rectangular, not trapezoidal.** + +## Objective function + +The objective function also depends on parameters specified by the user (see [3.2](#32-configuration-of-the-run)). +The `objective_choice` parameter modifies the values of penalties $\beta_1$, $\beta_2$, and $\beta_3$ in the objective function: +if `objective_choice` $= i$, then $\beta_i = 1$ and $\beta_j = 0.01$ for $j \neq i$. + +Specifically, if `objective_choice` takes on: +- $0$, the minimization of active power production $\sum\limits_{i,g}\boldsymbol{P_{i,g}}$ is prioritized. +- $1$, the minimization of $\sum\limits_{i} \boldsymbol{V_i}-(\rho V_i^{c,max} - (1-\rho)V_i^{c,min})^2$ is prioritized ($\rho$ +equals the configurable parameter `ratio_voltage_target`). +- $2$, the minimization of $\sum\limits_{i} (\boldsymbol{V_i} - V_i^t)^2$ is prioritized. + +The objective function of the ACOPF is: +$$\text{minimize} \left( 10\sum\limits_{i} (\boldsymbol{\sigma_{i}^{Q,+}} + \boldsymbol{\sigma_{i}^{Q,-}}) + \beta_1 \sum\limits_{g} \left( \alpha\boldsymbol{P_{i,g}} + (1-\alpha)(\frac{\boldsymbol{P_{i,g}} - P_{i,g}^t}{\max(1, |P_{i,g}^t|)})^2 \right) + \beta_2 \sum\limits_{i} \left( \boldsymbol{V_i} - (1-\rho)V_{i}^{min,c} + \rho V_{i}^{max,c} \right)^2 + \beta_3 \sum\limits_{i} (\boldsymbol{V_i} - V_i^t)^2 + 0.1 \sum\limits_{g} \left(\frac{\boldsymbol{Q_{i,g}}}{\max(1,Q_{g}^{min,c}, Q_{g}^{max,c})}\right)^2 + 0.1 \sum\limits_{ij} (\boldsymbol{\rho_{ij}} - \rho_{ij})^2 \right)$$ + +where: +- $P_{i,g}^t$ (resp. $V_i^t$) is the active target (resp. voltage initial point) specified in `ampl_network_generators.txt` (resp. `ampl_network_buses.txt`). +- $\rho_{ij}$ is the transformer ratio of line $ij$, specified in `ampl_network_tct.txt`. + +The sum of the reactive slack variables is penalized by a +high coefficient ($10$) to drive it towards $0$, ensuring reactive power balance at each bus of the network. + +## Solving + +Before solving the ACOPF, the voltage magnitudes $\boldsymbol{V_i}$ are warm-started with $V_i^t$ +(specified in `ampl_network_buses.txt`), as well as the voltage phases $\boldsymbol{\theta_i}$ with the results of the DCOPF (see [6](#6-direct-current-optimal-power-flow)). +Please also note that a scaling is applied with user-defined values before solving the ACOPF. + +The solving is considered as successful if the non-linear solver employed (see [Non-linear solver](#non-linear-optimization-solver)) +finds a feasible approximate solution (**even if the sum of +slacks is important**), and the script `reactiveopfoutput.run` is executed (see [8.1](#81-in-case-of-convergence)). + +Note that if the solving of ACOPF fails, and the $\alpha$ parameter is set to $1$ (default value), +then a new resolution is attempted, with $\alpha$ set to zero. This gives more freedom to the active powers +produced (see [7.2](#72-constraints)), leaving these variables free withing their respective bounds. + +If ACOPF solving fails another time, the script `reactiveopfexit.run` is executed (see [8.2](#82-in-case-of-inconsistency)). + diff --git a/docs/optimizer/dcOptimalPowerflow.md b/docs/optimizer/dcOptimalPowerflow.md new file mode 100644 index 00000000..510d3051 --- /dev/null +++ b/docs/optimizer/dcOptimalPowerflow.md @@ -0,0 +1,29 @@ +# Direct current optimal power flow + +## Generalities + +Before addressing the ACOPF (see [AC optimal powerflow](acOptimalPowerflow.md)), a DCOPF is solved for two main reasons: +- If the DCOPF resolution fails, it provides a strong indication that the ACOPF resolution will also fail. + Thus, it serves as a formal consistency check on the data. +- The phases computed by DCOPF resolution will be used as initial points for the solving of the ACOPF. + +## Optimization problem + +The DCOPF model involves the following constraints, in addition to the slack constraint $(1)$ introduced in [Slack bus and main connex component](slackBusMainConnexComponent.md): + +$$\sum\limits_{j\in v(i)} \boldsymbol{p_{ij}} = P_i^{in} + \boldsymbol{\sigma_{P,i}^{+}} - \boldsymbol{\sigma_{P,i}^{-}} - \sum\limits_{g}\boldsymbol{P_{i,g}}, \quad i\in\text{BUSCC} \quad (4)$$ + +where: +- $\boldsymbol{p_{ij}}$ is the active power leaving bus $i$ on branch $ij$, defined as $\boldsymbol{p_{ij}} = \frac{\boldsymbol{\theta_i} - \boldsymbol{\theta_j}}{X_{ij}}$, where $X_{ij}$ is the reactance of line $ij$ (specified in `ampl_network_branches.txt`). +- $P_i^{in}$ the constant active power injected or consumed in bus $i$ (by batteries, loads, VSC stations and LCC stations). +- $\boldsymbol{P_{i,g}}$ is the variable active power produced by generators of bus $i$. +- $\boldsymbol{\sigma_{P,i}^{+}}$ (resp. $\boldsymbol{\sigma_{P,i}^{-}}$) is a positive slack variable expressing the excess (resp. shortfall) of active power produced in bus $i$. + +And the following objective function: + +$$\text{minimize} \left(1000 \sum\limits_{i} (\boldsymbol{\sigma_{i}^{P,+}} + \boldsymbol{\sigma_{i}^{P,-}}) + \sum\limits_{g} \left(\frac{\boldsymbol{P_{i,g}} - P_{i,g}^{t}}{\max(1, \frac{P_{i,g}^t}{100})}\right)^2\right)$$ + +where $P_{i,g}^{t}$ is the target of the generator $g$ on bus $i$. + +The sum of the active slack variables ($\boldsymbol{\sigma_{i}^{P,+}}$ and $\boldsymbol{\sigma_{i}^{P,-}}$) is penalized by a high coefficient ($1000$) to drive it towards $0$, ensuring active power balance at each bus of the network. +The solving of the DCOPF is considered as successful if this sum **does not exceed** the configurable threshold `Pnull` (see [Configuration of the run](inputs.md#configuration-of-the-run)), and if the non-linear solver employed (see [Non-linear optimization solver](../gettingStarted.md#non-linear-optimization-solver)) finds a feasible solution without reaching one of its default limit. Otherwise, the solving is considered unsuccessful and the script `reactiveopfexit.run` is executed (see [In case of inconsistency](outputs.md#in-case-of-inconsistency)). diff --git a/docs/optimizer/divisionOfCode.md b/docs/optimizer/divisionOfCode.md new file mode 100644 index 00000000..152f94e6 --- /dev/null +++ b/docs/optimizer/divisionOfCode.md @@ -0,0 +1,23 @@ +# Division of the code + +The code of the reactive OPF is divided into several files, each serving a specific function: + +- `reactiveopf.dat` defines the network data files imported (files with *ampl_* prefix), and the files used to configure the run (files with *param_* prefix). + See [Inputs](inputs.md). + +- `iidm_importer.mod`, `or_param_importer.mod` and `commons.mod` define the sets and parameters of the optimization. + +- `connected_component.mod`, `dcopf.mod` and `acopf.mod` define the optimization problems solved in `reactiveopf.run`. + See [Slack bus and main connex components](slackBusMainConnexComponent.md), [DC optimal power flow](dcOptimalPowerflow.md) and [AC optimal power flow](acOptimalPowerflow.md), respectively. + +- `connected_component.run`, `dcopf.run`, `acopf_preprocessing.run` and `acopf.run` orchestrate the optimization and its post-process. + +- `reactiveopfoutput.mod` exports result files if the execution of `reactiveopf.run` is successful. + See [Outputs](outputs.md#in-case-of-convergence). + +- `reactiveopfexit.run` contains the code executed when the process fails. + Refer to section [8.2](outputs.md#in-case-of-inconsistency). + +- `reactiveopf.run` executes the AMPL process of OpenReac, calling the previous scripts. + + diff --git a/docs/optimizer/index.md b/docs/optimizer/index.md new file mode 100644 index 00000000..fc3b60ad --- /dev/null +++ b/docs/optimizer/index.md @@ -0,0 +1,26 @@ +# Overview + +The reactive optimal power flow (OPF) is implemented with AMPL. +Its goal is to compute voltage values on each point of the network as well as control values for reactive equipment and controllers of the grid (voltage set point of generating units, shunts, transformer ratios...). + +In a grid development study, you decide new equipment, new generating units, new substations, new loads, you set values for active and reactive loads, you set values for active power generation and HVDC flows. +Then if you wish to do AC powerflow simulations with N-1 analysis, you need all voltage and reactive set points and this reactive OPF is your solution. + +Please notice that this reactive OPF does **not** decide active power of generating units and HVDC branches. + + +```{toctree} +--- +maxdepth: 2 +hidden: true +divisionOfCode.md +inputs.md +preprocessing.md +slackBusMainConnexComponent.md +dcOptimalPowerflow.md +acOptimalPowerflow.md +outputs.md +--- + +``` + diff --git a/docs/optimizer/inputs.md b/docs/optimizer/inputs.md new file mode 100644 index 00000000..f64cb62e --- /dev/null +++ b/docs/optimizer/inputs.md @@ -0,0 +1,61 @@ +# Inputs + +## Network data + +Files with the prefix `ampl_` contain the data and the parameters of the network on which the reactive OPF is executed. +These files are obtained by using the [extended version of PowSyBl AMPL export](inv:powsyblcore:*:*:#the-extendedamplexporter), which is the default version. + +## Configuration of the run + +The user can configure the run with the dedicated Java interface +(see [OpenReacParameters](../open-reac/src/main/java/com/powsybl/openreac/parameters/input/OpenReacParameters.java)). +Specifically, the user can set various parameters and thresholds used in the preprocessing and modeling of the reactive OPF. +These are specified in the file `param_algo.txt`: + +| Parameter | Description | Java default value | Domain | +|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|-----------------------------------------------| +| `log_level_ampl` | Level of display for AMPL prints | INFO | {DEBUG, INFO, WARNING, ERROR} | +| `log_level_knitro` | Level of display for solver prints (see [AMPL documentation](https://dev.ampl.com/ampl/options.html)) | $1$ | {0, 1, 2} | +| `objective_choice` | Choice of the objective function for the ACOPF (see [AC optimal powerflow](acOptimalPowerflow.md)) | $0$ | {0, 1, 2} | +| `ratio_voltage_target` | Ratio to calculate target V of buses when `objective_choice` is set to $1$ (see [AC optimal powerflow](acOptimalPowerflow.md)) | $0.5$ | $\[0; 1\]$ | +| `coeff_alpha` | Weight to favor more/less minimization of active power produced by generators or deviation between them and target values (see [AC optimal powerflow](acOptimalPowerflow.md)) | $1$ | $\[0; 1\]$ | +| `Pnull` | Threshold of active and reactive powers considered as null | $0.01$ (MW) | $\[0; 1\]$ | +| `Znull` | Threshold of impedance considered as null (see [Zero impedance threshold](preprocessing.md#zero-impedance-threshold)) | $10^{-5}$ (p.u.) | $\[0; 0.1\]$ | + | `epsilon_nominal_voltage` | Threshold to ignore voltage levels with nominal voltage lower than it | $1$ (kV) | $\mathbb{R}^{+}$ | +| `min_plausible_low_voltage_limit` | Consistency bound for low voltage limit of voltage levels (see [Voltage level limit consistency](preprocessing.md#voltage-level-limit-consistency)) | $0.5$ (p.u.) | $\mathbb{R}^{+}$ | +| `max_plausible_high_voltage_limit` | Consistency bound for high voltage limit of voltage levels (see [Voltage level limit consistency](preprocessing.md#voltage-level-limit-consistency)) | $1.5$ (p.u.) | [`min_plausible_low_voltage_limit`; $\infty$] | +| `ignore_voltage_bounds` | Threshold to replace voltage limits of voltage levels with nominal voltage lower than it, by [min_plausible_low_voltage_limit; max_plausible_high_voltage_limit] | $0$ (p.u.) | $\mathbb{R}^{+}$ | +| `buses_with_reactive_slacks` | Choice of which buses will have reactive slacks attached in ACOPF solving (see [AC optimal powerflow](acOptimalPowerflow.md)) | ALL | {CONFIGURED, NO_GENERATION, ALL} | +| `PQmax` | Threshold for maximum active and reactive power considered in correction of generator limits (see [PQ unit domain](preprocessing.md#pq-unit-domain)) | $9000$ (MW, MVAr) | $\mathbb{R}$ | +| `defaultPmax` | Threshold for correction of high active power limit produced by generators (see [PQ unit domain](preprocessing.md#pq-unit-domain)) | $1000$ (MW) | $\mathbb{R}$ | +| `defaultPmin` | Threshold for correction of low active power limit produced by generators (see [PQ unit domain](preprocessing.md#pq-unit-domain)) | $0$ (MW) | $\mathbb{R}$ | +| `defaultQmaxPmaxRatio` | Ratio used to calculate threshold for corrections of high/low reactive power limits (see [PQ unit domain](preprocessing.md#pq-unit-domain)) | $0.3$ (MVAr/MW) | $\mathbb{R}$ | +| `minimalQPrange` | Threshold to fix active (resp. reactive) power of generators with active (resp. reactive) power limits that are closer than it (see [PQ unit domain](preprocessing.md#pq-unit-domain)) | $1$ (MW, MVAr) | $\mathbb{R}$ | +| `default_variable_scaling_factor` | Default scaling factor applied to all the variables (except reactive slacks and transformer ratios) before ACOPF solving | $1$ | $\mathbb{R}^{*,+}$ | +| `default_constraint_scaling_factor` | Default scaling factor applied to all the constraints before ACOPF solving | $1$ | $\mathbb{R}^{+}$ | +| `reactive_slack_variable_scaling_factor` | Scaling factor applied to all reactive slacks variables before ACOPF solving (see [AC optimal powerflow](acOptimalPowerflow.md)) | $0.1$ | $\mathbb{R}^{*,+}$ | +| `transformer_ratio_variable_scaling_factor` | Scaling factor applied to all transformer ratio variables before ACOPF solving (see [AC optimal powerflow](acOptimalPowerflow.md)) | $0.001$ | $\mathbb{R}^{*,+}$ | +| `shunt_variable_scaling_factor` | Scaling factor applied to all shunt variables before ACOPF solving (see [AC optimal powerflow](acOptimalPowerflow.md)) | $0.1$ | $\mathbb{R}^{*,+}$ | + +Please note that for these parameters, the AMPL code defines default values which may be different from those in Java (for example, for the scaling values). This allows a user to use the AMPL code without going through the Java interface, and without providing the file `param_algo.txt`. + +In addition to the previous parameters, the user can specify which parameters will be variable or fixed in the ACOPF solving (see [AC optimal powerflow](acOptimalPowerflow.md)). +This is done using the following files: + +| File | Description | Default behavior of modified values | +|---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------| +| `param_transformers.txt` | Ratio tap changers with a variable transformation ratio (real variable) | Transformation ratios are fixed | +| `param_shunt.txt` | Shunts with a continuous variable susceptance and which can be modified and/or connected (only if possible bus is defined in `ampl_network_shunts.txt`) | Shunt susceptances are fixed | +| `param_generators_reactive.txt` | Generators with a constant reactive power production. If this value is not consistent (> PQmax), the reactive power production stays variable | Coherent reactive power productions (see [4.5](#45-pq-units-domain)) are variable | +| `param_buses_with_reactive_slack.txt` | Buses with attached reactive slacks if configurable parameter buses_with_reactive_slacks = "CONFIGURED" | Only buses with no reactive power production have reactive slacks attached | + +All of these files share the same format: 2 columns #"num" "id". + +Once again, the user can directly execute the AMPL code without passing these parameters files as input. +If so, empty files will be created during execution. + +## New voltage limits + +In addition to the elements specified in section [Configuration of the run](#configuration-of-the-run), the user may choose to override the voltage limits of specified voltage levels. These values must be defined in `ampl_network_substations_override.txt` and are employed to establish the new voltage limits as specified in section [Voltage level limit consistency](preprocessing.md#voltage-level-limit-consistency). + +Format of `ampl_network_substations_override.txt`: 4 columns #"num" "minV (pu)" "maxV (pu)" "id" diff --git a/docs/optimizer/outputs.md b/docs/optimizer/outputs.md new file mode 100644 index 00000000..912ffb37 --- /dev/null +++ b/docs/optimizer/outputs.md @@ -0,0 +1,35 @@ +# Outputs + +## In case of convergence + +After the solving of the ACOPF, the script `reactiveopfoutput.run` is executed + and the following files (except `reactiveopf_results_indic.txt`) are exported only if the solving +is considered as successful (see [7](#7-alternative-current-optimal-power-flow)): + +| File | Content | Format | +|---------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| +| `reactiveopf_results_indic.txt` | General information (system OS, computation time, etc.). The configurable thresholds/parameters (see section [3.2](#32-configuration-of-the-run)). The cardinality of the sets (number of non-impedance branches, number of slack variables, etc.). Information about calculated angles (maximum/minimum theta, maximum difference between neighbors, etc.). | | +| `reactiveopf_results_static_var_compensators.csv` | Calculated voltage and reactive power values for the SVC that regulate voltage. | 6 columns #"variant" "num" "bus" "vRegul" "V(pu)" "Q(MVAr)" | +| `reactiveopf_results_shunts.csv` | Calculated reactive power (and susceptance) values for shunts that were either connected or modified after the optimization problems were resolved. | 6 columns #"variant" "num" "bus" "b(pu)" "Q(MVAr)" "section" | +| `reactiveopf_results_generators.csv` | Calculated active and reactive power values for generating units. | 9 columns #"variant" "num" "bus" "vRegul" "V(pu)" "targetP(MW)" "targetQ(MVAr)" "P(MW)" "Q(MW)" | +| `reactiveopf_results_vsc_converter_stations.csv` | Calculated reactive power values for VSC converter stations. | 8 columns #"variant" "num" "bus" "vRegul" "targetV(pu)" "targetQ(MVAr)" "P(MW)" "Q(MVAr)" | +| `reactiveopf_results_rtc.csv` | RTCs and associated taps, with transformer ratio closest to the one calculated after the optimization. | 3 columns #"variant" "num" "tap" | +| `reactiveopf_results_reactive_slacks.csv` | Calculated reactive slack variables $\boldsymbol{\sigma^{Q,-}}$ and $\boldsymbol{\sigma^{Q,+}}$. | 6 columns #"variant" "bus" "slack_condensator(MVAr)" "slack_self(MVAr)" "id" "substation" | +| `reactiveopf_results_voltages.csv` | Calculated voltages for each bus of the main connex component (see [5](#5-slack-bus--main-connex-component)). | 5 columns #"variant" "bus" "V(pu)" "theta(rad)" "id" | + +If ACOPF solving is not successful, the user can export the following optional files (aiding for the analysis of ACOPF results) by specifying the + ampl log parameter to a debug level (see [3.2](#32-configuration-of-the-run)): + +| File | Content | Format | +|--------------------------------------------|------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| `debug_bus.csv` | Information on calculated voltages (values, bounds, repartition, etc.). | 13 columns #"bus_id" "Vnom" "V" "Vlb" "Vub" "Vmin_mod" "Vmax_mod" "Vmin_OK" "Vmax_OK" "Vmin_ori" "Vmax_ori" "sQ1" "sQ2" | +| `reactiveopf_results_generators_Pnull.csv` | Information on units that are excluded from the solving of ACOPF (see [7](#7-alternative-current-optimal-power-flow)). | 11 columns #"variant" "num" "bus" "vRegul" "V(pu)" "targetP(MW)" "targetQ(Mvar)" "P(MW)" "Q(MW)" "id" "bus_id" | + +## In case of inconsistency + +If the computation of the main connex component (see [5](#5-slack-bus--main-connex-component)) or +of the DCOPF fails (see [6](#6-direct-current-optimal-power-flow)), +the problem is considered as inconsistent. +Then, the script `reactiveopfexit.run` is executed and the file `reactiveopf_results_indic.txt` described in [8.1](#81-in-case-of-convergence) + is exported, without the information on the calculated angles. + diff --git a/docs/optimizer/preprocessing.md b/docs/optimizer/preprocessing.md new file mode 100644 index 00000000..59b49c5a --- /dev/null +++ b/docs/optimizer/preprocessing.md @@ -0,0 +1,56 @@ +# Pre-processing + +Before solving the reactive ACOPF described in [AC optimal powerflow](acOptimalPowerflow.md), the following pre-processing blocks are executed to ensure the consistency of the values used in the optimization. + +##Voltage level limit consistency + +To ensure consistent voltage level limits for the buses, the configurable domain [`min_plausible_low_voltage_limit`; `max_plausible_high_voltage_limit`] is used (see [Configuration of the run](inputs.md#configuration-of-the-run)). + +Let $V_{s}^{min}$ (resp. $V_{s}^{max}$) be the low (resp. high) voltage limit of substation $s$ specified in `ampl_network_substations.txt` (or in `ampl_network_substations_override.txt` if an override is given for $s$) and $V_{s}^{min,c}$ (resp. $V_{s}^{max,c}$) its associated corrected low (resp. high) limit. Then, the limits are calculated as follows: +- $V_{s}^{min,c} = \max(V_{s}^{min},$ min_plausible_low_voltage_limit) +- $V_{s}^{max,c} = \min(V_{s}^{max},$ max_plausible_low_voltage_limit) + +## Zero-impedance branches + +Branches with an impedance magnitude, **calculated in per unit**, lower than the configurable threshold `Znull` (see section [Configuration of the run](inputs.md#configuration-of-the-run)) are considered as non-impedant. +These branches will have their reactance replaced by the threshold `Znull` (in p.u.), **even if the reactance specified in `ampl_network_branches.txt` is negative**. + +## Impedance of transformers + +In the calculations of the ACOPF (see [AC optimal powerflow](acOptimalPowerflow.md)), the transformers with an impedance (specified in `ampl_network_branches.txt`) considered as null (see [Zero-impedance branches](preprocessing.md#zero-impedance-branches)) **are treated as lines**. Then, the transformation ratios/phase shifts are ignored, as well as the impedance specified in the tap changer table `ampl_network_tct.txt`. + +For phase shifters transformers considered as impedant, the reactance values from the tap changer table (in `ampl_network_tct.txt`) replace the reactance specified in `ampl_network_branches.txt`. The resistance is then calculated proportionally. +For the ratio tap changers, the impedance stays as specified in `ampl_network_branches.txt`. **Please notice there is no specified handling for cases where resistance and/or reactance is negative or if there is both a ratio tap changer and a phase shift transformer on the same branch.** + +## P/Q unit domain + +The following corrections apply successively to determine consistent domains for the active power and reactive power produced by generators. + +To determine the consistent domain of produced active power, the bounds of the domains $P_g^{min}$ and $P_g^{max}$, as well as the target $P_g^{t}$ of generator $g$ (all specified in `ampl_network_generators.txt`) are used. + +Let $P_{g}^{min,c}$ and $P_{g}^{max,c}$ be the corrected active bounds: + +- By default, $P_{g}^{min,c} = \text{defaultPmin}$ and $P_{g}^{max,c} = \text{defaultPmax}$ (see [Configuration of the run](inputs.md#configuration-of-the-run)) +- If $|P_g^{max}| \geq \text{PQmax}$, then $P_{g}^{max,c} = \max(\text{defaultPmax}, P_g^t)$ +- If $|P_g^{min}| \geq \text{PQmax}$, then $P_{g}^{min,c} = \min(\text{defaultPmin}, P_g^t)$ +- If $|P_{g}^{max,c} - P_{g}^{min,c}| \leq \text{minimalQPrange}$, then $P_{g}^{max,c} = P_{g}^{min,c} = P_{g}^t$ (active power is fixed). + +To determine the consistent domain of produced reactive power, the reactive power diagram (specified in `ampl_network_generators.txt`) of generator $g$ is used: $qp_g$ (resp. $qP_g$) and $Qp_g$ ($QP_g$) when $P_{g}^{min,c}$ (resp. $P_{g}^{max,c}$) is reached. +Let $qp_g^c$ (resp. $qP_g^c$) and $Qp_g^c$ (resp. $QP_g^c$) be the bounds of the corrected reactive diagram, and $Q_{g}^{min,c}$ and $Q_{g}^{max,c}$ be the corrected reactive bounds: + +- By default, $qp_g^{c} = qP_{g}^{c} = - \text{defaultPmin} \times \text{defaultQmaxPmaxRatio}$ and $Qp_{g}^{c} = QP_{g}^{c} = \text{defaultPmax} \times \text{defaultQmaxPmaxRatio}$ (see [Configuration of the run](inputs.md#configuration-of-the-run)) +- If $|qp_{g}| \geq \text{PQmax}$, then $qp_{g}^{c} = -\text{defaultQmaxPmaxRatio} \times P_{max}^{g,c}$. + Same with $qP_{g}^{c}$. +- If $|Qp_{g}| \geq \text{PQmax}$, then $Qp_{g}^{c} = \text{defaultQmaxPmaxRatio} \times P_{max}^{g,c}$. + Same with $QP_{g}^{c}$. +- If $qp_{g}^{c} > Qp_{g}^{c}$, the values are swapped. + Same with $qP_{g}^{c}$ and $QP_{g}^{c}$. +- If the corrected reactive diagram is too small (the distances between the vertices of the reactive diagram are lower than $\text{minimalQPrange}$), then $qp_{g}^{c} = Qp_{g}^{c} = qP_{g}^{c} = QP_{g}^{c} = \frac{qp_{g}^{c} + Qp_{g}^{c} + qP_{g}^{c} + QP_{g}^{c}}{4}$ (reactive power is fixed). +- $Q_{g}^{min,c} = \min(qp_{g}^{c}, qP_{g}^{c})$ and $Q_{g}^{max,c} = \min(Qp_{g}^{c}, QP_{g}^{c})$ + +Please note that in the end, **the corrected bounds are rectangular**, not trapezoidal, and they are used only in the reactive OPF (see [AC optimal powerflow](acOptimalPowerflow.md). The trapezoidal diagram should be added shortly. +In addition, bounds $qP_{g}^0$ and $Qp_{g}^0$ are not used, as generators with zero active power will be excluded from the optimisation (see [AC optimal powerflow](acOptimalPowerflow.md#generalities)). + +The general correction of the generator's reactive power diagram $g$ is illustrated in the following figure: +![Reactive diagram correction](_static/img/reactive-diagram.png){width="30%" align=center} + diff --git a/docs/optimizer/slackBusMainConnexComponent.md b/docs/optimizer/slackBusMainConnexComponent.md new file mode 100644 index 00000000..cd317005 --- /dev/null +++ b/docs/optimizer/slackBusMainConnexComponent.md @@ -0,0 +1,23 @@ +# Slack bus and main connex component + +The slack bus $s$ is determined by identifying the bus with the **highest number of AC branches connected**, within the main component (`cc` set to $0$ in `ampl_network_buses.txt`). +If multiple buses have such cardinality, the one with the highest identifier (`num` parameter) is chosen. +In the event no bus satisfies these conditions, the first bus defined in `ampl_network_buses.txt` is selected. + +The OPFs are executed on the **main connex component** (i.e. buses connected to slack bus by AC branches) of the network. +Consequently, **buses connected to the slack only by HVDC lines are excluded**. + +This component is determined by solving the following optimization problem (the variables are bolded): + +$$\text{maximize} \left(\sum\limits_{i} \boldsymbol{\theta_i^{cc}}\right)$$ + +where $\boldsymbol{\theta_i^{cc}}$ is the voltage angle of bus $i$, and with the following constraints: + +$$\boldsymbol{\theta_s^{cc}} = 0 \quad (1)$$ + +$$\boldsymbol{\theta_i^{cc}} - \boldsymbol{\theta_j^{cc}} = 0, \quad ij \in BRANCH \quad (2)$$ + +$$0 \leq \boldsymbol{\theta_i^{cc}} \leq 1, \quad i \in BUS \quad (3)$$ + +If the solving is unsuccessful, the script `reactiveopfexit.run` is executed (see [In case of inconsistency](outputs.md#in-case-of-inconsistency)) and the execution is stopped. +The sets of buses and branches belonging to the main connex component are now denoted $BUSCC$ and $BRANCHCC$, respectively. diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..c410d7c5 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,5 @@ +sphinx==7.1.2 +sphinx-tabs +furo==2024.1.29 +myst-parser +sphinx-copybutton \ No newline at end of file