diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index f94cf44476..9c6436ad08 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -7,9 +7,17 @@ jobs: manylinux_build: name: Build linux ${{ matrix.python.name }} wheel runs-on: ubuntu-latest +<<<<<<< HEAD container: quay.io/pypa/manylinux2014_x86_64:2024-10-07-1887322 env: ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true +||||||| 4ebe183e + container: quay.io/pypa/manylinux2014_x86_64:2024-07-01-8dac23b + env: + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true +======= + container: quay.io/pypa/manylinux_2_28_x86_64:2024-11-16-d70d8cd +>>>>>>> main strategy: matrix: python: @@ -20,21 +28,24 @@ jobs: } steps: + - name: Install Linux Packages + run: dnf install -y wget + - name: Install Maven run: | - curl --fail --silent --show-error https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz -o /tmp/apache-maven-3.9.6-bin.tar.gz + wget -q https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz -P /tmp tar xf /tmp/apache-maven-*.tar.gz -C /opt echo /opt/apache-maven-3.9.6/bin >> $GITHUB_PATH - name: Setup GraalVM - uses: graalvm/setup-graalvm@v1.1.5 # !!! this is last version compatible with manylinux 2014 + uses: graalvm/setup-graalvm@v1 with: - java-version: '17' + java-version: '17.0.12' distribution: 'graalvm' github-token: ${{ secrets.GITHUB_TOKEN }} - name: Checkout sources - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true @@ -130,17 +141,17 @@ jobs: - name: Setup GraalVM uses: graalvm/setup-graalvm@v1 with: - java-version: '17' + java-version: '17.0.12' distribution: 'graalvm' github-token: ${{ secrets.GITHUB_TOKEN }} - name: Checkout sources - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python.version }} @@ -181,6 +192,7 @@ jobs: cmake $GITHUB_WORKSPACE/cpp -DBUILD_PYPOWSYBL_JAVA=OFF -DPYPOWSYBL_JAVA_LIBRARY_DIR=$GITHUB_WORKSPACE/dist/lib -DPYPOWSYBL_JAVA_INCLUDE_DIR=$GITHUB_WORKSPACE/dist/include cmake --build . --config Release +<<<<<<< HEAD build: name: Run SonarCloud analysis for Java code runs-on: ubuntu-latest @@ -210,3 +222,38 @@ jobs: env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} +||||||| 4ebe183e + + + +======= + build: + name: Run SonarCloud analysis for Java code + runs-on: ubuntu-latest + + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + + - name: Build with Maven (Ubuntu) + working-directory: ./java + run: > + mvn --batch-mode install + + - name: Run SonarCloud analysis + working-directory: ./java + run: > + mvn --batch-mode -DskipTests sonar:sonar + -Dsonar.host.url=https://sonarcloud.io + -Dsonar.organization=powsybl-ci-github + -Dsonar.projectKey=powsybl_pypowsybl-java + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + +>>>>>>> main diff --git a/.github/workflows/full-ci.yml b/.github/workflows/full-ci.yml index b44ad749d6..07fc18edd5 100644 --- a/.github/workflows/full-ci.yml +++ b/.github/workflows/full-ci.yml @@ -13,9 +13,7 @@ jobs: manylinux_build: name: Build linux ${{ matrix.python.name }} wheel runs-on: ubuntu-latest - container: quay.io/pypa/manylinux2014_x86_64:2024-10-07-1887322 - env: - ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true + container: quay.io/pypa/manylinux_2_28_x86_64:2024-11-16-d70d8cd strategy: matrix: python: @@ -44,28 +42,31 @@ jobs: abi: cp312, version: '3.12' } - - { - name: cp313, - abi: cp313, - version: '3.13' - } +# - { +# name: cp313, +# abi: cp313, +# version: '3.13' +# } steps: + - name: Install Linux Packages + run: dnf install -y wget + - name: Install Maven run: | - curl --fail --silent --show-error https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz -o /tmp/apache-maven-3.9.6-bin.tar.gz + wget -q https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz -P /tmp tar xf /tmp/apache-maven-*.tar.gz -C /opt echo /opt/apache-maven-3.9.6/bin >> $GITHUB_PATH - name: Setup GraalVM - uses: graalvm/setup-graalvm@v1.1.5 # !!! this is last version compatible with manylinux 2014 + uses: graalvm/setup-graalvm@v1 with: - java-version: '17' + java-version: '17.0.12' distribution: 'graalvm' github-token: ${{ secrets.GITHUB_TOKEN }} - name: Checkout sources - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true @@ -126,7 +127,7 @@ jobs: run: make html doctest SPHINXOPTS="-W" - name: Upload wheel - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: pypowsybl-wheel-linux-${{ matrix.python.name }} path: wheelhouse/*.whl @@ -174,26 +175,26 @@ jobs: name: cp312, version: '3.12', } - - { - name: cp313, - version: '3.13', - } +# - { +# name: cp313, +# version: '3.13', +# } steps: - name: Setup GraalVM uses: graalvm/setup-graalvm@v1 with: - java-version: '17' + java-version: '17.0.12' distribution: 'graalvm' github-token: ${{ secrets.GITHUB_TOKEN }} - name: Checkout sources - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python.version }} @@ -227,7 +228,7 @@ jobs: run: make html doctest SPHINXOPTS="-W" - name: Upload wheel - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: pypowsybl-wheel-${{ matrix.config.name }}-${{ matrix.python.name }} path: dist/*.whl @@ -239,12 +240,12 @@ jobs: steps: - name: Download wheels - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: path: download - name: Upload wheels - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: pypowsybl-wheels path: | @@ -263,10 +264,10 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' @@ -282,6 +283,6 @@ jobs: mvn --batch-mode -DskipTests sonar:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=powsybl-ci-github - -Dsonar.projectKey=powsybl_pypowsybl + -Dsonar.projectKey=powsybl_pypowsybl-java env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release-ci.yml b/.github/workflows/release-ci.yml index f9b7f34294..6839e0a574 100644 --- a/.github/workflows/release-ci.yml +++ b/.github/workflows/release-ci.yml @@ -12,9 +12,7 @@ jobs: manylinux_build: name: Build linux ${{ matrix.python.name }} wheel runs-on: ubuntu-latest - container: quay.io/pypa/manylinux2014_x86_64:2024-10-07-1887322 - env: - ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true + container: quay.io/pypa/manylinux_2_28_x86_64:2024-11-16-d70d8cd strategy: matrix: python: @@ -25,21 +23,24 @@ jobs: } steps: + - name: Install Linux Packages + run: dnf install -y wget + - name: Install Maven run: | - curl --fail --silent --show-error https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz -o /tmp/apache-maven-3.9.6-bin.tar.gz + wget https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz -P /tmp tar xf /tmp/apache-maven-*.tar.gz -C /opt echo /opt/apache-maven-3.9.6/bin >> $GITHUB_PATH - name: Setup GraalVM - uses: graalvm/setup-graalvm@v1.1.5 # !!! this is last version compatible with manylinux 2014 + uses: graalvm/setup-graalvm@v1 with: - java-version: '17' + java-version: '17.0.12' distribution: 'graalvm' github-token: ${{ secrets.GITHUB_TOKEN }} - name: Checkout sources - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true @@ -95,17 +96,17 @@ jobs: - name: Setup GraalVM uses: graalvm/setup-graalvm@v1 with: - java-version: '17' + java-version: '17.0.12' distribution: 'graalvm' github-token: ${{ secrets.GITHUB_TOKEN }} - name: Checkout sources - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python.version }} diff --git a/.github/workflows/snapshot-ci.yml b/.github/workflows/snapshot-ci.yml index aa35ca19cc..4d7f29b62a 100644 --- a/.github/workflows/snapshot-ci.yml +++ b/.github/workflows/snapshot-ci.yml @@ -6,7 +6,7 @@ on: - cron: '0 3 * * *' jobs: - buil_pypowsybl: + build_pypowsybl: name: Build ${{ matrix.config.name }} ${{ matrix.python.name }} wheel runs-on: ${{ matrix.config.os }} strategy: @@ -22,21 +22,16 @@ jobs: - { name: cp310, version: '3.10' } - { name: cp311, version: '3.11' } - { name: cp312, version: '3.12' } - fail-fast: false + defaults: + run: + shell: bash steps: - #SETUP JDK - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '17' - #SETUP PYTHON - name: Set up Python ${{ matrix.python.version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python.version }} - name: Install Python dependencies @@ -50,8 +45,17 @@ jobs: distribution: 'graalvm' github-token: ${{ secrets.GITHUB_TOKEN }} + #DEFINE SCRIPTS PATH + - name: Set up script path + run: | + SCRIPTS_PATH="${GITHUB_WORKSPACE}/scripts/.github/workflows/scripts" + if [[ "${{ matrix.config.name }}" == "windows" ]]; then + SCRIPTS_PATH=$(echo "$SCRIPTS_PATH" | sed 's/\\/\//g') + fi + echo "SCRIPTS_PATH=$SCRIPTS_PATH" >> $GITHUB_ENV + #BUILD CORE - - name: Checkout core-sources + - name: Checkout core sources uses: actions/checkout@v4 with: repository: powsybl/powsybl-core @@ -60,34 +64,25 @@ jobs: - name: Build CORE run: mvn -batch-mode --no-transfer-progress clean install -DskipTests working-directory: ./powsybl-core - shell: bash - name: get CORE_VERSION run: echo "CORE_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV working-directory: ./powsybl-core - shell: bash - #CHECKOUT PYPOSSYBL - - name: Checkout pypowsybl + #CHECKOUT SCRIPT + #The script check_snapshot_branch.sh is located in the workflow folder of the pypowsybl repository + #It is necessary for checking out the integration branch if it exists (pypowsybl include) + - name: Checkout script uses: actions/checkout@v4 with: - path: pypowsybl - submodules: true + sparse-checkout: | + .github + sparse-checkout-cone-mode: false + path: scripts - #DEFINE SCRIPTS PATH - - name: Set up script path - shell: bash - run: | - SCRIPTS_PATH="${GITHUB_WORKSPACE}/pypowsybl/.github/workflows/scripts" - if [[ "${{ matrix.config.name }}" == "windows" ]]; then - SCRIPTS_PATH=$(echo "$SCRIPTS_PATH" | sed 's/\\/\//g') - fi - echo "SCRIPTS_PATH=$SCRIPTS_PATH" >> $GITHUB_ENV - - #BUILD LOADFLOW - - name: Check LOADFLOW SNAPSHOT branch + #BUILD OPENLOADFLOW + - name: Checking for openloadflow snapshot branch run : ${{ env.SCRIPTS_PATH }}/check_snapshot_branch.sh "https://github.com/powsybl/powsybl-open-loadflow.git" ${{ env.CORE_VERSION }} - shell: bash - - name: Checkout loadflow-sources + - name: Checkout openloadflow sources uses: actions/checkout@v4 with: repository: powsybl/powsybl-open-loadflow @@ -95,21 +90,18 @@ jobs: path: powsybl-open-loadflow - name: Change core version run: mvn versions:set-property -Dproperty=powsybl-core.version -DnewVersion=${{ env.CORE_VERSION}} -DgenerateBackupPoms=false - shell: bash working-directory: ./powsybl-open-loadflow - name: Build LOADFLOW - run: mvn -batch-mode --no-transfer-progress clean install -DskipTests + run: mvn -batch-mode --no-transfer-progress clean install working-directory: ./powsybl-open-loadflow - name: Get LOADFLOW_VERSION run: echo "LOADFLOW_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV working-directory: ./powsybl-open-loadflow - shell: bash #BUILD DIAGRAM - - name: Check for DIAGRAM SNAPSHOT branch + - name: Checking for diagram snapshot branch run : ${{ env.SCRIPTS_PATH }}/check_snapshot_branch.sh "https://github.com/powsybl/powsybl-diagram.git" ${{ env.CORE_VERSION }} - shell: bash - - name: Checkout diagram-sources + - name: Checkout diagram sources uses: actions/checkout@v4 with: repository: powsybl/powsybl-diagram @@ -118,44 +110,38 @@ jobs: - name: Change core version run: mvn versions:set-property -Dproperty=powsybl-core.version -DnewVersion=${{ env.CORE_VERSION}} -DgenerateBackupPoms=false working-directory: ./powsybl-diagram - shell: bash - name: Build DIAGRAM - run: mvn -batch-mode --no-transfer-progress clean install -DskipTests + run: mvn -batch-mode --no-transfer-progress clean install working-directory: ./powsybl-diagram - name: Get DIAGRAM_VERSION version run: echo "DIAGRAM_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV working-directory: ./powsybl-diagram - shell: bash #BUILD ENTSOE - - name: Check for ENTSOE SNAPSHOT branch + - name: Checking for entsoe snapshot branch run : ${{ env.SCRIPTS_PATH }}/check_snapshot_branch.sh "https://github.com/powsybl/powsybl-entsoe.git" ${{ env.CORE_VERSION }} - shell: bash - - name: Checkout entsoe-sources + - name: Checkout entsoe sources uses: actions/checkout@v4 with: repository: powsybl/powsybl-entsoe ref: ${{ env.SNAPSHOT_BRANCH }} - path: powsybl-enstoe + path: powsybl-entsoe - name: Change core/loadflow version run: | - mvn versions:set-property -Dproperty=powsybl.core.version -DnewVersion=${{ env.CORE_VERSION}} -DgenerateBackupPoms=false + mvn versions:set-property -Dproperty=powsyblcore.version -DnewVersion=${{ env.CORE_VERSION}} -DgenerateBackupPoms=false mvn versions:set-property -Dproperty=powsyblopenloadflow.version -DnewVersion=${{ env.LOADFLOW_VERSION}} -DgenerateBackupPoms=false - working-directory: ./powsybl-enstoe - shell: bash + working-directory: ./powsybl-entsoe - name: Build ENTSOE - run: mvn -batch-mode --no-transfer-progress clean install -DskipTests - working-directory: ./powsybl-enstoe + run: mvn -batch-mode --no-transfer-progress clean install + working-directory: ./powsybl-entsoe - name: Get ENTSOE_VERSION run: echo "ENTSOE_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV - working-directory: ./powsybl-enstoe - shell: bash + working-directory: ./powsybl-entsoe #BUILD OPENRAO - - name: Check OPENRAO SNAPSHOT branch + - name: Checking for openrao snapshot branch run : ${{ env.SCRIPTS_PATH }}/check_snapshot_branch.sh "https://github.com/powsybl/powsybl-open-rao.git" ${{ env.CORE_VERSION }} - shell: bash - - name: Checkout open-rao-sources + - name: Checkout openrao sources uses: actions/checkout@v4 with: repository: powsybl/powsybl-open-rao @@ -167,20 +153,17 @@ jobs: mvn versions:set-property -Dproperty=powsybl.entsoe.version -DnewVersion=${{ env.ENTSOE_VERSION}} -DgenerateBackupPoms=false mvn versions:set-property -Dproperty=powsybl.openloadflow.version -DnewVersion=${{ env.LOADFLOW_VERSION}} -DgenerateBackupPoms=false working-directory: ./powsybl-openrao - shell: bash - name: Build OPENRAO - run: mvn -batch-mode --no-transfer-progress clean install -DskipTests + run: mvn -batch-mode --no-transfer-progress clean install working-directory: ./powsybl-openrao - name: Get OPENRAO_VERSION run: echo "OPENRAO_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV working-directory: ./powsybl-openrao - shell: bash #BUILD DYNAWO - - name: Check for DYNAWO SNAPSHOT branch + - name: Checking for dynawo snapshot branch run: ${{ env.SCRIPTS_PATH }}/check_snapshot_branch.sh "https://github.com/powsybl/powsybl-dynawo.git" ${{ env.CORE_VERSION }} - shell: bash - - name: Checkout dynawo-sources + - name: Checkout dynawo sources uses: actions/checkout@v4 with: repository: powsybl/powsybl-dynawo @@ -189,17 +172,15 @@ jobs: - name: Change core version run: mvn versions:set-property -Dproperty=powsybl-core.version -DnewVersion=${{ env.CORE_VERSION}} -DgenerateBackupPoms=false working-directory: ./powsybl-dynawo - shell: bash - name: Build DYNAWO - run: mvn -batch-mode --no-transfer-progress clean install -DskipTests + run: mvn -batch-mode --no-transfer-progress clean install working-directory: ./powsybl-dynawo - name: Get DYNAWO_VERSION run: echo "DYNAWO_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV working-directory: ./powsybl-dynawo - shell: bash #CHECKOUT_PYPOWSYBL_DEPENCIES - - name: Checkout powsybl-dependencies + - name: Checkout powsybl-dependencies sources uses: actions/checkout@v4 with: repository: powsybl/powsybl-dependencies @@ -208,11 +189,9 @@ jobs: - name: Get DEPENDENCIES_VERSION run: echo "DEPENDENCIES_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV working-directory: ./powsybl-dependencies - shell: bash #UPDATE/INSTALL_PYPOWSYBL_DEPENCIES - name: Update dependencies versions - shell: bash run: | mvn versions:set-property -Dproperty=powsybl-open-loadflow.version -DnewVersion=$LOADFLOW_VERSION -DgenerateBackupPoms=false mvn versions:set-property -Dproperty=powsybl-core.version -DnewVersion=$CORE_VERSION -DgenerateBackupPoms=false @@ -223,23 +202,29 @@ jobs: working-directory: ./powsybl-dependencies - name: Install powsybl-dependencies - run: mvn -batch-mode --no-transfer-progress clean install -DskipTests + run: mvn -batch-mode --no-transfer-progress clean install working-directory: ./powsybl-dependencies - shell: bash #BUILD PYPOWSYBL + - name: Checking dor pypowsybl snapshot branch + run: ${{ env.SCRIPTS_PATH }}/check_snapshot_branch.sh "https://github.com/powsybl/pypowsybl.git" ${{ env.CORE_VERSION }} + - name: Checkout pypowsybl + uses: actions/checkout@v4 + with: + repository: powsybl/pypowsybl + ref: ${{ env.SNAPSHOT_BRANCH }} + path: pypowsybl + submodules: true - name: update java/pom.xml - shell: bash run: mvn versions:set-property -Dproperty=powsybl-dependencies.version -DnewVersion=$DEPENDENCIES_VERSION -DgenerateBackupPoms=false working-directory: ./pypowsybl/java - - name: Install dependencies + - name: Install requirement.txt run: pip3 install -r requirements.txt working-directory: ./pypowsybl - name: Build wheel run: python3 setup.py bdist_wheel working-directory: ./pypowsybl - name: Install wheel - shell: bash run: python -m pip install dist/*.whl --user working-directory: ./pypowsybl - name: check pypowsybl versions @@ -249,26 +234,7 @@ jobs: working-directory: ./pypowsybl/tests run: pytest - name: Upload wheel - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: pypowsybl-wheel-${{ matrix.config.name }}-${{ matrix.python.name }} path: dist/*.whl - - #SLACK NOTIFICATION ON FAILURE - - name: Slack Notification - uses: 8398a7/action-slack@v3 - if: failure() - with: - status: ${{ job.status }} - fields: repo,message,commit,author,action,eventName,ref,workflow - text: | - Repository: ${{ github.repository }} - Workflow: ${{ github.workflow }} - Job: ${{ github.job }} - Status: ${{ job.status }} - Event: ${{ github.event_name }} - Commit: ${{ github.sha }} - Branch: ${{ github.ref }} - Author: ${{ github.actor }} - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.pylintrc b/.pylintrc index 983b18f9e6..c2e4594025 100644 --- a/.pylintrc +++ b/.pylintrc @@ -582,5 +582,5 @@ preferred-modules= # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception +overgeneral-exceptions=builtins.BaseException, + builtins.Exception diff --git a/README.md b/README.md index 58baeac319..492d426133 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # PyPowSyBl -[![Actions Status](https://github.com/powsybl/pypowsybl/workflows/CI/badge.svg)](https://github.com/powsybl/pypowsybl/actions) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=powsybl_pypowsybl&metric=alert_status)](https://sonarcloud.io/dashboard?id=powsybl_pypowsybl) -[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=powsybl_pypowsybl&metric=coverage)](https://sonarcloud.io/dashboard?id=powsybl_pypowsybl) [![PyPI Latest Release](https://img.shields.io/pypi/v/pypowsybl.svg)](https://pypi.org/project/pypowsybl/) [![Documentation Status](https://readthedocs.org/projects/pypowsybl/badge/?version=latest)](https://pypowsybl.readthedocs.io/en/latest/?badge=latest) [![MPL-2.0 License](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](https://www.mozilla.org/en-US/MPL/2.0/) [![Slack](https://img.shields.io/badge/slack-powsybl-blueviolet.svg?logo=slack)](https://join.slack.com/t/powsybl/shared_invite/zt-rzvbuzjk-nxi0boim1RKPS5PjieI0rA) +[![Actions Status](https://github.com/powsybl/pypowsybl/workflows/Full%20CI/badge.svg)](https://github.com/powsybl/pypowsybl/actions) + +Python code quality : +[![Quality Gate Status (python)](https://sonarcloud.io/api/project_badges/measure?project=powsybl_pypowsybl&metric=alert_status)](https://sonarcloud.io/dashboard?id=powsybl_pypowsybl) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=powsybl_pypowsybl&metric=coverage)](https://sonarcloud.io/dashboard?id=powsybl_pypowsybl) + +Java code quality : +[![Quality Gate Status (java)](https://sonarcloud.io/api/project_badges/measure?project=powsybl_pypowsybl-java&metric=alert_status)](https://sonarcloud.io/dashboard?id=powsybl_pypowsybl-java) The PyPowSyBl project gives access PowSyBl Java framework to Python developers. This Python integration relies on GraalVM to compile Java code to a native library. diff --git a/cpp/powsybl-cpp/powsybl-cpp.cpp b/cpp/powsybl-cpp/powsybl-cpp.cpp index 2ab022301d..f934e8a61b 100644 --- a/cpp/powsybl-cpp/powsybl-cpp.cpp +++ b/cpp/powsybl-cpp/powsybl-cpp.cpp @@ -485,6 +485,18 @@ std::vector getNetworkExportFormats() { return formats.get(); } +std::vector getNetworkImportPostProcessors() { + auto postProcessorsArrayPtr = PowsyblCaller::get()->callJava(::getNetworkImportPostProcessors); + ToStringVector postProcessors(postProcessorsArrayPtr); + return postProcessors.get(); +} + +std::vector getNetworkImportSupportedExtensions() { + auto supportedExtensionsArrayPtr = PowsyblCaller::get()->callJava(::getNetworkImportSupportedExtensions); + ToStringVector supportedExtensions(supportedExtensionsArrayPtr); + return supportedExtensions.get(); +} + std::vector getLoadFlowProviderNames() { auto formatsArrayPtr = PowsyblCaller::get()->callJava(::getLoadFlowProviderNames); ToStringVector formats(formatsArrayPtr); @@ -524,7 +536,7 @@ std::shared_ptr getNetworkMetadata(const JavaHandle& network) }); } -JavaHandle loadNetwork(const std::string& file, const std::map& parameters, JavaHandle* reportNode) { +JavaHandle loadNetwork(const std::string& file, const std::map& parameters, const std::vector& postProcessors, JavaHandle* reportNode) { std::vector parameterNames; std::vector parameterValues; parameterNames.reserve(parameters.size()); @@ -535,11 +547,13 @@ JavaHandle loadNetwork(const std::string& file, const std::mapcallJava(::loadNetwork, (char*) file.data(), parameterNamesPtr.get(), parameterNames.size(), - parameterValuesPtr.get(), parameterValues.size(), (reportNode == nullptr) ? nullptr : *reportNode); + parameterValuesPtr.get(), parameterValues.size(), postProcessorsPtr.get(), postProcessors.size(), + (reportNode == nullptr) ? nullptr : *reportNode); } -JavaHandle loadNetworkFromString(const std::string& fileName, const std::string& fileContent, const std::map& parameters, JavaHandle* reportNode) { +JavaHandle loadNetworkFromString(const std::string& fileName, const std::string& fileContent, const std::map& parameters, const std::vector& postProcessors, JavaHandle* reportNode) { std::vector parameterNames; std::vector parameterValues; parameterNames.reserve(parameters.size()); @@ -550,9 +564,10 @@ JavaHandle loadNetworkFromString(const std::string& fileName, const std::string& } ToCharPtrPtr parameterNamesPtr(parameterNames); ToCharPtrPtr parameterValuesPtr(parameterValues); + ToCharPtrPtr postProcessorsPtr(postProcessors); return PowsyblCaller::get()->callJava(::loadNetworkFromString, (char*) fileName.data(), (char*) fileContent.data(), - parameterNamesPtr.get(), parameterNames.size(), - parameterValuesPtr.get(), parameterValues.size(), (reportNode == nullptr) ? nullptr : *reportNode); + parameterNamesPtr.get(), parameterNames.size(), parameterValuesPtr.get(), parameterValues.size(), + postProcessorsPtr.get(), postProcessors.size(), (reportNode == nullptr) ? nullptr : *reportNode); } void saveNetwork(const JavaHandle& network, const std::string& file, const std::string& format, const std::map& parameters, JavaHandle* reportNode) { @@ -707,10 +722,11 @@ std::vector getMatrixMultiSubstationSvgAndMetadata(const JavaHandle return svgAndMetadata.get(); } -void writeNetworkAreaDiagramSvg(const JavaHandle& network, const std::string& svgFile, const std::vector& voltageLevelIds, int depth, double highNominalVoltageBound, double lowNominalVoltageBound, const NadParameters& parameters) { +void writeNetworkAreaDiagramSvg(const JavaHandle& network, const std::string& svgFile, const std::string& metadataFile, const std::vector& voltageLevelIds, int depth, double highNominalVoltageBound, double lowNominalVoltageBound, const NadParameters& parameters) { auto c_parameters = parameters.to_c_struct(); ToCharPtrPtr voltageLevelIdPtr(voltageLevelIds); - PowsyblCaller::get()->callJava(::writeNetworkAreaDiagramSvg, network, (char*) svgFile.data(), voltageLevelIdPtr.get(), voltageLevelIds.size(), depth, highNominalVoltageBound, lowNominalVoltageBound, c_parameters.get()); + PowsyblCaller::get()->callJava(::writeNetworkAreaDiagramSvg, network, (char*) svgFile.data(), (char*) metadataFile.data(), + voltageLevelIdPtr.get(), voltageLevelIds.size(), depth, highNominalVoltageBound, lowNominalVoltageBound, c_parameters.get()); } std::string getNetworkAreaDiagramSvg(const JavaHandle& network, const std::vector& voltageLevelIds, int depth, double highNominalVoltageBound, double lowNominalVoltageBound, const NadParameters& parameters) { @@ -719,6 +735,14 @@ std::string getNetworkAreaDiagramSvg(const JavaHandle& network, const std::vecto return toString(PowsyblCaller::get()->callJava(::getNetworkAreaDiagramSvg, network, voltageLevelIdPtr.get(), voltageLevelIds.size(), depth, highNominalVoltageBound, lowNominalVoltageBound, c_parameters.get())); } +std::vector getNetworkAreaDiagramSvgAndMetadata(const JavaHandle& network, const std::vector& voltageLevelIds, int depth, double highNominalVoltageBound, double lowNominalVoltageBound, const NadParameters& parameters) { + auto c_parameters = parameters.to_c_struct(); + ToCharPtrPtr voltageLevelIdPtr(voltageLevelIds); + auto svgAndMetadataArrayPtr = PowsyblCaller::get()->callJava(::getNetworkAreaDiagramSvgAndMetadata, network, voltageLevelIdPtr.get(), voltageLevelIds.size(), depth, highNominalVoltageBound, lowNominalVoltageBound, c_parameters.get()); + ToStringVector svgAndMetadata(svgAndMetadataArrayPtr); + return svgAndMetadata.get(); +} + std::vector getNetworkAreaDiagramDisplayedVoltageLevels(const JavaHandle& network, const std::vector& voltageLevelIds, int depth) { ToCharPtrPtr voltageLevelIdPtr(voltageLevelIds); auto displayedVoltageLevelIdsArrayPtr = PowsyblCaller::get()->callJava(::getNetworkAreaDiagramDisplayedVoltageLevels, network, voltageLevelIdPtr.get(), voltageLevelIds.size(), depth); @@ -1187,6 +1211,10 @@ void removeAliases(pypowsybl::JavaHandle network, dataframe* dataframe) { pypowsybl::PowsyblCaller::get()->callJava(::removeAliases, network, dataframe); } +void removeInternalConnections(pypowsybl::JavaHandle network, dataframe* dataframe) { + pypowsybl::PowsyblCaller::get()->callJava(::removeInternalConnections, network, dataframe); +} + void closePypowsybl() { pypowsybl::PowsyblCaller::get()->callJava(::closePypowsybl); } @@ -1292,7 +1320,7 @@ void removeElementsModification(pypowsybl::JavaHandle network, const std::vector pypowsybl::PowsyblCaller::get()->callJava(::removeElementsModification, network, connectableIdsPtr.get(), connectableIds.size(), dataframe, removeModificationType, throwException, (reportNode == nullptr) ? nullptr : *reportNode); } -/*---------------------------------DYNAMIC MODELLING WITH DYNAWALTZ---------------------------*/ +/*---------------------------------DYNAMIC MODELLING WITH DYNAWO---------------------------*/ JavaHandle createDynamicSimulationContext() { return PowsyblCaller::get()->callJava(::createDynamicSimulationContext); } @@ -1309,20 +1337,20 @@ JavaHandle createEventMapping() { return PowsyblCaller::get()->callJava(::createEventMapping); } -JavaHandle runDynamicModel(JavaHandle dynamicModelContext, JavaHandle network, JavaHandle dynamicMapping, JavaHandle eventMapping, JavaHandle timeSeriesMapping, int start, int stop) { - return PowsyblCaller::get()->callJava(::runDynamicModel, dynamicModelContext, network, dynamicMapping, eventMapping, timeSeriesMapping, start, stop); +JavaHandle runDynamicModel(JavaHandle dynamicModelContext, JavaHandle network, JavaHandle dynamicMapping, JavaHandle eventMapping, JavaHandle timeSeriesMapping, int start, int stop, JavaHandle reportNode) { + return PowsyblCaller::get()->callJava(::runDynamicModel, dynamicModelContext, network, dynamicMapping, eventMapping, timeSeriesMapping, start, stop, reportNode); } -void addDynamicMappings(JavaHandle dynamicMappingHandle, DynamicMappingType mappingType, dataframe* mappingDf) { - PowsyblCaller::get()->callJava<>(::addDynamicMappings, dynamicMappingHandle, mappingType, mappingDf); +void addDynamicMappings(JavaHandle dynamicMappingHandle, DynamicMappingType mappingType, dataframe_array* dataframes) { + PowsyblCaller::get()->callJava<>(::addDynamicMappings, dynamicMappingHandle, mappingType, dataframes); } -void addCurve(JavaHandle curveMappingHandle, std::string dynamicId, std::string variable) { - PowsyblCaller::get()->callJava<>(::addCurve, curveMappingHandle, (char*) dynamicId.c_str(), (char*) variable.c_str()); +void addEventMappings(JavaHandle eventMappingHandle, EventMappingType mappingType, dataframe* mappingDf) { + PowsyblCaller::get()->callJava<>(::addEventMappings, eventMappingHandle, mappingType, mappingDf); } -void addEventDisconnection(const JavaHandle& eventMappingHandle, const std::string& staticId, double eventTime, int disconnectOnly) { - PowsyblCaller::get()->callJava<>(::addEventDisconnection, eventMappingHandle, (char*) staticId.c_str(), eventTime, disconnectOnly); +void addCurve(JavaHandle curveMappingHandle, std::string dynamicId, std::string variable) { + PowsyblCaller::get()->callJava<>(::addCurve, curveMappingHandle, (char*) dynamicId.c_str(), (char*) variable.c_str()); } std::string getDynamicSimulationResultsStatus(JavaHandle dynamicSimulationResultsHandle) { @@ -1338,12 +1366,27 @@ std::vector getAllDynamicCurvesIds(JavaHandle resultHandle) { return vector.get(); } -std::vector getDynamicMappingsMetaData(DynamicMappingType mappingType) { - dataframe_metadata* metadata = pypowsybl::PowsyblCaller::get()->callJava(::getDynamicMappingsMetaData, mappingType); +std::vector getSupportedModels(DynamicMappingType mappingType) { + ToStringVector vector(PowsyblCaller::get()->callJava(::getSupportedModels, mappingType)); + return vector.get(); +} + +std::vector> getDynamicMappingsMetaData(DynamicMappingType mappingType) { + dataframes_metadata* metadata = pypowsybl::PowsyblCaller::get()->callJava(::getDynamicMappingsMetaData, mappingType); + std::vector> res; + for (int i =0; i < metadata->dataframes_count; i++) { + res.push_back(convertDataframeMetadata(metadata->dataframes_metadata + i)); + } + pypowsybl::PowsyblCaller::get()->callJava(::freeDataframesMetadata, metadata); + return res; +} + +std::vector getEventMappingsMetaData(EventMappingType mappingType) { + dataframe_metadata* metadata = pypowsybl::PowsyblCaller::get()->callJava(::getEventMappingsMetaData, mappingType); std::vector res = convertDataframeMetadata(metadata); PowsyblCaller::get()->callJava(::freeDataframeMetadata, metadata); return res; - } +} std::vector getModificationMetadata(network_modification_type networkModificationType) { dataframe_metadata* metadata = pypowsybl::PowsyblCaller::get()->callJava(::getModificationMetadata, networkModificationType); diff --git a/cpp/powsybl-cpp/powsybl-cpp.h b/cpp/powsybl-cpp/powsybl-cpp.h index 5bfe81f46c..d953435c9d 100644 --- a/cpp/powsybl-cpp/powsybl-cpp.h +++ b/cpp/powsybl-cpp/powsybl-cpp.h @@ -370,7 +370,8 @@ class SensitivityAnalysisParameters { enum class RescaleMode { NONE = 0, ACER_METHODOLOGY, - PROPORTIONAL + PROPORTIONAL, + MAX_CURRENT_OVERLOAD }; class FlowDecompositionParameters { @@ -446,7 +447,9 @@ enum ShortCircuitStudyType { enum InitialVoltageProfileMode { NOMINAL = 0, - PREVIOUS_VALUE + // The enum is incomplete, the last case (CONFIGURED) will be done later. + // For now the value of PREVIOUS_VALUE must be specified. + PREVIOUS_VALUE = 2 }; class ShortCircuitAnalysisParameters { @@ -516,8 +519,12 @@ std::vector getNetworkElementsIds(const JavaHandle& network, elemen std::vector getNetworkImportFormats(); +std::vector getNetworkImportSupportedExtensions(); + std::vector getNetworkExportFormats(); +std::vector getNetworkImportPostProcessors(); + std::vector getLoadFlowProviderNames(); std::vector getSecurityAnalysisProviderNames(); @@ -530,9 +537,9 @@ SeriesArray* createExporterParametersSeriesArray(const std::string& format); std::shared_ptr getNetworkMetadata(const JavaHandle& network); -JavaHandle loadNetwork(const std::string& file, const std::map& parameters, JavaHandle* reportNode); +JavaHandle loadNetwork(const std::string& file, const std::map& parameters, const std::vector& postProcessors, JavaHandle* reportNode); -JavaHandle loadNetworkFromString(const std::string& fileName, const std::string& fileContent, const std::map& parameters, JavaHandle* reportNode); +JavaHandle loadNetworkFromString(const std::string& fileName, const std::string& fileContent, const std::map& parameters, const std::vector& postProcessors, JavaHandle* reportNode); void saveNetwork(const JavaHandle& network, const std::string& file, const std::string& format, const std::map& parameters, JavaHandle* reportNode); @@ -572,10 +579,12 @@ std::vector getMatrixMultiSubstationSvgAndMetadata(const JavaHandle std::vector getSingleLineDiagramComponentLibraryNames(); -void writeNetworkAreaDiagramSvg(const JavaHandle& network, const std::string& svgFile, const std::vector& voltageLevelIds, int depth, double highNominalVoltageBound, double lowNominalVoltageBound, const NadParameters& parameters); +void writeNetworkAreaDiagramSvg(const JavaHandle& network, const std::string& svgFile, const std::string& metadataFile, const std::vector& voltageLevelIds, int depth, double highNominalVoltageBound, double lowNominalVoltageBound, const NadParameters& parameters); std::string getNetworkAreaDiagramSvg(const JavaHandle& network, const std::vector& voltageLevelIds, int depth, double highNominalVoltageBound, double lowNominalVoltageBound, const NadParameters& parameters); +std::vector getNetworkAreaDiagramSvgAndMetadata(const JavaHandle& network, const std::vector& voltageLevelIds, int depth, double highNominalVoltageBound, double lowNominalVoltageBound, const NadParameters& parameters); + std::vector getNetworkAreaDiagramDisplayedVoltageLevels(const JavaHandle& network, const std::vector& voltageLevelIds, int depth); JavaHandle createSecurityAnalysis(); @@ -739,6 +748,8 @@ std::vector getUnusedConnectableOrderPositions(pypowsybl::JavaHandle networ void removeAliases(pypowsybl::JavaHandle network, dataframe* dataframe); +void removeInternalConnections(pypowsybl::JavaHandle network, dataframe* dataframe); + void closePypowsybl(); void removeElementsModification(pypowsybl::JavaHandle network, const std::vector& connectableIds, dataframe* dataframe, remove_modification_type removeModificationType, bool throwException, JavaHandle* reportNode); @@ -747,7 +758,7 @@ SldParameters* createSldParameters(); NadParameters* createNadParameters(); -//=======dynamic modeling for dynawaltz package========== +//=======dynamic modeling for dynawo package========== //handle creation JavaHandle createDynamicSimulationContext(); @@ -755,24 +766,26 @@ JavaHandle createDynamicModelMapping(); JavaHandle createTimeseriesMapping(); JavaHandle createEventMapping(); -JavaHandle runDynamicModel(JavaHandle dynamicModelContext, JavaHandle network, JavaHandle dynamicMapping, JavaHandle eventMapping, JavaHandle timeSeriesMapping, int start, int stop); +JavaHandle runDynamicModel(JavaHandle dynamicModelContext, JavaHandle network, JavaHandle dynamicMapping, JavaHandle eventMapping, JavaHandle timeSeriesMapping, int start, int stop, JavaHandle reportNode); // timeseries/curves mapping void addCurve(JavaHandle curveMappingHandle, std::string dynamicId, std::string variable); // events mapping -void addEventDisconnection(const JavaHandle& eventMappingHandle, const std::string& staticId, double eventTime, int disconnectOnly); +void addEventMappings(JavaHandle eventMappingHandle, EventMappingType mappingType, dataframe* mappingDf); +std::vector getEventMappingsMetaData(EventMappingType mappingType); // dynamic model mapping -void addDynamicMappings(JavaHandle dynamicMappingHandle, DynamicMappingType mappingType, dataframe* mappingDf); -std::vector getDynamicMappingsMetaData(DynamicMappingType mappingType); +void addDynamicMappings(JavaHandle dynamicMappingHandle, DynamicMappingType mappingType, dataframe_array* dataframes); +std::vector> getDynamicMappingsMetaData(DynamicMappingType mappingType); +std::vector getSupportedModels(DynamicMappingType mappingType); // results std::string getDynamicSimulationResultsStatus(JavaHandle dynamicSimulationResultsHandle); SeriesArray* getDynamicCurve(JavaHandle resultHandle, std::string curveName); std::vector getAllDynamicCurvesIds(JavaHandle resultHandle); -//=======END OF dynamic modeling for dynawaltz package========== +//=======END OF dynamic modeling for dynawo package========== //=======Voltage initializer mapping======== diff --git a/cpp/pypowsybl-cpp/bindings.cpp b/cpp/pypowsybl-cpp/bindings.cpp index 56c1d1b5e0..3923753bc1 100644 --- a/cpp/pypowsybl-cpp/bindings.cpp +++ b/cpp/pypowsybl-cpp/bindings.cpp @@ -22,7 +22,7 @@ namespace py = pybind11; //Explicitly update log level on java side void setLogLevelFromPythonLogger(pypowsybl::GraalVmGuard* guard, exception_handler* exc); -pypowsybl::JavaHandle loadNetworkFromBinaryBuffersPython(std::vector byteBuffers, const std::map& parameters, pypowsybl::JavaHandle* reportNode); +pypowsybl::JavaHandle loadNetworkFromBinaryBuffersPython(std::vector byteBuffers, const std::map& parameters, const std::vector& postProcessors, pypowsybl::JavaHandle* reportNode); py::bytes saveNetworkToBinaryBufferPython(const pypowsybl::JavaHandle& network, const std::string& format, const std::map& parameters, pypowsybl::JavaHandle* reportNode); @@ -134,6 +134,11 @@ void createExtensionsBind(pypowsybl::JavaHandle network, const std::vector& dataframes) { + std::shared_ptr dataframeArray = ::createDataframeArray(dataframes); + pypowsybl::addDynamicMappings(dynamic_mapping_handle, mapping_type, dataframeArray.get()); +} + template py::array seriesAsNumpyArray(const series& series) { //Last argument is to bind lifetime of series to the returned array @@ -143,14 +148,37 @@ py::array seriesAsNumpyArray(const series& series) { void dynamicSimulationBindings(py::module_& m) { py::enum_(m, "DynamicMappingType") - .value("ALPHA_BETA_LOAD", DynamicMappingType::ALPHA_BETA_LOAD) - .value("ONE_TRANSFORMER_LOAD", DynamicMappingType::ONE_TRANSFORMER_LOAD) - .value("GENERATOR_SYNCHRONOUS_THREE_WINDINGS", DynamicMappingType::GENERATOR_SYNCHRONOUS_THREE_WINDINGS) - .value("GENERATOR_SYNCHRONOUS_THREE_WINDINGS_PROPORTIONAL_REGULATIONS", DynamicMappingType::GENERATOR_SYNCHRONOUS_THREE_WINDINGS_PROPORTIONAL_REGULATIONS) - .value("GENERATOR_SYNCHRONOUS_FOUR_WINDINGS", DynamicMappingType::GENERATOR_SYNCHRONOUS_FOUR_WINDINGS) - .value("GENERATOR_SYNCHRONOUS_FOUR_WINDINGS_PROPORTIONAL_REGULATIONS", DynamicMappingType::GENERATOR_SYNCHRONOUS_FOUR_WINDINGS_PROPORTIONAL_REGULATIONS) - .value("GENERATOR_SYNCHRONOUS", DynamicMappingType::GENERATOR_SYNCHRONOUS) - .value("CURRENT_LIMIT_AUTOMATON", DynamicMappingType::CURRENT_LIMIT_AUTOMATON); + .value("BASE_LOAD", DynamicMappingType::BASE_LOAD) + .value("LOAD_ONE_TRANSFORMER", DynamicMappingType::LOAD_ONE_TRANSFORMER) + .value("LOAD_ONE_TRANSFORMER_TAP_CHANGER", DynamicMappingType::LOAD_ONE_TRANSFORMER_TAP_CHANGER) + .value("LOAD_TWO_TRANSFORMERS", DynamicMappingType::LOAD_TWO_TRANSFORMERS) + .value("LOAD_TWO_TRANSFORMERS_TAP_CHANGERS", DynamicMappingType::LOAD_TWO_TRANSFORMERS_TAP_CHANGERS) + .value("BASE_GENERATOR", DynamicMappingType::BASE_GENERATOR) + .value("SYNCHRONIZED_GENERATOR", DynamicMappingType::SYNCHRONIZED_GENERATOR) + .value("SYNCHRONOUS_GENERATOR", DynamicMappingType::SYNCHRONOUS_GENERATOR) + .value("WECC", DynamicMappingType::WECC) + .value("GRID_FORMING_CONVERTER", DynamicMappingType::GRID_FORMING_CONVERTER) + .value("SIGNAL_N_GENERATOR", DynamicMappingType::SIGNAL_N_GENERATOR) + .value("HVDC_P", DynamicMappingType::HVDC_P) + .value("HVDC_VSC", DynamicMappingType::HVDC_VSC) + .value("BASE_TRANSFORMER", DynamicMappingType::BASE_TRANSFORMER) + .value("BASE_STATIC_VAR_COMPENSATOR", DynamicMappingType::BASE_STATIC_VAR_COMPENSATOR) + .value("BASE_LINE", DynamicMappingType::BASE_LINE) + .value("BASE_BUS", DynamicMappingType::BASE_BUS) + .value("INFINITE_BUS", DynamicMappingType::INFINITE_BUS) + .value("OVERLOAD_MANAGEMENT_SYSTEM", DynamicMappingType::OVERLOAD_MANAGEMENT_SYSTEM) + .value("TWO_LEVELS_OVERLOAD_MANAGEMENT_SYSTEM", DynamicMappingType::TWO_LEVELS_OVERLOAD_MANAGEMENT_SYSTEM) + .value("UNDER_VOLTAGE", DynamicMappingType::UNDER_VOLTAGE) + .value("PHASE_SHIFTER_I", DynamicMappingType::PHASE_SHIFTER_I) + .value("PHASE_SHIFTER_P", DynamicMappingType::PHASE_SHIFTER_P) + .value("PHASE_SHIFTER_BLOCKING_I", DynamicMappingType::PHASE_SHIFTER_BLOCKING_I) + .value("TAP_CHANGER", DynamicMappingType::TAP_CHANGER) + .value("TAP_CHANGER_BLOCKING", DynamicMappingType::TAP_CHANGER_BLOCKING); + + py::enum_(m, "EventMappingType") + .value("DISCONNECT", EventMappingType::DISCONNECT) + .value("NODE_FAULT", EventMappingType::NODE_FAULT) + .value("ACTIVE_POWER_VARIATION", EventMappingType::ACTIVE_POWER_VARIATION); //entrypoints for constructors m.def("create_dynamic_simulation_context", &pypowsybl::createDynamicSimulationContext); @@ -160,17 +188,19 @@ void dynamicSimulationBindings(py::module_& m) { //running simulations m.def("run_dynamic_model", &pypowsybl::runDynamicModel, py::call_guard(), - py::arg("dynamic_model"), py::arg("network"), py::arg("dynamic_mapping"), py::arg("event_mapping"), py::arg("timeseries_mapping"), py::arg("start"), py::arg("stop")); + py::arg("dynamic_model"), py::arg("network"), py::arg("dynamic_mapping"), py::arg("event_mapping"), py::arg("timeseries_mapping"), py::arg("start"), py::arg("stop"), py::arg("report_node")); //model mapping - m.def("add_all_dynamic_mappings", &pypowsybl::addDynamicMappings, py::arg("dynamic_mapping_handle"), py::arg("mapping_type"), py::arg("mapping_df")); + m.def("add_all_dynamic_mappings", ::addDynamicMappingsBind, py::arg("dynamic_mapping_handle"), py::arg("mapping_type"), py::arg("dataframes")); m.def("get_dynamic_mappings_meta_data", &pypowsybl::getDynamicMappingsMetaData, py::arg("mapping_type")); + m.def("get_supported_models", &pypowsybl::getSupportedModels, py::arg("mapping_type")); // timeseries/curves mapping m.def("add_curve", &pypowsybl::addCurve, py::arg("curve_mapping_handle"), py::arg("dynamic_id"), py::arg("variable")); // events mapping - m.def("add_event_disconnection", &pypowsybl::addEventDisconnection, py::arg("event_mapping_handle"), py::arg("static_id"), py::arg("eventTime"), py::arg("disconnectOnly")); + m.def("add_all_event_mappings", &pypowsybl::addEventMappings, py::arg("event_mapping_handle"), py::arg("mapping_type"), py::arg("mapping_df")); + m.def("get_event_mappings_meta_data", &pypowsybl::getEventMappingsMetaData, py::arg("mapping_type")); // Simulation results m.def("get_dynamic_simulation_results_status", &pypowsybl::getDynamicSimulationResultsStatus, py::arg("result_handle")); @@ -238,7 +268,7 @@ void voltageInitializerBinding(py::module_& m) { m.def("voltage_initializer_set_low_active_power_default_limit", &pypowsybl::voltageInitializerSetLowActivePowerDefaultLimit, py::arg("params_handle"), py::arg("low_active_power_default_limit")); m.def("voltage_initializer_set_default_minimal_qp_range", &pypowsybl::voltageInitializerSetDefaultMinimalQPRange, py::arg("params_handle"), py::arg("default_minimal_qp_range")); m.def("voltage_initializer_set_default_qmax_pmax_ratio", &pypowsybl::voltageInitializerSetDefaultQmaxPmaxRatio, py::arg("params_handle"), py::arg("default_qmax_pmax_ratio")); - + m.def("voltage_initializer_apply_all_modifications", &pypowsybl::voltageInitializerApplyAllModifications, py::arg("result_handle"), py::arg("network_handle")); m.def("voltage_initializer_get_status", &pypowsybl::voltageInitializerGetStatus, py::arg("result_handle")); m.def("voltage_initializer_get_indicators", &pypowsybl::voltageInitializerGetIndicators, py::arg("result_handle")); @@ -301,11 +331,13 @@ PYBIND11_MODULE(_pypowsybl, m) { .value("THREE_WINDINGS_TRANSFORMER", element_type::THREE_WINDINGS_TRANSFORMER) .value("GENERATOR", element_type::GENERATOR) .value("LOAD", element_type::LOAD) + .value("GROUND", element_type::GROUND) .value("BATTERY", element_type::BATTERY) .value("SHUNT_COMPENSATOR", element_type::SHUNT_COMPENSATOR) .value("NON_LINEAR_SHUNT_COMPENSATOR_SECTION", element_type::NON_LINEAR_SHUNT_COMPENSATOR_SECTION) .value("LINEAR_SHUNT_COMPENSATOR_SECTION", element_type::LINEAR_SHUNT_COMPENSATOR_SECTION) .value("DANGLING_LINE", element_type::DANGLING_LINE) + .value("DANGLING_LINE_GENERATION", element_type::DANGLING_LINE_GENERATION) .value("TIE_LINE", element_type::TIE_LINE) .value("LCC_CONVERTER_STATION", element_type::LCC_CONVERTER_STATION) .value("VSC_CONVERTER_STATION", element_type::VSC_CONVERTER_STATION) @@ -321,13 +353,18 @@ PYBIND11_MODULE(_pypowsybl, m) { .value("PHASE_TAP_CHANGER", element_type::PHASE_TAP_CHANGER) .value("REACTIVE_CAPABILITY_CURVE_POINT", element_type::REACTIVE_CAPABILITY_CURVE_POINT) .value("OPERATIONAL_LIMITS", element_type::OPERATIONAL_LIMITS) + .value("SELECTED_OPERATIONAL_LIMITS", element_type::SELECTED_OPERATIONAL_LIMITS) .value("MINMAX_REACTIVE_LIMITS", element_type::MINMAX_REACTIVE_LIMITS) .value("ALIAS", element_type::ALIAS) .value("IDENTIFIABLE", element_type::IDENTIFIABLE) .value("INJECTION", element_type::INJECTION) .value("BRANCH", element_type::BRANCH) .value("TERMINAL", element_type::TERMINAL) - .value("SUB_NETWORK", element_type::SUB_NETWORK); + .value("SUB_NETWORK", element_type::SUB_NETWORK) + .value("AREA", element_type::AREA) + .value("AREA_VOLTAGE_LEVELS", element_type::AREA_VOLTAGE_LEVELS) + .value("AREA_BOUNDARIES", element_type::AREA_BOUNDARIES) + .value("INTERNAL_CONNECTION", element_type::INTERNAL_CONNECTION); py::enum_(m, "FilterAttributesType") .value("ALL_ATTRIBUTES", filter_attributes_type::ALL_ATTRIBUTES) @@ -366,8 +403,9 @@ PYBIND11_MODULE(_pypowsybl, m) { py::arg("not_connected_to_same_bus_at_both_sides")); m.def("get_network_import_formats", &pypowsybl::getNetworkImportFormats, "Get supported import formats"); + m.def("get_network_import_supported_extensions", &pypowsybl::getNetworkImportSupportedExtensions, "Get supported import extensions"); m.def("get_network_export_formats", &pypowsybl::getNetworkExportFormats, "Get supported export formats"); - + m.def("get_network_import_post_processors", &pypowsybl::getNetworkImportPostProcessors, "Get supported import post processors"); m.def("get_loadflow_provider_names", &pypowsybl::getLoadFlowProviderNames, "Get supported loadflow providers"); m.def("get_security_analysis_provider_names", &pypowsybl::getSecurityAnalysisProviderNames, "Get supported security analysis providers"); @@ -380,13 +418,13 @@ PYBIND11_MODULE(_pypowsybl, m) { py::arg("format")); m.def("load_network", &pypowsybl::loadNetwork, "Load a network from a file", py::call_guard(), - py::arg("file"), py::arg("parameters"), py::arg("report_node")); + py::arg("file"), py::arg("parameters"), py::arg("post_processors"), py::arg("report_node")); m.def("load_network_from_string", &pypowsybl::loadNetworkFromString, "Load a network from a string", py::call_guard(), - py::arg("file_name"), py::arg("file_content"),py::arg("parameters"), py::arg("report_node")); + py::arg("file_name"), py::arg("file_content"),py::arg("parameters"), py::arg("post_processors"), py::arg("report_node")); m.def("load_network_from_binary_buffers", ::loadNetworkFromBinaryBuffersPython, "Load a network from a list of binary buffer", py::call_guard(), - py::arg("buffers"), py::arg("parameters"), py::arg("report_node")); + py::arg("buffers"), py::arg("parameters"), py::arg("post_processors"), py::arg("report_node")); m.def("save_network", &pypowsybl::saveNetwork, "Save network to a file in a given format", py::call_guard(), py::arg("network"), py::arg("file"),py::arg("format"), py::arg("parameters"), py::arg("report_node")); @@ -596,10 +634,14 @@ PYBIND11_MODULE(_pypowsybl, m) { m.def("get_single_line_diagram_component_library_names", &pypowsybl::getSingleLineDiagramComponentLibraryNames, "Get supported component library providers for single line diagram"); m.def("write_network_area_diagram_svg", &pypowsybl::writeNetworkAreaDiagramSvg, "Write network area diagram SVG", - py::arg("network"), py::arg("svg_file"), py::arg("voltage_level_ids"), py::arg("depth"), py::arg("high_nominal_voltage_bound"), py::arg("low_nominal_voltage_bound"), py::arg("nad_parameters")); + py::arg("network"), py::arg("svg_file"), py::arg("metadata_file"), py::arg("voltage_level_ids"), + py::arg("depth"), py::arg("high_nominal_voltage_bound"), py::arg("low_nominal_voltage_bound"), py::arg("nad_parameters")); m.def("get_network_area_diagram_svg", &pypowsybl::getNetworkAreaDiagramSvg, "Get network area diagram SVG as a string", py::arg("network"), py::arg("voltage_level_ids"), py::arg("depth"), py::arg("high_nominal_voltage_bound"), py::arg("low_nominal_voltage_bound"), py::arg("nad_parameters")); + + m.def("get_network_area_diagram_svg_and_metadata", &pypowsybl::getNetworkAreaDiagramSvgAndMetadata, "Get network area diagram SVG and its metadata as a list of strings", + py::arg("network"), py::arg("voltage_level_ids"), py::arg("depth"), py::arg("high_nominal_voltage_bound"), py::arg("low_nominal_voltage_bound"), py::arg("nad_parameters")); m.def("get_network_area_diagram_displayed_voltage_levels", &pypowsybl::getNetworkAreaDiagramDisplayedVoltageLevels, "Get network area diagram displayed voltage level", py::arg("network"), py::arg("voltage_level_ids"), py::arg("depth")); @@ -966,7 +1008,8 @@ PYBIND11_MODULE(_pypowsybl, m) { py::enum_(m, "RescaleMode") .value("NONE", pypowsybl::RescaleMode::NONE) .value("ACER_METHODOLOGY", pypowsybl::RescaleMode::ACER_METHODOLOGY) - .value("PROPORTIONAL", pypowsybl::RescaleMode::PROPORTIONAL); + .value("PROPORTIONAL", pypowsybl::RescaleMode::PROPORTIONAL) + .value("MAX_CURRENT_OVERLOAD", pypowsybl::RescaleMode::MAX_CURRENT_OVERLOAD); py::class_(m, "FlowDecompositionParameters") .def(py::init(&pypowsybl::createFlowDecompositionParameters)) @@ -988,6 +1031,8 @@ PYBIND11_MODULE(_pypowsybl, m) { m.def("remove_aliases", &pypowsybl::removeAliases, "remove specified aliases on a network", py::arg("network"), py::arg("dataframe")); + m.def("remove_internal_connections", &pypowsybl::removeInternalConnections, "remove specified internal connections", py::arg("network"), py::arg("dataframe")); + m.def("close", &pypowsybl::closePypowsybl, "Closes pypowsybl module."); m.def("remove_elements_modification", &pypowsybl::removeElementsModification, "remove a list of feeder bays", py::arg("network"), py::arg("connectable_ids"), py::arg("extraDataDf"), py::arg("remove_modification_type"), py::arg("raise_exception"), py::arg("report_node")); @@ -1056,7 +1101,7 @@ void setLogLevelFromPythonLogger(pypowsybl::GraalVmGuard* guard, exception_handl } } -pypowsybl::JavaHandle loadNetworkFromBinaryBuffersPython(std::vector byteBuffers, const std::map& parameters, pypowsybl::JavaHandle* reportNode) { +pypowsybl::JavaHandle loadNetworkFromBinaryBuffersPython(std::vector byteBuffers, const std::map& parameters, const std::vector& postProcessors, pypowsybl::JavaHandle* reportNode) { std::vector parameterNames; std::vector parameterValues; parameterNames.reserve(parameters.size()); @@ -1067,6 +1112,7 @@ pypowsybl::JavaHandle loadNetworkFromBinaryBuffersPython(std::vector } pypowsybl::ToCharPtrPtr parameterNamesPtr(parameterNames); pypowsybl::ToCharPtrPtr parameterValuesPtr(parameterValues); + pypowsybl::ToCharPtrPtr postProcessorsPtr(postProcessors); char** dataPtrs = new char*[byteBuffers.size()]; int* dataSizes = new int[byteBuffers.size()]; @@ -1077,8 +1123,8 @@ pypowsybl::JavaHandle loadNetworkFromBinaryBuffersPython(std::vector } pypowsybl::JavaHandle networkHandle = pypowsybl::PowsyblCaller::get()->callJava(::loadNetworkFromBinaryBuffers, dataPtrs, dataSizes, byteBuffers.size(), - parameterNamesPtr.get(), parameterNames.size(), - parameterValuesPtr.get(), parameterValues.size(), (reportNode == nullptr) ? nullptr : *reportNode); + parameterNamesPtr.get(), parameterNames.size(), parameterValuesPtr.get(), parameterValues.size(), + postProcessorsPtr.get(), postProcessors.size(), (reportNode == nullptr) ? nullptr : *reportNode); delete[] dataPtrs; delete[] dataSizes; return networkHandle; diff --git a/cpp/pypowsybl-java/powsybl-api.h b/cpp/pypowsybl-java/powsybl-api.h index 863d9b8a98..b9286ea2c2 100644 --- a/cpp/pypowsybl-java/powsybl-api.h +++ b/cpp/pypowsybl-java/powsybl-api.h @@ -141,11 +141,13 @@ typedef enum { THREE_WINDINGS_TRANSFORMER, GENERATOR, LOAD, + GROUND, BATTERY, SHUNT_COMPENSATOR, NON_LINEAR_SHUNT_COMPENSATOR_SECTION, LINEAR_SHUNT_COMPENSATOR_SECTION, DANGLING_LINE, + DANGLING_LINE_GENERATION, TIE_LINE, LCC_CONVERTER_STATION, VSC_CONVERTER_STATION, @@ -161,13 +163,18 @@ typedef enum { PHASE_TAP_CHANGER, REACTIVE_CAPABILITY_CURVE_POINT, OPERATIONAL_LIMITS, + SELECTED_OPERATIONAL_LIMITS, MINMAX_REACTIVE_LIMITS, ALIAS, IDENTIFIABLE, INJECTION, BRANCH, TERMINAL, - SUB_NETWORK + SUB_NETWORK, + AREA, + AREA_VOLTAGE_LEVELS, + AREA_BOUNDARIES, + INTERNAL_CONNECTION } element_type; typedef enum { @@ -364,16 +371,40 @@ typedef struct nad_parameters_struct { } nad_parameters; typedef enum { - ALPHA_BETA_LOAD = 0, - ONE_TRANSFORMER_LOAD, - CURRENT_LIMIT_AUTOMATON, - GENERATOR_SYNCHRONOUS, - GENERATOR_SYNCHRONOUS_THREE_WINDINGS, - GENERATOR_SYNCHRONOUS_THREE_WINDINGS_PROPORTIONAL_REGULATIONS, - GENERATOR_SYNCHRONOUS_FOUR_WINDINGS, - GENERATOR_SYNCHRONOUS_FOUR_WINDINGS_PROPORTIONAL_REGULATIONS, + BASE_LOAD = 0, + LOAD_ONE_TRANSFORMER, + LOAD_ONE_TRANSFORMER_TAP_CHANGER, + LOAD_TWO_TRANSFORMERS, + LOAD_TWO_TRANSFORMERS_TAP_CHANGERS, + BASE_GENERATOR, + SYNCHRONIZED_GENERATOR, + SYNCHRONOUS_GENERATOR, + WECC, + GRID_FORMING_CONVERTER, + SIGNAL_N_GENERATOR, + HVDC_P, + HVDC_VSC, + BASE_TRANSFORMER, + BASE_STATIC_VAR_COMPENSATOR, + BASE_LINE, + BASE_BUS, + INFINITE_BUS, + OVERLOAD_MANAGEMENT_SYSTEM, + TWO_LEVELS_OVERLOAD_MANAGEMENT_SYSTEM, + UNDER_VOLTAGE, + PHASE_SHIFTER_I, + PHASE_SHIFTER_P, + PHASE_SHIFTER_BLOCKING_I, + TAP_CHANGER, + TAP_CHANGER_BLOCKING, } DynamicMappingType; +typedef enum { + DISCONNECT = 0, + NODE_FAULT, + ACTIVE_POWER_VARIATION, +} EventMappingType; + typedef enum { UNDEFINED = -1, ONE, diff --git a/data/educ_case14_storage.json b/data/educ_case14_storage.json new file mode 100644 index 0000000000..a65673f0ac --- /dev/null +++ b/data/educ_case14_storage.json @@ -0,0 +1,1766 @@ +{ + "_module": "pandapower.auxiliary", + "_class": "pandapowerNet", + "_object": { + "bus": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"vn_kv\",\"type\",\"zone\",\"in_service\",\"min_vm_pu\",\"max_vm_pu\"],\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],\"data\":[[1,138.0,\"b\",1.0,true,0.94,1.06],[2,138.0,\"b\",1.0,true,0.94,1.06],[3,138.0,\"b\",1.0,true,0.94,1.06],[4,138.0,\"b\",1.0,true,0.94,1.06],[5,138.0,\"b\",1.0,true,0.94,1.06],[6,20.0,\"b\",1.0,true,0.94,1.06],[7,14.0,\"b\",1.0,true,0.94,1.06],[8,12.0,\"b\",1.0,true,0.94,1.06],[9,20.0,\"b\",1.0,true,0.94,1.06],[10,20.0,\"b\",1.0,true,0.94,1.06],[11,20.0,\"b\",1.0,true,0.94,1.06],[12,20.0,\"b\",1.0,true,0.94,1.06],[13,20.0,\"b\",1.0,true,0.94,1.06],[14,20.0,\"b\",1.0,true,0.94,1.06]]}", + "orient": "split", + "dtype": { + "name": "object", + "vn_kv": "float64", + "type": "object", + "zone": "object", + "in_service": "bool", + "min_vm_pu": "float64", + "max_vm_pu": "float64" + } + }, + "load": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_mw\",\"q_mvar\",\"const_z_percent\",\"const_i_percent\",\"sn_mva\",\"scaling\",\"in_service\",\"type\",\"controllable\"],\"index\":[0,1,2,3,4,5,6,7,8,9,10],\"data\":[[null,1,21.699999999999999,12.699999999999999,0.0,0.0,null,1.0,true,null,false],[null,2,94.200000000000003,19.0,0.0,0.0,null,1.0,true,null,false],[null,3,47.799999999999997,-3.9,0.0,0.0,null,1.0,true,null,false],[null,4,7.6,1.6,0.0,0.0,null,1.0,true,null,false],[null,5,11.199999999999999,7.5,0.0,0.0,null,1.0,true,null,false],[null,8,29.5,16.600000000000001,0.0,0.0,null,1.0,true,null,false],[null,9,9.0,5.8,0.0,0.0,null,1.0,true,null,false],[null,10,3.5,1.8,0.0,0.0,null,1.0,true,null,false],[null,11,6.1,1.6,0.0,0.0,null,1.0,true,null,false],[null,12,13.5,5.8,0.0,0.0,null,1.0,true,null,false],[null,13,14.9,5.0,0.0,0.0,null,1.0,true,null,false]]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "uint32", + "p_mw": "float64", + "q_mvar": "float64", + "const_z_percent": "float64", + "const_i_percent": "float64", + "sn_mva": "float64", + "scaling": "float64", + "in_service": "bool", + "type": "object", + "controllable": "object" + } + }, + "sgen": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_mw\",\"q_mvar\",\"sn_mva\",\"scaling\",\"in_service\",\"type\",\"current_source\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "int64", + "p_mw": "float64", + "q_mvar": "float64", + "sn_mva": "float64", + "scaling": "float64", + "in_service": "bool", + "type": "object", + "current_source": "bool" + } + }, + "motor": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"pn_mech_mw\",\"loading_percent\",\"cos_phi\",\"cos_phi_n\",\"efficiency_percent\",\"efficiency_n_percent\",\"lrc_pu\",\"vn_kv\",\"scaling\",\"in_service\",\"rx\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "int64", + "pn_mech_mw": "float64", + "loading_percent": "float64", + "cos_phi": "float64", + "cos_phi_n": "float64", + "efficiency_percent": "float64", + "efficiency_n_percent": "float64", + "lrc_pu": "float64", + "vn_kv": "float64", + "scaling": "float64", + "in_service": "bool", + "rx": "float64" + } + }, + "asymmetric_load": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\",\"sn_mva\",\"scaling\",\"in_service\",\"type\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "uint32", + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64", + "sn_mva": "float64", + "scaling": "float64", + "in_service": "bool", + "type": "object" + } + }, + "asymmetric_sgen": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\",\"sn_mva\",\"scaling\",\"in_service\",\"type\",\"current_source\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "int64", + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64", + "sn_mva": "float64", + "scaling": "float64", + "in_service": "bool", + "type": "object", + "current_source": "bool" + } + }, + "storage": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_mw\",\"q_mvar\",\"sn_mva\",\"soc_percent\",\"min_e_mwh\",\"max_e_mwh\",\"scaling\",\"in_service\",\"type\"],\"index\":[0,1],\"data\":[[null,5,0.0,0.0,null,null,0.0,15.0,1.0,true,null],[null,7,0.0,0.0,null,null,0.0,7.0,1.0,true,null]]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "int64", + "p_mw": "float64", + "q_mvar": "float64", + "sn_mva": "float64", + "soc_percent": "float64", + "min_e_mwh": "float64", + "max_e_mwh": "float64", + "scaling": "float64", + "in_service": "bool", + "type": "object" + } + }, + "gen": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_mw\",\"vm_pu\",\"sn_mva\",\"min_q_mvar\",\"max_q_mvar\",\"scaling\",\"slack\",\"in_service\",\"type\",\"controllable\",\"min_p_mw\",\"max_p_mw\",\"slack_weight\",\"power_station_trafo\"],\"index\":[0,1,2,3,4,5],\"data\":[[null,1,40.0,1.045,null,-40.0,50.0,1.0,false,true,null,true,0.0,140.0,0.0,null],[null,2,0.0,1.01,null,0.0,40.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[null,5,0.0,1.07,null,-6.0,24.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[null,5,0.0,1.07,null,-6.0,24.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[null,7,0.0,1.09,null,-6.0,24.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[\"gen_0_5\",0,219.0,1.06,null,-9999.0,9999.0,1.0,true,true,null,true,null,null,1.0,null]]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "uint32", + "p_mw": "float64", + "vm_pu": "float64", + "sn_mva": "float64", + "min_q_mvar": "float64", + "max_q_mvar": "float64", + "scaling": "float64", + "slack": "bool", + "in_service": "bool", + "type": "object", + "controllable": "object", + "min_p_mw": "float64", + "max_p_mw": "float64", + "slack_weight": "float64", + "power_station_trafo": "float64" + } + }, + "switch": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"bus\",\"element\",\"et\",\"type\",\"closed\",\"name\",\"z_ohm\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "bus": "int64", + "element": "int64", + "et": "object", + "type": "object", + "closed": "bool", + "name": "object", + "z_ohm": "float64" + } + }, + "shunt": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"bus\",\"name\",\"q_mvar\",\"p_mw\",\"vn_kv\",\"step\",\"max_step\",\"in_service\"],\"index\":[0],\"data\":[[8,null,-19.0,0.0,20.0,1,1,true]]}", + "orient": "split", + "dtype": { + "bus": "uint32", + "name": "object", + "q_mvar": "float64", + "p_mw": "float64", + "vn_kv": "float64", + "step": "uint32", + "max_step": "uint32", + "in_service": "bool" + } + }, + "line": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"std_type\",\"from_bus\",\"to_bus\",\"length_km\",\"r_ohm_per_km\",\"x_ohm_per_km\",\"c_nf_per_km\",\"g_us_per_km\",\"max_i_ka\",\"df\",\"parallel\",\"type\",\"in_service\",\"max_loading_percent\"],\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14],\"data\":[[null,null,0,1,1.0,3.6907272,11.2683348,882.522683811391971,0.0,41.418606267951418,1.0,1,\"ol\",true,100.0],[null,null,0,4,1.0,10.2894732,42.475737599999995,822.350682642433412,0.0,41.418606267951418,1.0,1,\"ol\",true,100.0],[null,null,1,2,1.0,8.948775599999999,37.701406800000001,732.092680888995574,0.0,41.418606267951418,1.0,1,\"ol\",true,100.0],[null,null,1,3,1.0,11.0664684,33.578380799999998,568.29112215127509,0.0,41.418606267951418,1.0,1,\"ol\",true,100.0],[null,null,1,4,1.0,10.845558,33.1137072,578.319789012768069,0.0,41.418606267951418,1.0,1,\"ol\",true,100.0],[null,null,2,3,1.0,12.761384400000001,32.570953199999998,213.94489304518595,0.0,41.418606267951418,1.0,1,\"ol\",true,100.0],[null,null,3,4,1.0,2.542374,8.019428400000001,0.0,0.0,41.418606267951418,1.0,1,\"ol\",true,100.0],[null,null,5,10,1.0,0.37992,0.7956,0.0,0.0,285.788383248864761,1.0,1,\"ol\",true,100.0],[null,null,5,11,1.0,0.49164,1.02324,0.0,0.0,285.788383248864761,1.0,1,\"ol\",true,100.0],[null,null,5,12,1.0,0.2646,0.52108,0.0,0.0,285.788383248864761,1.0,1,\"ol\",true,100.0],[null,null,8,9,1.0,0.12724,0.338,0.0,0.0,285.788383248864761,1.0,1,\"ol\",true,100.0],[null,null,8,13,1.0,0.50844,1.08152,0.0,0.0,285.788383248864761,1.0,1,\"ol\",true,100.0],[null,null,9,10,1.0,0.3282,0.76828,0.0,0.0,285.788383248864761,1.0,1,\"ol\",true,100.0],[null,null,11,12,1.0,0.88368,0.79952,0.0,0.0,285.788383248864761,1.0,1,\"ol\",true,100.0],[null,null,12,13,1.0,0.68372,1.39208,0.0,0.0,285.788383248864761,1.0,1,\"ol\",true,100.0]]}", + "orient": "split", + "dtype": { + "name": "object", + "std_type": "object", + "from_bus": "uint32", + "to_bus": "uint32", + "length_km": "float64", + "r_ohm_per_km": "float64", + "x_ohm_per_km": "float64", + "c_nf_per_km": "float64", + "g_us_per_km": "float64", + "max_i_ka": "float64", + "df": "float64", + "parallel": "uint32", + "type": "object", + "in_service": "bool", + "max_loading_percent": "float64" + } + }, + "trafo": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"std_type\",\"hv_bus\",\"lv_bus\",\"sn_mva\",\"vn_hv_kv\",\"vn_lv_kv\",\"vk_percent\",\"vkr_percent\",\"pfe_kw\",\"i0_percent\",\"shift_degree\",\"tap_side\",\"tap_neutral\",\"tap_min\",\"tap_max\",\"tap_step_percent\",\"tap_step_degree\",\"tap_pos\",\"tap_phase_shifter\",\"parallel\",\"df\",\"in_service\",\"max_loading_percent\"],\"index\":[0,1,2,3,4],\"data\":[[null,null,3,6,9900.0,138.0,14.0,2070.288000000000011,0.0,0.0,0.0,0.0,\"hv\",0,null,null,2.200000000000002,0.0,-1,false,1,1.0,true,100.0],[null,null,3,8,9900.0,138.0,20.0,5506.181999999999789,0.0,0.0,0.0,0.0,\"hv\",0,null,null,3.100000000000003,0.0,-1,false,1,1.0,true,100.0],[null,null,4,5,9900.0,138.0,20.0,2494.998000000000047,0.0,0.0,0.0,0.0,\"hv\",0,null,null,6.799999999999995,0.0,-1,false,1,1.0,true,100.0],[null,null,6,7,9900.0,14.0,12.0,1743.884999999999991,0.0,0.0,0.0,0.0,false,0,null,null,0.0,0.0,0,false,1,1.0,true,100.0],[null,null,8,6,9900.0,20.0,14.0,1089.098999999999933,0.0,0.0,0.0,0.0,false,0,null,null,0.0,0.0,0,false,1,1.0,true,100.0]]}", + "orient": "split", + "dtype": { + "name": "object", + "std_type": "object", + "hv_bus": "uint32", + "lv_bus": "uint32", + "sn_mva": "float64", + "vn_hv_kv": "float64", + "vn_lv_kv": "float64", + "vk_percent": "float64", + "vkr_percent": "float64", + "pfe_kw": "float64", + "i0_percent": "float64", + "shift_degree": "float64", + "tap_side": "object", + "tap_neutral": "int32", + "tap_min": "float64", + "tap_max": "float64", + "tap_step_percent": "float64", + "tap_step_degree": "float64", + "tap_pos": "int32", + "tap_phase_shifter": "bool", + "parallel": "uint32", + "df": "float64", + "in_service": "bool", + "max_loading_percent": "float64" + } + }, + "trafo3w": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"std_type\",\"hv_bus\",\"mv_bus\",\"lv_bus\",\"sn_hv_mva\",\"sn_mv_mva\",\"sn_lv_mva\",\"vn_hv_kv\",\"vn_mv_kv\",\"vn_lv_kv\",\"vk_hv_percent\",\"vk_mv_percent\",\"vk_lv_percent\",\"vkr_hv_percent\",\"vkr_mv_percent\",\"vkr_lv_percent\",\"pfe_kw\",\"i0_percent\",\"shift_mv_degree\",\"shift_lv_degree\",\"tap_side\",\"tap_neutral\",\"tap_min\",\"tap_max\",\"tap_step_percent\",\"tap_step_degree\",\"tap_pos\",\"tap_at_star_point\",\"in_service\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "std_type": "object", + "hv_bus": "uint32", + "mv_bus": "uint32", + "lv_bus": "uint32", + "sn_hv_mva": "float64", + "sn_mv_mva": "float64", + "sn_lv_mva": "float64", + "vn_hv_kv": "float64", + "vn_mv_kv": "float64", + "vn_lv_kv": "float64", + "vk_hv_percent": "float64", + "vk_mv_percent": "float64", + "vk_lv_percent": "float64", + "vkr_hv_percent": "float64", + "vkr_mv_percent": "float64", + "vkr_lv_percent": "float64", + "pfe_kw": "float64", + "i0_percent": "float64", + "shift_mv_degree": "float64", + "shift_lv_degree": "float64", + "tap_side": "object", + "tap_neutral": "int32", + "tap_min": "int32", + "tap_max": "int32", + "tap_step_percent": "float64", + "tap_step_degree": "float64", + "tap_pos": "int32", + "tap_at_star_point": "bool", + "in_service": "bool" + } + }, + "impedance": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"from_bus\",\"to_bus\",\"rft_pu\",\"xft_pu\",\"rtf_pu\",\"xtf_pu\",\"sn_mva\",\"in_service\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "from_bus": "uint32", + "to_bus": "uint32", + "rft_pu": "float64", + "xft_pu": "float64", + "rtf_pu": "float64", + "xtf_pu": "float64", + "sn_mva": "float64", + "in_service": "bool" + } + }, + "dcline": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"from_bus\",\"to_bus\",\"p_mw\",\"loss_percent\",\"loss_mw\",\"vm_from_pu\",\"vm_to_pu\",\"max_p_mw\",\"min_q_from_mvar\",\"min_q_to_mvar\",\"max_q_from_mvar\",\"max_q_to_mvar\",\"in_service\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "from_bus": "uint32", + "to_bus": "uint32", + "p_mw": "float64", + "loss_percent": "float64", + "loss_mw": "float64", + "vm_from_pu": "float64", + "vm_to_pu": "float64", + "max_p_mw": "float64", + "min_q_from_mvar": "float64", + "min_q_to_mvar": "float64", + "max_q_from_mvar": "float64", + "max_q_to_mvar": "float64", + "in_service": "bool" + } + }, + "ward": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"ps_mw\",\"qs_mvar\",\"qz_mvar\",\"pz_mw\",\"in_service\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "uint32", + "ps_mw": "float64", + "qs_mvar": "float64", + "qz_mvar": "float64", + "pz_mw": "float64", + "in_service": "bool" + } + }, + "xward": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"ps_mw\",\"qs_mvar\",\"qz_mvar\",\"pz_mw\",\"r_ohm\",\"x_ohm\",\"vm_pu\",\"in_service\",\"slack_weight\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "uint32", + "ps_mw": "float64", + "qs_mvar": "float64", + "qz_mvar": "float64", + "pz_mw": "float64", + "r_ohm": "float64", + "x_ohm": "float64", + "vm_pu": "float64", + "in_service": "bool", + "slack_weight": "float64" + } + }, + "measurement": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"measurement_type\",\"element_type\",\"element\",\"value\",\"std_dev\",\"side\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "measurement_type": "object", + "element_type": "object", + "element": "uint32", + "value": "float64", + "std_dev": "float64", + "side": "object" + } + }, + "pwl_cost": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"power_type\",\"element\",\"et\",\"points\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "power_type": "object", + "element": "uint32", + "et": "object", + "points": "object" + } + }, + "poly_cost": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"element\",\"et\",\"cp0_eur\",\"cp1_eur_per_mw\",\"cp2_eur_per_mw2\",\"cq0_eur\",\"cq1_eur_per_mvar\",\"cq2_eur_per_mvar2\"],\"index\":[0,1,2,3,4],\"data\":[[0,\"ext_grid\",0.0,20.0,0.0430293,0.0,0.0,0.0],[0,\"gen\",0.0,20.0,0.25,0.0,0.0,0.0],[1,\"gen\",0.0,40.0,0.01,0.0,0.0,0.0],[2,\"gen\",0.0,40.0,0.01,0.0,0.0,0.0],[3,\"gen\",0.0,40.0,0.01,0.0,0.0,0.0]]}", + "orient": "split", + "dtype": { + "element": "uint32", + "et": "object", + "cp0_eur": "float64", + "cp1_eur_per_mw": "float64", + "cp2_eur_per_mw2": "float64", + "cq0_eur": "float64", + "cq1_eur_per_mvar": "float64", + "cq2_eur_per_mvar2": "float64" + } + }, + "characteristic": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"object\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "object": "object" + } + }, + "controller": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"object\",\"in_service\",\"order\",\"level\",\"initial_run\",\"recycle\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "object": "object", + "in_service": "bool", + "order": "float64", + "level": "object", + "initial_run": "bool", + "recycle": "object" + } + }, + "line_geodata": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"coords\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "coords": "object" + } + }, + "bus_geodata": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"x\",\"y\",\"coords\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "x": "float64", + "y": "float64", + "coords": "object" + } + }, + "version": "2.8.0", + "converged": false, + "name": "", + "f_hz": 50, + "sn_mva": 1.0, + "std_types": { + "line": { + "NAYY 4x50 SE": { + "c_nf_per_km": 210, + "r_ohm_per_km": 0.642, + "x_ohm_per_km": 0.083, + "max_i_ka": 0.142, + "type": "cs", + "q_mm2": 50, + "alpha": 0.00403 + }, + "NAYY 4x120 SE": { + "c_nf_per_km": 264, + "r_ohm_per_km": 0.225, + "x_ohm_per_km": 0.08, + "max_i_ka": 0.242, + "type": "cs", + "q_mm2": 120, + "alpha": 0.00403 + }, + "NAYY 4x150 SE": { + "c_nf_per_km": 261, + "r_ohm_per_km": 0.208, + "x_ohm_per_km": 0.08, + "max_i_ka": 0.27, + "type": "cs", + "q_mm2": 150, + "alpha": 0.00403 + }, + "NA2XS2Y 1x95 RM/25 12/20 kV": { + "c_nf_per_km": 216, + "r_ohm_per_km": 0.313, + "x_ohm_per_km": 0.132, + "max_i_ka": 0.252, + "type": "cs", + "q_mm2": 95, + "alpha": 0.00403 + }, + "NA2XS2Y 1x185 RM/25 12/20 kV": { + "c_nf_per_km": 273, + "r_ohm_per_km": 0.161, + "x_ohm_per_km": 0.117, + "max_i_ka": 0.362, + "type": "cs", + "q_mm2": 185, + "alpha": 0.00403 + }, + "NA2XS2Y 1x240 RM/25 12/20 kV": { + "c_nf_per_km": 304, + "r_ohm_per_km": 0.122, + "x_ohm_per_km": 0.112, + "max_i_ka": 0.421, + "type": "cs", + "q_mm2": 240, + "alpha": 0.00403 + }, + "NA2XS2Y 1x95 RM/25 6/10 kV": { + "c_nf_per_km": 315, + "r_ohm_per_km": 0.313, + "x_ohm_per_km": 0.123, + "max_i_ka": 0.249, + "type": "cs", + "q_mm2": 95, + "alpha": 0.00403 + }, + "NA2XS2Y 1x185 RM/25 6/10 kV": { + "c_nf_per_km": 406, + "r_ohm_per_km": 0.161, + "x_ohm_per_km": 0.11, + "max_i_ka": 0.358, + "type": "cs", + "q_mm2": 185, + "alpha": 0.00403 + }, + "NA2XS2Y 1x240 RM/25 6/10 kV": { + "c_nf_per_km": 456, + "r_ohm_per_km": 0.122, + "x_ohm_per_km": 0.105, + "max_i_ka": 0.416, + "type": "cs", + "q_mm2": 240, + "alpha": 0.00403 + }, + "NA2XS2Y 1x150 RM/25 12/20 kV": { + "c_nf_per_km": 250, + "r_ohm_per_km": 0.206, + "x_ohm_per_km": 0.116, + "max_i_ka": 0.319, + "type": "cs", + "q_mm2": 150, + "alpha": 0.00403 + }, + "NA2XS2Y 1x120 RM/25 12/20 kV": { + "c_nf_per_km": 230, + "r_ohm_per_km": 0.253, + "x_ohm_per_km": 0.119, + "max_i_ka": 0.283, + "type": "cs", + "q_mm2": 120, + "alpha": 0.00403 + }, + "NA2XS2Y 1x70 RM/25 12/20 kV": { + "c_nf_per_km": 190, + "r_ohm_per_km": 0.443, + "x_ohm_per_km": 0.132, + "max_i_ka": 0.22, + "type": "cs", + "q_mm2": 70, + "alpha": 0.00403 + }, + "NA2XS2Y 1x150 RM/25 6/10 kV": { + "c_nf_per_km": 360, + "r_ohm_per_km": 0.206, + "x_ohm_per_km": 0.11, + "max_i_ka": 0.315, + "type": "cs", + "q_mm2": 150, + "alpha": 0.00403 + }, + "NA2XS2Y 1x120 RM/25 6/10 kV": { + "c_nf_per_km": 340, + "r_ohm_per_km": 0.253, + "x_ohm_per_km": 0.113, + "max_i_ka": 0.28, + "type": "cs", + "q_mm2": 120, + "alpha": 0.00403 + }, + "NA2XS2Y 1x70 RM/25 6/10 kV": { + "c_nf_per_km": 280, + "r_ohm_per_km": 0.443, + "x_ohm_per_km": 0.123, + "max_i_ka": 0.217, + "type": "cs", + "q_mm2": 70, + "alpha": 0.00403 + }, + "N2XS(FL)2Y 1x120 RM/35 64/110 kV": { + "c_nf_per_km": 112, + "r_ohm_per_km": 0.153, + "x_ohm_per_km": 0.166, + "max_i_ka": 0.366, + "type": "cs", + "q_mm2": 120, + "alpha": 0.00393 + }, + "N2XS(FL)2Y 1x185 RM/35 64/110 kV": { + "c_nf_per_km": 125, + "r_ohm_per_km": 0.099, + "x_ohm_per_km": 0.156, + "max_i_ka": 0.457, + "type": "cs", + "q_mm2": 185, + "alpha": 0.00393 + }, + "N2XS(FL)2Y 1x240 RM/35 64/110 kV": { + "c_nf_per_km": 135, + "r_ohm_per_km": 0.075, + "x_ohm_per_km": 0.149, + "max_i_ka": 0.526, + "type": "cs", + "q_mm2": 240, + "alpha": 0.00393 + }, + "N2XS(FL)2Y 1x300 RM/35 64/110 kV": { + "c_nf_per_km": 144, + "r_ohm_per_km": 0.06, + "x_ohm_per_km": 0.144, + "max_i_ka": 0.588, + "type": "cs", + "q_mm2": 300, + "alpha": 0.00393 + }, + "15-AL1/3-ST1A 0.4": { + "c_nf_per_km": 11, + "r_ohm_per_km": 1.8769, + "x_ohm_per_km": 0.35, + "max_i_ka": 0.105, + "type": "ol", + "q_mm2": 16, + "alpha": 0.00403 + }, + "24-AL1/4-ST1A 0.4": { + "c_nf_per_km": 11.25, + "r_ohm_per_km": 1.2012, + "x_ohm_per_km": 0.335, + "max_i_ka": 0.14, + "type": "ol", + "q_mm2": 24, + "alpha": 0.00403 + }, + "48-AL1/8-ST1A 0.4": { + "c_nf_per_km": 12.2, + "r_ohm_per_km": 0.5939, + "x_ohm_per_km": 0.3, + "max_i_ka": 0.21, + "type": "ol", + "q_mm2": 48, + "alpha": 0.00403 + }, + "94-AL1/15-ST1A 0.4": { + "c_nf_per_km": 13.2, + "r_ohm_per_km": 0.306, + "x_ohm_per_km": 0.29, + "max_i_ka": 0.35, + "type": "ol", + "q_mm2": 94, + "alpha": 0.00403 + }, + "34-AL1/6-ST1A 10.0": { + "c_nf_per_km": 9.7, + "r_ohm_per_km": 0.8342, + "x_ohm_per_km": 0.36, + "max_i_ka": 0.17, + "type": "ol", + "q_mm2": 34, + "alpha": 0.00403 + }, + "48-AL1/8-ST1A 10.0": { + "c_nf_per_km": 10.1, + "r_ohm_per_km": 0.5939, + "x_ohm_per_km": 0.35, + "max_i_ka": 0.21, + "type": "ol", + "q_mm2": 48, + "alpha": 0.00403 + }, + "70-AL1/11-ST1A 10.0": { + "c_nf_per_km": 10.4, + "r_ohm_per_km": 0.4132, + "x_ohm_per_km": 0.339, + "max_i_ka": 0.29, + "type": "ol", + "q_mm2": 70, + "alpha": 0.00403 + }, + "94-AL1/15-ST1A 10.0": { + "c_nf_per_km": 10.75, + "r_ohm_per_km": 0.306, + "x_ohm_per_km": 0.33, + "max_i_ka": 0.35, + "type": "ol", + "q_mm2": 94, + "alpha": 0.00403 + }, + "122-AL1/20-ST1A 10.0": { + "c_nf_per_km": 11.1, + "r_ohm_per_km": 0.2376, + "x_ohm_per_km": 0.323, + "max_i_ka": 0.41, + "type": "ol", + "q_mm2": 122, + "alpha": 0.00403 + }, + "149-AL1/24-ST1A 10.0": { + "c_nf_per_km": 11.25, + "r_ohm_per_km": 0.194, + "x_ohm_per_km": 0.315, + "max_i_ka": 0.47, + "type": "ol", + "q_mm2": 149, + "alpha": 0.00403 + }, + "34-AL1/6-ST1A 20.0": { + "c_nf_per_km": 9.15, + "r_ohm_per_km": 0.8342, + "x_ohm_per_km": 0.382, + "max_i_ka": 0.17, + "type": "ol", + "q_mm2": 34, + "alpha": 0.00403 + }, + "48-AL1/8-ST1A 20.0": { + "c_nf_per_km": 9.5, + "r_ohm_per_km": 0.5939, + "x_ohm_per_km": 0.372, + "max_i_ka": 0.21, + "type": "ol", + "q_mm2": 48, + "alpha": 0.00403 + }, + "70-AL1/11-ST1A 20.0": { + "c_nf_per_km": 9.7, + "r_ohm_per_km": 0.4132, + "x_ohm_per_km": 0.36, + "max_i_ka": 0.29, + "type": "ol", + "q_mm2": 70, + "alpha": 0.00403 + }, + "94-AL1/15-ST1A 20.0": { + "c_nf_per_km": 10, + "r_ohm_per_km": 0.306, + "x_ohm_per_km": 0.35, + "max_i_ka": 0.35, + "type": "ol", + "q_mm2": 94, + "alpha": 0.00403 + }, + "122-AL1/20-ST1A 20.0": { + "c_nf_per_km": 10.3, + "r_ohm_per_km": 0.2376, + "x_ohm_per_km": 0.344, + "max_i_ka": 0.41, + "type": "ol", + "q_mm2": 122, + "alpha": 0.00403 + }, + "149-AL1/24-ST1A 20.0": { + "c_nf_per_km": 10.5, + "r_ohm_per_km": 0.194, + "x_ohm_per_km": 0.337, + "max_i_ka": 0.47, + "type": "ol", + "q_mm2": 149, + "alpha": 0.00403 + }, + "184-AL1/30-ST1A 20.0": { + "c_nf_per_km": 10.75, + "r_ohm_per_km": 0.1571, + "x_ohm_per_km": 0.33, + "max_i_ka": 0.535, + "type": "ol", + "q_mm2": 184, + "alpha": 0.00403 + }, + "243-AL1/39-ST1A 20.0": { + "c_nf_per_km": 11, + "r_ohm_per_km": 0.1188, + "x_ohm_per_km": 0.32, + "max_i_ka": 0.645, + "type": "ol", + "q_mm2": 243, + "alpha": 0.00403 + }, + "48-AL1/8-ST1A 110.0": { + "c_nf_per_km": 8, + "r_ohm_per_km": 0.5939, + "x_ohm_per_km": 0.46, + "max_i_ka": 0.21, + "type": "ol", + "q_mm2": 48, + "alpha": 0.00403 + }, + "70-AL1/11-ST1A 110.0": { + "c_nf_per_km": 8.4, + "r_ohm_per_km": 0.4132, + "x_ohm_per_km": 0.45, + "max_i_ka": 0.29, + "type": "ol", + "q_mm2": 70, + "alpha": 0.00403 + }, + "94-AL1/15-ST1A 110.0": { + "c_nf_per_km": 8.65, + "r_ohm_per_km": 0.306, + "x_ohm_per_km": 0.44, + "max_i_ka": 0.35, + "type": "ol", + "q_mm2": 94, + "alpha": 0.00403 + }, + "122-AL1/20-ST1A 110.0": { + "c_nf_per_km": 8.5, + "r_ohm_per_km": 0.2376, + "x_ohm_per_km": 0.43, + "max_i_ka": 0.41, + "type": "ol", + "q_mm2": 122, + "alpha": 0.00403 + }, + "149-AL1/24-ST1A 110.0": { + "c_nf_per_km": 8.75, + "r_ohm_per_km": 0.194, + "x_ohm_per_km": 0.41, + "max_i_ka": 0.47, + "type": "ol", + "q_mm2": 149, + "alpha": 0.00403 + }, + "184-AL1/30-ST1A 110.0": { + "c_nf_per_km": 8.8, + "r_ohm_per_km": 0.1571, + "x_ohm_per_km": 0.4, + "max_i_ka": 0.535, + "type": "ol", + "q_mm2": 184, + "alpha": 0.00403 + }, + "243-AL1/39-ST1A 110.0": { + "c_nf_per_km": 9, + "r_ohm_per_km": 0.1188, + "x_ohm_per_km": 0.39, + "max_i_ka": 0.645, + "type": "ol", + "q_mm2": 243, + "alpha": 0.00403 + }, + "305-AL1/39-ST1A 110.0": { + "c_nf_per_km": 9.2, + "r_ohm_per_km": 0.0949, + "x_ohm_per_km": 0.38, + "max_i_ka": 0.74, + "type": "ol", + "q_mm2": 305, + "alpha": 0.00403 + }, + "490-AL1/64-ST1A 110.0": { + "c_nf_per_km": 9.75, + "r_ohm_per_km": 0.059, + "x_ohm_per_km": 0.37, + "max_i_ka": 0.96, + "type": "ol", + "q_mm2": 490, + "alpha": 0.00403 + }, + "679-AL1/86-ST1A 110.0": { + "c_nf_per_km": 9.95, + "r_ohm_per_km": 0.042, + "x_ohm_per_km": 0.36, + "max_i_ka": 1.15, + "type": "ol", + "q_mm2": 679, + "alpha": 0.00403 + }, + "490-AL1/64-ST1A 220.0": { + "c_nf_per_km": 10, + "r_ohm_per_km": 0.059, + "x_ohm_per_km": 0.285, + "max_i_ka": 0.96, + "type": "ol", + "q_mm2": 490, + "alpha": 0.00403 + }, + "679-AL1/86-ST1A 220.0": { + "c_nf_per_km": 11.7, + "r_ohm_per_km": 0.042, + "x_ohm_per_km": 0.275, + "max_i_ka": 1.15, + "type": "ol", + "q_mm2": 679, + "alpha": 0.00403 + }, + "490-AL1/64-ST1A 380.0": { + "c_nf_per_km": 11, + "r_ohm_per_km": 0.059, + "x_ohm_per_km": 0.253, + "max_i_ka": 0.96, + "type": "ol", + "q_mm2": 490, + "alpha": 0.00403 + }, + "679-AL1/86-ST1A 380.0": { + "c_nf_per_km": 14.6, + "r_ohm_per_km": 0.042, + "x_ohm_per_km": 0.25, + "max_i_ka": 1.15, + "type": "ol", + "q_mm2": 679, + "alpha": 0.00403 + } + }, + "trafo": { + "160 MVA 380/110 kV": { + "i0_percent": 0.06, + "pfe_kw": 60, + "vkr_percent": 0.25, + "sn_mva": 160, + "vn_lv_kv": 110.0, + "vn_hv_kv": 380.0, + "vk_percent": 12.2, + "shift_degree": 0, + "vector_group": "Yy0", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "100 MVA 220/110 kV": { + "i0_percent": 0.06, + "pfe_kw": 55, + "vkr_percent": 0.26, + "sn_mva": 100, + "vn_lv_kv": 110.0, + "vn_hv_kv": 220.0, + "vk_percent": 12.0, + "shift_degree": 0, + "vector_group": "Yy0", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "63 MVA 110/20 kV": { + "i0_percent": 0.04, + "pfe_kw": 22, + "vkr_percent": 0.32, + "sn_mva": 63, + "vn_lv_kv": 20.0, + "vn_hv_kv": 110.0, + "vk_percent": 18, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "40 MVA 110/20 kV": { + "i0_percent": 0.05, + "pfe_kw": 18, + "vkr_percent": 0.34, + "sn_mva": 40, + "vn_lv_kv": 20.0, + "vn_hv_kv": 110.0, + "vk_percent": 16.2, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "25 MVA 110/20 kV": { + "i0_percent": 0.07, + "pfe_kw": 14, + "vkr_percent": 0.41, + "sn_mva": 25, + "vn_lv_kv": 20.0, + "vn_hv_kv": 110.0, + "vk_percent": 12, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "63 MVA 110/10 kV": { + "sn_mva": 63, + "vn_hv_kv": 110, + "vn_lv_kv": 10, + "vk_percent": 18, + "vkr_percent": 0.32, + "pfe_kw": 22, + "i0_percent": 0.04, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "40 MVA 110/10 kV": { + "sn_mva": 40, + "vn_hv_kv": 110, + "vn_lv_kv": 10, + "vk_percent": 16.2, + "vkr_percent": 0.34, + "pfe_kw": 18, + "i0_percent": 0.05, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "25 MVA 110/10 kV": { + "sn_mva": 25, + "vn_hv_kv": 110, + "vn_lv_kv": 10, + "vk_percent": 12, + "vkr_percent": 0.41, + "pfe_kw": 14, + "i0_percent": 0.07, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "0.25 MVA 20/0.4 kV": { + "sn_mva": 0.25, + "vn_hv_kv": 20, + "vn_lv_kv": 0.4, + "vk_percent": 6, + "vkr_percent": 1.44, + "pfe_kw": 0.8, + "i0_percent": 0.32, + "shift_degree": 150, + "vector_group": "Yzn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + }, + "0.4 MVA 20/0.4 kV": { + "sn_mva": 0.4, + "vn_hv_kv": 20, + "vn_lv_kv": 0.4, + "vk_percent": 6, + "vkr_percent": 1.425, + "pfe_kw": 1.35, + "i0_percent": 0.3375, + "shift_degree": 150, + "vector_group": "Dyn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + }, + "0.63 MVA 20/0.4 kV": { + "sn_mva": 0.63, + "vn_hv_kv": 20, + "vn_lv_kv": 0.4, + "vk_percent": 6, + "vkr_percent": 1.206, + "pfe_kw": 1.65, + "i0_percent": 0.2619, + "shift_degree": 150, + "vector_group": "Dyn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + }, + "0.25 MVA 10/0.4 kV": { + "sn_mva": 0.25, + "vn_hv_kv": 10, + "vn_lv_kv": 0.4, + "vk_percent": 4, + "vkr_percent": 1.2, + "pfe_kw": 0.6, + "i0_percent": 0.24, + "shift_degree": 150, + "vector_group": "Dyn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + }, + "0.4 MVA 10/0.4 kV": { + "sn_mva": 0.4, + "vn_hv_kv": 10, + "vn_lv_kv": 0.4, + "vk_percent": 4, + "vkr_percent": 1.325, + "pfe_kw": 0.95, + "i0_percent": 0.2375, + "shift_degree": 150, + "vector_group": "Dyn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + }, + "0.63 MVA 10/0.4 kV": { + "sn_mva": 0.63, + "vn_hv_kv": 10, + "vn_lv_kv": 0.4, + "vk_percent": 4, + "vkr_percent": 1.0794, + "pfe_kw": 1.18, + "i0_percent": 0.1873, + "shift_degree": 150, + "vector_group": "Dyn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + } + }, + "trafo3w": { + "63/25/38 MVA 110/20/10 kV": { + "sn_hv_mva": 63, + "sn_mv_mva": 25, + "sn_lv_mva": 38, + "vn_hv_kv": 110, + "vn_mv_kv": 20, + "vn_lv_kv": 10, + "vk_hv_percent": 10.4, + "vk_mv_percent": 10.4, + "vk_lv_percent": 10.4, + "vkr_hv_percent": 0.28, + "vkr_mv_percent": 0.32, + "vkr_lv_percent": 0.35, + "pfe_kw": 35, + "i0_percent": 0.89, + "shift_mv_degree": 0, + "shift_lv_degree": 0, + "vector_group": "YN0yn0yn0", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -10, + "tap_max": 10, + "tap_step_percent": 1.2 + }, + "63/25/38 MVA 110/10/10 kV": { + "sn_hv_mva": 63, + "sn_mv_mva": 25, + "sn_lv_mva": 38, + "vn_hv_kv": 110, + "vn_mv_kv": 10, + "vn_lv_kv": 10, + "vk_hv_percent": 10.4, + "vk_mv_percent": 10.4, + "vk_lv_percent": 10.4, + "vkr_hv_percent": 0.28, + "vkr_mv_percent": 0.32, + "vkr_lv_percent": 0.35, + "pfe_kw": 35, + "i0_percent": 0.89, + "shift_mv_degree": 0, + "shift_lv_degree": 0, + "vector_group": "YN0yn0yn0", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -10, + "tap_max": 10, + "tap_step_percent": 1.2 + } + } + }, + "res_bus": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"vm_pu\",\"va_degree\",\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "vm_pu": "float64", + "va_degree": "float64", + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_line": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_from_mw\",\"q_from_mvar\",\"p_to_mw\",\"q_to_mvar\",\"pl_mw\",\"ql_mvar\",\"i_from_ka\",\"i_to_ka\",\"i_ka\",\"vm_from_pu\",\"va_from_degree\",\"vm_to_pu\",\"va_to_degree\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_from_mw": "float64", + "q_from_mvar": "float64", + "p_to_mw": "float64", + "q_to_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_from_ka": "float64", + "i_to_ka": "float64", + "i_ka": "float64", + "vm_from_pu": "float64", + "va_from_degree": "float64", + "vm_to_pu": "float64", + "va_to_degree": "float64", + "loading_percent": "float64" + } + }, + "res_trafo": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_hv_mw\",\"q_hv_mvar\",\"p_lv_mw\",\"q_lv_mvar\",\"pl_mw\",\"ql_mvar\",\"i_hv_ka\",\"i_lv_ka\",\"vm_hv_pu\",\"va_hv_degree\",\"vm_lv_pu\",\"va_lv_degree\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_hv_mw": "float64", + "q_hv_mvar": "float64", + "p_lv_mw": "float64", + "q_lv_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_hv_ka": "float64", + "i_lv_ka": "float64", + "vm_hv_pu": "float64", + "va_hv_degree": "float64", + "vm_lv_pu": "float64", + "va_lv_degree": "float64", + "loading_percent": "float64" + } + }, + "res_trafo3w": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_hv_mw\",\"q_hv_mvar\",\"p_mv_mw\",\"q_mv_mvar\",\"p_lv_mw\",\"q_lv_mvar\",\"pl_mw\",\"ql_mvar\",\"i_hv_ka\",\"i_mv_ka\",\"i_lv_ka\",\"vm_hv_pu\",\"va_hv_degree\",\"vm_mv_pu\",\"va_mv_degree\",\"vm_lv_pu\",\"va_lv_degree\",\"va_internal_degree\",\"vm_internal_pu\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_hv_mw": "float64", + "q_hv_mvar": "float64", + "p_mv_mw": "float64", + "q_mv_mvar": "float64", + "p_lv_mw": "float64", + "q_lv_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_hv_ka": "float64", + "i_mv_ka": "float64", + "i_lv_ka": "float64", + "vm_hv_pu": "float64", + "va_hv_degree": "float64", + "vm_mv_pu": "float64", + "va_mv_degree": "float64", + "vm_lv_pu": "float64", + "va_lv_degree": "float64", + "va_internal_degree": "float64", + "vm_internal_pu": "float64", + "loading_percent": "float64" + } + }, + "res_impedance": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_from_mw\",\"q_from_mvar\",\"p_to_mw\",\"q_to_mvar\",\"pl_mw\",\"ql_mvar\",\"i_from_ka\",\"i_to_ka\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_from_mw": "float64", + "q_from_mvar": "float64", + "p_to_mw": "float64", + "q_to_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_from_ka": "float64", + "i_to_ka": "float64" + } + }, + "res_ext_grid": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_load": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_motor": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_sgen": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_storage": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_shunt": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\",\"vm_pu\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64", + "vm_pu": "float64" + } + }, + "res_gen": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\",\"va_degree\",\"vm_pu\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64", + "va_degree": "float64", + "vm_pu": "float64" + } + }, + "res_ward": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\",\"vm_pu\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64", + "vm_pu": "float64" + } + }, + "res_xward": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\",\"vm_pu\",\"va_internal_degree\",\"vm_internal_pu\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64", + "vm_pu": "float64", + "va_internal_degree": "float64", + "vm_internal_pu": "float64" + } + }, + "res_dcline": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_from_mw\",\"q_from_mvar\",\"p_to_mw\",\"q_to_mvar\",\"pl_mw\",\"vm_from_pu\",\"va_from_degree\",\"vm_to_pu\",\"va_to_degree\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_from_mw": "float64", + "q_from_mvar": "float64", + "p_to_mw": "float64", + "q_to_mvar": "float64", + "pl_mw": "float64", + "vm_from_pu": "float64", + "va_from_degree": "float64", + "vm_to_pu": "float64", + "va_to_degree": "float64" + } + }, + "res_asymmetric_load": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_asymmetric_sgen": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_bus_est": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"vm_pu\",\"va_degree\",\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "vm_pu": "float64", + "va_degree": "float64", + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_line_est": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_from_mw\",\"q_from_mvar\",\"p_to_mw\",\"q_to_mvar\",\"pl_mw\",\"ql_mvar\",\"i_from_ka\",\"i_to_ka\",\"i_ka\",\"vm_from_pu\",\"va_from_degree\",\"vm_to_pu\",\"va_to_degree\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_from_mw": "float64", + "q_from_mvar": "float64", + "p_to_mw": "float64", + "q_to_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_from_ka": "float64", + "i_to_ka": "float64", + "i_ka": "float64", + "vm_from_pu": "float64", + "va_from_degree": "float64", + "vm_to_pu": "float64", + "va_to_degree": "float64", + "loading_percent": "float64" + } + }, + "res_trafo_est": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_hv_mw\",\"q_hv_mvar\",\"p_lv_mw\",\"q_lv_mvar\",\"pl_mw\",\"ql_mvar\",\"i_hv_ka\",\"i_lv_ka\",\"vm_hv_pu\",\"va_hv_degree\",\"vm_lv_pu\",\"va_lv_degree\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_hv_mw": "float64", + "q_hv_mvar": "float64", + "p_lv_mw": "float64", + "q_lv_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_hv_ka": "float64", + "i_lv_ka": "float64", + "vm_hv_pu": "float64", + "va_hv_degree": "float64", + "vm_lv_pu": "float64", + "va_lv_degree": "float64", + "loading_percent": "float64" + } + }, + "res_trafo3w_est": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_hv_mw\",\"q_hv_mvar\",\"p_mv_mw\",\"q_mv_mvar\",\"p_lv_mw\",\"q_lv_mvar\",\"pl_mw\",\"ql_mvar\",\"i_hv_ka\",\"i_mv_ka\",\"i_lv_ka\",\"vm_hv_pu\",\"va_hv_degree\",\"vm_mv_pu\",\"va_mv_degree\",\"vm_lv_pu\",\"va_lv_degree\",\"va_internal_degree\",\"vm_internal_pu\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_hv_mw": "float64", + "q_hv_mvar": "float64", + "p_mv_mw": "float64", + "q_mv_mvar": "float64", + "p_lv_mw": "float64", + "q_lv_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_hv_ka": "float64", + "i_mv_ka": "float64", + "i_lv_ka": "float64", + "vm_hv_pu": "float64", + "va_hv_degree": "float64", + "vm_mv_pu": "float64", + "va_mv_degree": "float64", + "vm_lv_pu": "float64", + "va_lv_degree": "float64", + "va_internal_degree": "float64", + "vm_internal_pu": "float64", + "loading_percent": "float64" + } + }, + "res_impedance_est": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_from_mw\",\"q_from_mvar\",\"p_to_mw\",\"q_to_mvar\",\"pl_mw\",\"ql_mvar\",\"i_from_ka\",\"i_to_ka\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_from_mw": "float64", + "q_from_mvar": "float64", + "p_to_mw": "float64", + "q_to_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_from_ka": "float64", + "i_to_ka": "float64" + } + }, + "res_bus_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_line_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_trafo_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_trafo3w_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_ext_grid_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_gen_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_sgen_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_bus_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"vm_a_pu\",\"va_a_degree\",\"vm_b_pu\",\"va_b_degree\",\"vm_c_pu\",\"va_c_degree\",\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "vm_a_pu": "float64", + "va_a_degree": "float64", + "vm_b_pu": "float64", + "va_b_degree": "float64", + "vm_c_pu": "float64", + "va_c_degree": "float64", + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64" + } + }, + "res_line_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_a_from_mw\",\"q_a_from_mvar\",\"p_b_from_mw\",\"q_b_from_mvar\",\"q_c_from_mvar\",\"p_a_to_mw\",\"q_a_to_mvar\",\"p_b_to_mw\",\"q_b_to_mvar\",\"p_c_to_mw\",\"q_c_to_mvar\",\"p_a_l_mw\",\"q_a_l_mvar\",\"p_b_l_mw\",\"q_b_l_mvar\",\"p_c_l_mw\",\"q_c_l_mvar\",\"i_a_from_ka\",\"i_a_to_ka\",\"i_b_from_ka\",\"i_b_to_ka\",\"i_c_from_ka\",\"i_c_to_ka\",\"i_a_ka\",\"i_b_ka\",\"i_c_ka\",\"i_n_from_ka\",\"i_n_to_ka\",\"i_n_ka\",\"loading_a_percent\",\"loading_b_percent\",\"loading_c_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_a_from_mw": "float64", + "q_a_from_mvar": "float64", + "p_b_from_mw": "float64", + "q_b_from_mvar": "float64", + "q_c_from_mvar": "float64", + "p_a_to_mw": "float64", + "q_a_to_mvar": "float64", + "p_b_to_mw": "float64", + "q_b_to_mvar": "float64", + "p_c_to_mw": "float64", + "q_c_to_mvar": "float64", + "p_a_l_mw": "float64", + "q_a_l_mvar": "float64", + "p_b_l_mw": "float64", + "q_b_l_mvar": "float64", + "p_c_l_mw": "float64", + "q_c_l_mvar": "float64", + "i_a_from_ka": "float64", + "i_a_to_ka": "float64", + "i_b_from_ka": "float64", + "i_b_to_ka": "float64", + "i_c_from_ka": "float64", + "i_c_to_ka": "float64", + "i_a_ka": "float64", + "i_b_ka": "float64", + "i_c_ka": "float64", + "i_n_from_ka": "float64", + "i_n_to_ka": "float64", + "i_n_ka": "float64", + "loading_a_percent": "float64", + "loading_b_percent": "float64", + "loading_c_percent": "float64" + } + }, + "res_trafo_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_a_hv_mw\",\"q_a_hv_mvar\",\"p_b_hv_mw\",\"q_b_hv_mvar\",\"p_c_hv_mw\",\"q_c_hv_mvar\",\"p_a_lv_mw\",\"q_a_lv_mvar\",\"p_b_lv_mw\",\"q_b_lv_mvar\",\"p_c_lv_mw\",\"q_c_lv_mvar\",\"p_a_l_mw\",\"q_a_l_mvar\",\"p_b_l_mw\",\"q_b_l_mvar\",\"p_c_l_mw\",\"q_c_l_mvar\",\"i_a_hv_ka\",\"i_a_lv_ka\",\"i_b_hv_ka\",\"i_b_lv_ka\",\"i_c_hv_ka\",\"i_c_lv_ka\",\"loading_a_percent\",\"loading_b_percent\",\"loading_c_percent\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_a_hv_mw": "float64", + "q_a_hv_mvar": "float64", + "p_b_hv_mw": "float64", + "q_b_hv_mvar": "float64", + "p_c_hv_mw": "float64", + "q_c_hv_mvar": "float64", + "p_a_lv_mw": "float64", + "q_a_lv_mvar": "float64", + "p_b_lv_mw": "float64", + "q_b_lv_mvar": "float64", + "p_c_lv_mw": "float64", + "q_c_lv_mvar": "float64", + "p_a_l_mw": "float64", + "q_a_l_mvar": "float64", + "p_b_l_mw": "float64", + "q_b_l_mvar": "float64", + "p_c_l_mw": "float64", + "q_c_l_mvar": "float64", + "i_a_hv_ka": "float64", + "i_a_lv_ka": "float64", + "i_b_hv_ka": "float64", + "i_b_lv_ka": "float64", + "i_c_hv_ka": "float64", + "i_c_lv_ka": "float64", + "loading_a_percent": "float64", + "loading_b_percent": "float64", + "loading_c_percent": "float64", + "loading_percent": "float64" + } + }, + "res_ext_grid_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64" + } + }, + "res_shunt_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_load_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_sgen_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_storage_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_asymmetric_load_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64" + } + }, + "res_asymmetric_sgen_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64" + } + }, + "user_pf_options": {} + } +} \ No newline at end of file diff --git a/docs/_static/images/explorer_map.png b/docs/_static/images/explorer_map.png new file mode 100644 index 0000000000..bc43cde12d Binary files /dev/null and b/docs/_static/images/explorer_map.png differ diff --git a/docs/_static/images/explorer_map_blank.png b/docs/_static/images/explorer_map_blank.png new file mode 100644 index 0000000000..760a38ca72 Binary files /dev/null and b/docs/_static/images/explorer_map_blank.png differ diff --git a/docs/_static/images/explorer_map_substation.png b/docs/_static/images/explorer_map_substation.png new file mode 100644 index 0000000000..9b0762ee65 Binary files /dev/null and b/docs/_static/images/explorer_map_substation.png differ diff --git a/docs/_static/images/explorer_nad.png b/docs/_static/images/explorer_nad.png new file mode 100644 index 0000000000..1d406a5480 Binary files /dev/null and b/docs/_static/images/explorer_nad.png differ diff --git a/docs/_static/images/explorer_navigate.png b/docs/_static/images/explorer_navigate.png new file mode 100644 index 0000000000..7b65ebd75d Binary files /dev/null and b/docs/_static/images/explorer_navigate.png differ diff --git a/docs/_static/images/explorer_sld.png b/docs/_static/images/explorer_sld.png new file mode 100644 index 0000000000..ee5cb2cbbc Binary files /dev/null and b/docs/_static/images/explorer_sld.png differ diff --git a/docs/_static/images/explorer_slide_button.png b/docs/_static/images/explorer_slide_button.png new file mode 100644 index 0000000000..c81f951b55 Binary files /dev/null and b/docs/_static/images/explorer_slide_button.png differ diff --git a/docs/reference/dynamic.rst b/docs/reference/dynamic.rst index 6c900844b9..9359daac11 100644 --- a/docs/reference/dynamic.rst +++ b/docs/reference/dynamic.rst @@ -1,6 +1,6 @@ -========= -Dynawaltz -========= +====================== +Simulation with Dynawo +====================== .. module:: pypowsybl.dynamic @@ -12,14 +12,30 @@ ModelMapping :toctree: api/ ModelMapping - ModelMapping.add_all_dynamic_mappings - ModelMapping.add_alpha_beta_load - ModelMapping.add_one_transformer_load - ModelMapping.add_generator_synchronous_three_windings - ModelMapping.add_generator_synchronous_three_windings_proportional_regulations - ModelMapping.add_generator_synchronous_four_windings - ModelMapping.add_generator_synchronous_four_windings_proportional_regulations - ModelMapping.add_current_limit_automaton + ModelMapping.add_base_load + ModelMapping.add_load_one_transformer + ModelMapping.add_load_one_transformer_tap_changer + ModelMapping.add_load_two_transformers + ModelMapping.add_load_two_transformers_tap_changers + ModelMapping.add_base_generator + ModelMapping.add_synchronized_generator + ModelMapping.add_synchronous_generator + ModelMapping.add_wecc + ModelMapping.add_grid_forming_converter + ModelMapping.add_hvdc_p + ModelMapping.add_hvdc_vsc + ModelMapping.add_base_transformer + ModelMapping.add_base_static_var_compensator + ModelMapping.add_base_line + ModelMapping.add_base_bus + ModelMapping.add_infinite_bus + ModelMapping.add_overload_management_system + ModelMapping.add_two_levels_overload_management_system + ModelMapping.add_under_voltage_automation_system + ModelMapping.add_phase_shifter_i_automation_system + ModelMapping.add_phase_shifter_p_automation_system + ModelMapping.add_tap_changer_automation_system + ModelMapping.add_tap_changer_blocking_automation_system EventMapping ------------ @@ -27,8 +43,9 @@ EventMapping :toctree: api/ EventMapping - EventMapping.get_possible_events EventMapping.add_disconnection + EventMapping.add_active_power_variation + EventMapping.add_node_fault CurveMapping ------------ diff --git a/docs/reference/network.rst b/docs/reference/network.rst index ec9260b7ac..6da7cc4058 100644 --- a/docs/reference/network.rst +++ b/docs/reference/network.rst @@ -72,11 +72,14 @@ All network elements are accessible as dataframes, using the following getters. Network.get_2_windings_transformers Network.get_3_windings_transformers Network.get_aliases + Network.get_areas + Network.get_areas_boundaries + Network.get_areas_voltage_levels Network.get_batteries Network.get_branches - Network.get_bus_breaker_topology Network.get_busbar_sections Network.get_buses + Network.get_bus_breaker_view_buses Network.get_current_limits Network.get_dangling_lines Network.get_generators @@ -87,7 +90,6 @@ All network elements are accessible as dataframes, using the following getters. Network.get_lines Network.get_loads Network.get_linear_shunt_compensator_sections - Network.get_node_breaker_topology Network.get_non_linear_shunt_compensator_sections Network.get_operational_limits Network.get_phase_tap_changer_steps @@ -104,6 +106,29 @@ All network elements are accessible as dataframes, using the following getters. Network.get_vsc_converter_stations Network.get_tie_lines +Bus/Breaker or Node/Breaker topology description of a given voltage level can be retrieved using the following getters: + +.. autosummary:: + :toctree: api/ + :nosignatures: + + Network.get_bus_breaker_topology + Network.get_node_breaker_topology + +These getters return an object of the following classes: + +.. autosummary:: + :nosignatures: + + BusBreakerTopology + NodeBreakerTopology + +.. include it in the toctree +.. toctree:: + :hidden: + + network/bus_breaker_topology + network/node_breaker_topology Network elements update ------------------------ @@ -116,6 +141,7 @@ Network elements can be modified using dataframes: Network.update_2_windings_transformers Network.update_3_windings_transformers + Network.update_areas Network.update_batteries Network.update_buses Network.update_dangling_lines @@ -151,6 +177,9 @@ Network elements can be created or removed using the following methods: Network.create_2_windings_transformers Network.create_3_windings_transformers + Network.create_areas + Network.create_areas_voltage_levels + Network.create_areas_boundaries Network.create_batteries Network.create_busbar_sections Network.create_buses @@ -158,6 +187,7 @@ Network elements can be created or removed using the following methods: Network.create_dangling_lines Network.create_generators Network.create_hvdc_lines + Network.create_internal_connections Network.create_lcc_converter_stations Network.create_lines Network.create_loads @@ -173,6 +203,7 @@ Network elements can be created or removed using the following methods: Network.create_vsc_converter_stations Network.create_tie_lines Network.remove_elements + Network.remove_internal_connections Network variants management @@ -246,6 +277,7 @@ I/O Network.dump_to_string get_import_formats get_import_parameters + get_import_post_processors get_export_formats get_export_parameters Network.save diff --git a/docs/reference/network/bus_breaker_topology.rst b/docs/reference/network/bus_breaker_topology.rst new file mode 100644 index 0000000000..b4b532580a --- /dev/null +++ b/docs/reference/network/bus_breaker_topology.rst @@ -0,0 +1,7 @@ +pypowsybl.network.BusBreakerTopology +==================================== + +.. currentmodule:: pypowsybl.network + +.. autoclass:: BusBreakerTopology + :members: diff --git a/docs/reference/network/node_breaker_topology.rst b/docs/reference/network/node_breaker_topology.rst new file mode 100644 index 0000000000..75bbdf2688 --- /dev/null +++ b/docs/reference/network/node_breaker_topology.rst @@ -0,0 +1,7 @@ +pypowsybl.network.NodeBreakerTopology +===================================== + +.. currentmodule:: pypowsybl.network + +.. autoclass:: NodeBreakerTopology + :members: diff --git a/docs/user_guide/dynamic.rst b/docs/user_guide/dynamic.rst index f445276e40..6daefdaecb 100644 --- a/docs/user_guide/dynamic.rst +++ b/docs/user_guide/dynamic.rst @@ -1,4 +1,4 @@ -Running a dynamic simulation with dynawaltz +Running a dynamic simulation with dynawo =========================================== .. currentmodule:: pypowsybl.dynamic @@ -14,23 +14,23 @@ Start by importing the module: Providers --------- -For now we only support the Dynawaltz integration, provided by the `Dynawo `_ project. +For now we only support the Dynawo simulator integration, provided by the `Dynawo `_ project. Prerequisites ------------- -The pypowsybl config file (generally located at ~/.itools/config.yaml) must define the dynawaltz section to find your dynawaltz installation and defaults parameters -Here is an example of a simple config.yaml file. It uses the same configurations as in powsybl-dynawatlz. +The pypowsybl config file (generally located at ~/.itools/config.yaml) must define the dynawo section to find your dynawo installation and defaults parameters +Here is an example of a simple config.yaml file. It uses the same configurations as in powsybl-dynawo. .. code-block:: yaml+jinja dynamic-simulation-default-parameters: startTime: 0 stopTime: 30 - dynawaltz: + dynawo: homeDir: PATH_TO_DYNAWO debug: true - dynawaltz-default-parameters: + dynawo-simulation-default-parameters: parametersFile: ./models.par network.parametersFile: ./network.par network.parametersId: "1" @@ -45,20 +45,20 @@ Parameters To make a dynamic simulation, you need multiple things: 1. A dynamic mapping, it links the static elements (generators, loads, lines) to their dynamic behavior (alpha beta load) - 2. A curve mapping, it records the given values to be watch by the simulation tool. Curves are the output of the simulation - 3. A event mapping, it maps the different events. (their time event is done in configurations file for now) + 2. A event mapping, it maps the different events. (e.g equipment disconnection) + 3. A curve mapping, it records the given values to be watch by the simulation tool. Curves are the output of the simulation There is a class for each of these elements. -You will see a lot of arguments called parameterSetId. Dynawaltz use a lot of parameters that will be stored in files. +You will see a lot of arguments called parameterSetId. Dynawo simulator use a lot of parameters that will be stored in files. -Pypowsybl will find the path to this file in the powsybl config.yaml in dynawaltz-default-parameters.parametersFile value. +Pypowsybl will find the path to this file in the powsybl config.yaml in dynawo-simulation-default-parameters.parametersFile value. The parameterSetId argument must match an id in this file (generally called models.par). Simple example -------------- -To run a Dynawaltz simulation: +To run a Dynawo simulation: .. code-block:: python @@ -70,22 +70,26 @@ To run a Dynawaltz simulation: # dynamic mapping model_mapping = dyn.ModelMapping() - model_mapping.add_alpha_beta_load("LOAD", "LAB") # and so on + model_mapping.add_base_load(static_id='LOAD', + parameter_set_id='LAB', + dynamic_model_id='DM_LOAD', + model_name='LoadAlphaBeta') # and so on # events mapping - events = dyn.EventMapping() - events.add_event("GEN_DISCONNECTION", dyn.EventType.DISCONNECTION, "GEN") - events.add_event("LINE_DISCONNECTION", dyn.EventType.DISCONNECTION, "NHV1_NHV2_1", BranchSide.ONE) + event_mapping = dyn.EventMapping() + event_mapping.add.add_disconnection(static_id='GEN', start_time=10) + event_mapping.add_disconnection(static_id='NHV1_NHV2_1', start_time=10, disconnect_only='ONE') # curves mapping - curves = dyn.CurveMapping() - curves.add_curves("LOAD", ["load_PPu", "load_QPu"]) + curve_mapping = dyn.CurveMapping() + curve_mapping.add_curves("LOAD", ["load_PPu", "load_QPu"]) # simulations parameters start_time = 0 end_time = 50 sim = dyn.Simulation() # running the simulation - results = sim.run(network, model_mapping, events, curves, start_time, end_time) + results = sim.run(network, model_mapping, event_mapping, curve_mapping, start_time, end_time) # getting the results - results.curves() # dataframe containing the curves mapped \ No newline at end of file + results.status() + results.curves() # dataframe containing the mapped curves \ No newline at end of file diff --git a/docs/user_guide/loadflow.rst b/docs/user_guide/loadflow.rst index 2281d01a12..1ab017b9dd 100644 --- a/docs/user_guide/loadflow.rst +++ b/docs/user_guide/loadflow.rst @@ -60,7 +60,23 @@ Some parameters are not supported by all load flow providers but specific to onl parameters could be specified in a less typed way than common parameters using the `provider_parameters` attribute. .. warning:: - `provider_parameters` is dictionary and all keys and values have to be a string even in case of a numeric value. + `provider_parameters` is a dictionary in which all keys and values **must** be a string, even in case of a numeric value: + + * string and integer parameters do not bring much challenge: + + ``provider_parameters={'someStringParam' : 'myStringValue', 'someIntegerParam' : '42'}`` + + * for float (double) parameters, use the dot as decimal separator. E notation is also supported: + + ``provider_parameters={'someDoubleParam' : '1.23', 'someOtherDoubleParam' : '4.56E-2'}`` + + * for boolean parameters, use either `'True'`, `'true'`, `'False'`, `'false'`: + + ``provider_parameters={'someBooleanParam' : 'true'}`` + + * for string list parameters, use the comma as a separator: + + ``provider_parameters={'someStringListParam' : 'value1,value2,value3'}`` We can list supported parameters specific to default provider using: @@ -69,16 +85,17 @@ We can list supported parameters specific to default provider using: >>> lf.get_provider_parameters_names() ['slackBusSelectionMode', 'slackBusesIds', 'lowImpedanceBranchMode', 'voltageRemoteControl', ...] -And get more detailed information about theses parameters using: +And get more detailed information about theses parameters, such as parameter description, type, default value if any, +possible values if applicable, using: .. doctest:: :options: +NORMALIZE_WHITESPACE - >>> lf.get_provider_parameters().iloc[:2] - description type default possible_values + >>> lf.get_provider_parameters().query('name == "slackBusSelectionMode" or name == "slackBusesIds"') + category_key description type default possible_values name - slackBusSelectionMode Slack bus selection mode STRING MOST_MESHED [FIRST, MOST_MESHED, NAME, LARGEST_GENERATOR] - slackBusesIds Slack bus IDs STRING_LIST + slackBusSelectionMode SlackDistribution Slack bus selection mode STRING MOST_MESHED [FIRST, MOST_MESHED, NAME, LARGEST_GENERATOR] + slackBusesIds SlackDistribution Slack bus IDs STRING_LIST For instance, OLF supports configuration of slack bus from its ID like this: diff --git a/docs/user_guide/network.rst b/docs/user_guide/network.rst index 2250fd50d7..99a7d0d938 100644 --- a/docs/user_guide/network.rst +++ b/docs/user_guide/network.rst @@ -53,6 +53,20 @@ Only zipped network loading are supported for now, but inside the zip file the s You may also create your own network from scratch, see below. +We can also configure some post processors to be loaded after import. +To see the list of available post processors: + +.. doctest:: + + >>> pp.network.get_import_post_processors() + ['loadflowResultsCompletion', 'odreGeoDataImporter', 'replaceTieLinesByLines'] + +Then a list of post processors can be pass to the load function: + + .. code-block:: python + + network = pp.network.load('mycgmes.zip', post_processors=['replaceTieLinesByLines']) + Save a network -------------- @@ -93,6 +107,7 @@ Reading network elements data All network elements data can be read as :class:`DataFrames `. Supported elements are: + - areas - buses (from bus view) - buses from bus/breaker view - lines @@ -934,3 +949,65 @@ S1VL1 is connected to S1VL2 by the transformer TWT, so it is kept after the netw It is the only voltage level connected to S1VL1 by one branch. the parameter "ids" can be used to specify the exact voltage levels that will be kept + +Using operational limits +------------------------ + +Operational limits can be added on various network elements : +- each side of the branches (lines and 2 windings transformers) +- each leg of 3 windings transformers +- dangling lines + +For more information on the model of operational limits, see `the internal model documentation `_ + +Limits are defined in operational limit groups, that are represented by an `id`. Each line, transformer or dangling line is associated with a collection of groups, and for each element one of its groups is the selected one. +The id of the selected limit group can be found in the data of each element : + +.. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> net = pp.network.create_eurostag_tutorial_example1_network() + >>> net.get_lines(attributes=["selected_limits_group_1", "selected_limits_group_2"]) + selected_limits_group_1 selected_limits_group_2 + id + NHV1_NHV2_1 DEFAULT DEFAULT + NHV1_NHV2_2 DEFAULT DEFAULT + +To retrieve the limits present on a network, the user can use the `get_operational_limits` method (choosing with the `show_inactive_sets` parameter whether to get all groups or only the active ones). +For example on a network with a line that has two sets of current limits : + +.. code-block:: python + + >>> net.get_operational_limits() # only selected sets + + element_type side name type value acceptable_duration + element_id + LINE1 LINE TWO permanent_limit CURRENT 1000 -1 + LINE1 LINE TWO 10' CURRENT 1200 600 + LINE1 LINE TWO 1' CURRENT 1500 60 + + >>> net.get_operational_limits(all_attributes=True, show_inactive_sets=True) # all sets + + element_type side name type value acceptable_duration fictitious group_name selected + element_id + LINE1 LINE TWO permanent_limit CURRENT 1000 -1 False DEFAULT True + LINE1 LINE TWO 10' CURRENT 1200 600 False DEFAULT True + LINE1 LINE TWO 1' CURRENT 1500 60 False DEFAULT True + LINE1 LINE TWO permanent_limit CURRENT 1100 -1 False OTHER_GROUP False + + +Finally, the user can change the selected group of limits using the respective update methods of network elements. +Using the same example as above : + +.. code-block:: python + + >>> net.update_lines(id='LINE1', 'selected_limits_group_2'='OTHER_GROUP') + >>> net.get_operational_limits(all_attributes=True, show_inactive_sets=True) + + element_type side name type value acceptable_duration fictitious group_name selected + element_id + LINE1 LINE TWO permanent_limit CURRENT 1000 -1 False DEFAULT False + LINE1 LINE TWO 10' CURRENT 1200 600 False DEFAULT False + LINE1 LINE TWO 1' CURRENT 1500 60 False DEFAULT False + LINE1 LINE TWO permanent_limit CURRENT 1100 -1 False OTHER_GROUP True + diff --git a/docs/user_guide/network_visualization.rst b/docs/user_guide/network_visualization.rst index 0587518833..128e68c662 100644 --- a/docs/user_guide/network_visualization.rst +++ b/docs/user_guide/network_visualization.rst @@ -348,3 +348,75 @@ the geographical layout: .. image:: ../_static/images/nad_microgridbe_geo.svg :class: forced-white-background + +Display diagrams using Jupyter widgets +-------------------------------------------- +You can also display diagrams through `Jupyter widgets `_. + +Get a handle on Jupyter widgets +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the explorer to check the example notebooks given in the `pypowsybl-jupyter repository `_: + +.. code-block:: bash + + pip install pypowsybl_jupyter + jupyter lab + +The network_explorer features three tabs: + +* A tab for network-area diagrams; + +* A tab for single-line diagrams; + +* A tab for a map viewer. + +Explore the network_explorer tabs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- **The network-area diagram tab** + +.. image:: ../_static/images/explorer_nad.png + +The network-area diagram tab displays the network-area diagram of the selected voltage level in the left column at the desired depth. +The depth is user-defined, thanks to the slide button above the display zone. + +.. image:: ../_static/images/explorer_slide_button.png + +- **The single-line diagram tab** + +.. image:: ../_static/images/explorer_sld.png + +The single-line diagram tab displays the single-line diagram of the selected voltage level in the left column. +You can navigate from voltage level to voltage level using the circled arrows. + +.. image:: ../_static/images/explorer_navigate.png + +- **The map viewer tab** + +.. image:: ../_static/images/explorer_map.png + +The map viewer tab displays a geographical representation of the network with a background map. +The vertices of the graph are substations and the edges are lines, tie lines or HVDC lines. +Voltage levels are represented as concentric circles inside a substation: + +.. image:: ../_static/images/explorer_map_substation.png + +Selecting a voltage level on the left column will center the map on the corresponding substation. + +Users can filter the displayed voltage levels through a nominal voltage filter. By default, only higher nominal voltages are selected. + +Please note that if no geographical extensions are available for substations, the tab will be blank. + +.. image:: ../_static/images/explorer_map_blank.png + +Go further +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Check the complete documentation available on the widgets `here `_. + + + + + + diff --git a/docs/user_guide/per_unit.rst b/docs/user_guide/per_unit.rst index b9a7d6e063..a1e7acf7a4 100644 --- a/docs/user_guide/per_unit.rst +++ b/docs/user_guide/per_unit.rst @@ -110,4 +110,4 @@ with Sn the nominal apparent power #. Angle -the angle are in degrees in PyPowSyBl, when per-united it is in radian. \ No newline at end of file +the angle are in degrees in PyPowSyBl, but when per-unit is activated it is in radian even if it is not really related to per-uniting. \ No newline at end of file diff --git a/docs/user_guide/security.rst b/docs/user_guide/security.rst index 95094519bb..b4ba3670c3 100644 --- a/docs/user_guide/security.rst +++ b/docs/user_guide/security.rst @@ -52,13 +52,13 @@ Information can be obtained on buses, branches and three windings transformers. .. doctest:: security.monitored_elements :options: +NORMALIZE_WHITESPACE - >>> network = pp.network.create_eurostag_tutorial_example1_network() + >>> network = pp.network.create_eurostag_tutorial_example1_with_more_generators_network() >>> security_analysis = pp.security.create_analysis() >>> security_analysis.add_single_element_contingency('NHV1_NHV2_1', 'NHV1_NHV2_1') - >>> security_analysis.add_single_element_contingency('NGEN_NHV1', 'NGEN_NHV1') + >>> security_analysis.add_single_element_contingency('GEN', 'GEN') >>> security_analysis.add_monitored_elements(voltage_level_ids=['VLHV2']) - >>> security_analysis.add_postcontingency_monitored_elements(branch_ids=['NHV1_NHV2_2'], contingency_ids=['NHV1_NHV2_1', 'NGEN_NHV1']) - >>> security_analysis.add_postcontingency_monitored_elements(branch_ids=['NHV1_NHV2_1'], contingency_ids='NGEN_NHV1') + >>> security_analysis.add_postcontingency_monitored_elements(branch_ids=['NHV1_NHV2_2'], contingency_ids=['NHV1_NHV2_1', 'GEN']) + >>> security_analysis.add_postcontingency_monitored_elements(branch_ids=['NHV1_NHV2_1'], contingency_ids='GEN') >>> security_analysis.add_precontingency_monitored_elements(branch_ids=['NHV1_NHV2_2']) >>> results = security_analysis.run_ac(network) >>> results.bus_results @@ -69,8 +69,8 @@ Information can be obtained on buses, branches and three windings transformers. p1 q1 i1 p2 q2 i2 flow_transfer contingency_id operator_strategy_id branch_id NHV1_NHV2_2 302.44 98.74 456.77 -300.43 -137.19 488.99 NaN - NGEN_NHV1 NHV1_NHV2_1 301.06 0.00 302.80 -300.19 -116.60 326.75 NaN - NHV1_NHV2_2 301.06 0.00 302.80 -300.19 -116.60 326.75 NaN + GEN NHV1_NHV2_1 302.44 98.74 456.77 -300.43 -137.19 488.99 NaN + NHV1_NHV2_2 302.44 98.74 456.77 -300.43 -137.19 488.99 NaN NHV1_NHV2_1 NHV1_NHV2_2 610.56 334.06 1,008.93 -601.00 -285.38 1,047.83 NaN It also possible to get flow transfer on monitored branches in case of N-1 branch contingencies: diff --git a/docs/user_guide/sensitivity.rst b/docs/user_guide/sensitivity.rst index ba08f0f487..cd3c0dd659 100644 --- a/docs/user_guide/sensitivity.rst +++ b/docs/user_guide/sensitivity.rst @@ -315,7 +315,7 @@ and postcontingency_branch_flow_factor_matrix methods. LOAD -0.5 -0.5 >>> result.get_sensitivity_matrix('postcontingency', 'NHV1_NHV2_1') NHV1_NHV2_1 NHV1_NHV2_2 - GEN 0.0 0.0 + GEN 0.0 -0.0 Advanced sensitivity analysis factors configuration --------------------------------------------------- diff --git a/docs/user_guide/voltage_initializer.rst b/docs/user_guide/voltage_initializer.rst index be1ae654b4..ec74b12bc3 100644 --- a/docs/user_guide/voltage_initializer.rst +++ b/docs/user_guide/voltage_initializer.rst @@ -8,7 +8,7 @@ Prerequisites For now the voltage initializer tool rely on Ampl and Knitro. the binary knitroampl must be in your PATH. -The pypowsybl config file (generally located at ~/.itools/config.yaml) must define the ampl section to find your dynawaltz installation and defaults parameters +The pypowsybl config file (generally located at ~/.itools/config.yaml) must define the ampl section to find your dynawo installation and defaults parameters Here is an example of a simple config.yaml file. .. code-block:: yaml+jinja diff --git a/integration_tests/test_dynawo.py b/integration_tests/test_dynawo.py new file mode 100644 index 0000000000..722ecdcdb0 --- /dev/null +++ b/integration_tests/test_dynawo.py @@ -0,0 +1,48 @@ +# +# Copyright (c) 2024, RTE (http://www.rte-france.com) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# +import pypowsybl as pp +import pypowsybl.dynamic as dyn +import pypowsybl.report as rp +import pandas as pd + + +def test_simulation(): + """ + Running that test requires to have installed dynawo, + and configured its path in your config.yml. + """ + network = pp.network.create_ieee14() + report_node = rp.Reporter() + + model_mapping = dyn.ModelMapping() + model_mapping.add_base_load(static_id='B3-L', parameter_set_id='LAB', dynamic_model_id='BBM_LOAD', model_name='LoadAlphaBeta') + + generator_mapping_df = pd.DataFrame( + index=pd.Series(name='static_id', data=['B6-G', 'B8-G']), + data={ + 'dynamic_model_id': ['BBM_GEN6', 'BBM_GEN8'], + 'parameter_set_id': ['GSTWPR_GEN____6_SM', 'GSTWPR_GEN____8_SM'], + 'model_name': 'GeneratorSynchronousThreeWindingsProportionalRegulations' + } + ) + model_mapping.add_synchronous_generator(generator_mapping_df) + + event_mapping = dyn.EventMapping() + event_mapping.add_disconnection(static_id='L1-2-1', start_time=5, disconnect_only='TWO') + event_mapping.add_active_power_variation(static_id='B3-L', start_time=4, delta_p=0.02) + + curves_mapping = dyn.CurveMapping() + curves_mapping.add_curves('BBM_LOAD', ['load_PPu', 'load_QPu']) + + sim = dyn.Simulation() + res = sim.run(network, model_mapping, event_mapping, curves_mapping, 0, 100, report_node) + + assert report_node + assert 'Ok' == res.status() + assert 'BBM_LOAD_load_PPu' in res.curves() + assert 'BBM_LOAD_load_QPu' in res.curves() diff --git a/java/pom.xml b/java/pom.xml index 1eb93778dc..21795c9f3e 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -58,7 +58,7 @@ jar - 1.7.0-SNAPSHOT + 1.9.0-SNAPSHOT 17 @@ -68,7 +68,7 @@ 5.10.0 3.0.8 3.6.0 - 2024.3.0 + 2024.3.1 0.7.0 1.5.5-3 @@ -169,7 +169,6 @@ - ch.qos.logback logback-classic @@ -222,6 +221,10 @@ com.powsybl powsybl-cgmes-model + + com.powsybl + powsybl-cgmes-extensions + com.powsybl powsybl-ieee-cdf-converter @@ -304,6 +307,10 @@ com.powsybl powsybl-entsoe-commons + + com.powsybl + powsybl-iidm-geodata + com.powsybl powsybl-iidm-impl diff --git a/java/src/main/java/com/powsybl/dataframe/AbstractDataframeMapper.java b/java/src/main/java/com/powsybl/dataframe/AbstractDataframeMapper.java index 0b186d58cd..f11c79177d 100644 --- a/java/src/main/java/com/powsybl/dataframe/AbstractDataframeMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/AbstractDataframeMapper.java @@ -23,7 +23,7 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public abstract class AbstractDataframeMapper implements DataframeMapper { diff --git a/java/src/main/java/com/powsybl/dataframe/BaseDataframeMapperBuilder.java b/java/src/main/java/com/powsybl/dataframe/BaseDataframeMapperBuilder.java index 1ddceb886d..b31e88e81a 100644 --- a/java/src/main/java/com/powsybl/dataframe/BaseDataframeMapperBuilder.java +++ b/java/src/main/java/com/powsybl/dataframe/BaseDataframeMapperBuilder.java @@ -22,7 +22,7 @@ * Base class for builders of mappers. It uses recursive generic pattern to return the actual * builder class. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class BaseDataframeMapperBuilder> { diff --git a/java/src/main/java/com/powsybl/dataframe/BooleanSeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/BooleanSeriesMapper.java index b44dc70820..a70282937b 100644 --- a/java/src/main/java/com/powsybl/dataframe/BooleanSeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/BooleanSeriesMapper.java @@ -11,7 +11,7 @@ import java.util.function.Predicate; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class BooleanSeriesMapper implements SeriesMapper { diff --git a/java/src/main/java/com/powsybl/dataframe/DataframeElementType.java b/java/src/main/java/com/powsybl/dataframe/DataframeElementType.java index e70ee1ffc8..32bc7d7a1b 100644 --- a/java/src/main/java/com/powsybl/dataframe/DataframeElementType.java +++ b/java/src/main/java/com/powsybl/dataframe/DataframeElementType.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public enum DataframeElementType { BUS, @@ -18,11 +18,13 @@ public enum DataframeElementType { THREE_WINDINGS_TRANSFORMER, GENERATOR, LOAD, + GROUND, BATTERY, SHUNT_COMPENSATOR, NON_LINEAR_SHUNT_COMPENSATOR_SECTION, LINEAR_SHUNT_COMPENSATOR_SECTION, DANGLING_LINE, + DANGLING_LINE_GENERATION, TIE_LINE, LCC_CONVERTER_STATION, VSC_CONVERTER_STATION, @@ -38,11 +40,16 @@ public enum DataframeElementType { PHASE_TAP_CHANGER, REACTIVE_CAPABILITY_CURVE_POINT, OPERATIONAL_LIMITS, + SELECTED_OPERATIONAL_LIMITS, MINMAX_REACTIVE_LIMITS, ALIAS, IDENTIFIABLE, INJECTION, BRANCH, TERMINAL, - SUB_NETWORK + SUB_NETWORK, + AREA, + AREA_VOLTAGE_LEVELS, + AREA_BOUNDARIES, + INTERNAL_CONNECTION } diff --git a/java/src/main/java/com/powsybl/dataframe/DataframeFilter.java b/java/src/main/java/com/powsybl/dataframe/DataframeFilter.java index 33c0beb36f..dcd8cbd7f2 100644 --- a/java/src/main/java/com/powsybl/dataframe/DataframeFilter.java +++ b/java/src/main/java/com/powsybl/dataframe/DataframeFilter.java @@ -17,8 +17,8 @@ /** * Define filters to apply to a dataframe. * - * @author Massimo Ferraro - * @author Christian Biasuzzi + * @author Massimo Ferraro {@literal } + * @author Christian Biasuzzi {@literal } */ public class DataframeFilter { diff --git a/java/src/main/java/com/powsybl/dataframe/DataframeHandler.java b/java/src/main/java/com/powsybl/dataframe/DataframeHandler.java index 33aa183d1c..56ab5108ac 100644 --- a/java/src/main/java/com/powsybl/dataframe/DataframeHandler.java +++ b/java/src/main/java/com/powsybl/dataframe/DataframeHandler.java @@ -11,7 +11,7 @@ * Receives series data, is in charge of doing something with it, * typically writing to a data structure. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public interface DataframeHandler { diff --git a/java/src/main/java/com/powsybl/dataframe/DataframeMapper.java b/java/src/main/java/com/powsybl/dataframe/DataframeMapper.java index bfbaef5905..9964f76931 100644 --- a/java/src/main/java/com/powsybl/dataframe/DataframeMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/DataframeMapper.java @@ -19,7 +19,7 @@ * The dataframe data can be read by a {@link DataframeHandler}, * and provided by variants of "indexed series". * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public interface DataframeMapper { diff --git a/java/src/main/java/com/powsybl/dataframe/DataframeMapperBuilder.java b/java/src/main/java/com/powsybl/dataframe/DataframeMapperBuilder.java index 284dd7e0ba..94631c119d 100644 --- a/java/src/main/java/com/powsybl/dataframe/DataframeMapperBuilder.java +++ b/java/src/main/java/com/powsybl/dataframe/DataframeMapperBuilder.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class DataframeMapperBuilder extends BaseDataframeMapperBuilder> { diff --git a/java/src/main/java/com/powsybl/dataframe/DoubleIndexedSeries.java b/java/src/main/java/com/powsybl/dataframe/DoubleIndexedSeries.java index 0b646ca8bf..a491f4a875 100644 --- a/java/src/main/java/com/powsybl/dataframe/DoubleIndexedSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/DoubleIndexedSeries.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public interface DoubleIndexedSeries { diff --git a/java/src/main/java/com/powsybl/dataframe/DoubleSeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/DoubleSeriesMapper.java index 120f9efd7d..177878d29a 100644 --- a/java/src/main/java/com/powsybl/dataframe/DoubleSeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/DoubleSeriesMapper.java @@ -11,7 +11,7 @@ import java.util.function.ToDoubleBiFunction; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class DoubleSeriesMapper implements SeriesMapper { diff --git a/java/src/main/java/com/powsybl/dataframe/EnumSeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/EnumSeriesMapper.java index d7bd7c517c..4768444d44 100644 --- a/java/src/main/java/com/powsybl/dataframe/EnumSeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/EnumSeriesMapper.java @@ -13,7 +13,7 @@ import java.util.function.Function; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class EnumSeriesMapper> implements SeriesMapper { diff --git a/java/src/main/java/com/powsybl/dataframe/IndexedSeries.java b/java/src/main/java/com/powsybl/dataframe/IndexedSeries.java index 8788da0b95..0ef601014f 100644 --- a/java/src/main/java/com/powsybl/dataframe/IndexedSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/IndexedSeries.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public interface IndexedSeries { diff --git a/java/src/main/java/com/powsybl/dataframe/IntIndexedSeries.java b/java/src/main/java/com/powsybl/dataframe/IntIndexedSeries.java index 7f06f1221b..96aa3e6c6c 100644 --- a/java/src/main/java/com/powsybl/dataframe/IntIndexedSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/IntIndexedSeries.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public interface IntIndexedSeries { diff --git a/java/src/main/java/com/powsybl/dataframe/IntSeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/IntSeriesMapper.java index 7927519463..81dd55e7ce 100644 --- a/java/src/main/java/com/powsybl/dataframe/IntSeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/IntSeriesMapper.java @@ -11,7 +11,7 @@ import java.util.function.ToIntFunction; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class IntSeriesMapper implements SeriesMapper { diff --git a/java/src/main/java/com/powsybl/dataframe/MappingUtils.java b/java/src/main/java/com/powsybl/dataframe/MappingUtils.java index 5ac218125c..d837b4a9e9 100644 --- a/java/src/main/java/com/powsybl/dataframe/MappingUtils.java +++ b/java/src/main/java/com/powsybl/dataframe/MappingUtils.java @@ -15,7 +15,7 @@ * Utility methods to help mapping objects to values. * * @author Geoffroy Jamgotchian {@literal } - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public final class MappingUtils { diff --git a/java/src/main/java/com/powsybl/dataframe/SeriesDataType.java b/java/src/main/java/com/powsybl/dataframe/SeriesDataType.java index 934b003a4f..923c98642b 100644 --- a/java/src/main/java/com/powsybl/dataframe/SeriesDataType.java +++ b/java/src/main/java/com/powsybl/dataframe/SeriesDataType.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public enum SeriesDataType { STRING, diff --git a/java/src/main/java/com/powsybl/dataframe/SeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/SeriesMapper.java index f45d67299f..b76c6a433b 100644 --- a/java/src/main/java/com/powsybl/dataframe/SeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/SeriesMapper.java @@ -14,7 +14,7 @@ * It defines a name for the series, a way to retrieve data from the underlying objects * as a series, and a way to write data to the objects from a series input. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public interface SeriesMapper { diff --git a/java/src/main/java/com/powsybl/dataframe/SeriesMetadata.java b/java/src/main/java/com/powsybl/dataframe/SeriesMetadata.java index 43078f39f2..2b5de81591 100644 --- a/java/src/main/java/com/powsybl/dataframe/SeriesMetadata.java +++ b/java/src/main/java/com/powsybl/dataframe/SeriesMetadata.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class SeriesMetadata { diff --git a/java/src/main/java/com/powsybl/dataframe/StringSeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/StringSeriesMapper.java index 8f4eaf2621..ad3589006c 100644 --- a/java/src/main/java/com/powsybl/dataframe/StringSeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/StringSeriesMapper.java @@ -12,7 +12,7 @@ import java.util.function.Function; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class StringSeriesMapper implements SeriesMapper { diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/CurvesSeries.java b/java/src/main/java/com/powsybl/dataframe/dynamic/CurvesSeries.java index b8ccf0dcbe..fddc2f4a89 100644 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/CurvesSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/CurvesSeries.java @@ -13,7 +13,7 @@ import com.powsybl.timeseries.TimeSeries; /** - * @author Nicolas Pierre + * @author Nicolas Pierre {@literal } */ public final class CurvesSeries { diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/DynamicModelSeriesUtils.java b/java/src/main/java/com/powsybl/dataframe/dynamic/DynamicModelSeriesUtils.java new file mode 100644 index 0000000000..624d655c98 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/DynamicModelSeriesUtils.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; + +import java.util.*; +import java.util.function.Consumer; + +/** + * @author Laurent Issertial {@literal } + */ +public final class DynamicModelSeriesUtils { + + private DynamicModelSeriesUtils() { + } + + public static Map> createIdMap(UpdatingDataframe dataframe, String indexColumn, String idColumn) { + if (dataframe.getRowCount() == 0) { + return Collections.emptyMap(); + } + StringSeries joinIds = dataframe.getStrings(indexColumn); + if (joinIds == null) { + throw new PowsyblException("Join dataframe: %s column is not set".formatted(indexColumn)); + } + StringSeries ids = dataframe.getStrings(idColumn); + if (ids == null) { + throw new PowsyblException("Join dataframe: %s column is not set".formatted(idColumn)); + } + Map> idMap = new HashMap<>(); + for (int index = 0; index < dataframe.getRowCount(); index++) { + String joinId = joinIds.get(index); + idMap.computeIfAbsent(joinId, k -> new ArrayList<>()) + .add(ids.get(index)); + } + return idMap; + } + + public static void applyIfPresent(Map> idMap, String id, Consumer> consumer) { + List idList = idMap.get(id); + if (idList != null) { + consumer.accept(idList); + } + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/PersistentDoubleSeries.java b/java/src/main/java/com/powsybl/dataframe/dynamic/PersistentDoubleSeries.java new file mode 100644 index 0000000000..72c16a5f56 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/PersistentDoubleSeries.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic; + +import com.powsybl.dataframe.update.DoubleSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Laurent Issertial {@literal } + */ +public final class PersistentDoubleSeries implements DoubleSeries { + + private final List values; + + public static PersistentDoubleSeries copyOf(UpdatingDataframe dataframe, String columnName) { + DoubleSeries series = dataframe.getDoubles(columnName); + if (series == null) { + return null; + } + int rowCount = dataframe.getRowCount(); + List values = new ArrayList<>(rowCount); + for (int row = 0; row < rowCount; row++) { + values.add(series.get(row)); + } + return new PersistentDoubleSeries(values); + } + + private PersistentDoubleSeries(List values) { + this.values = values; + } + + @Override + public double get(int index) { + return values.get(index); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/PersistentStringSeries.java b/java/src/main/java/com/powsybl/dataframe/dynamic/PersistentStringSeries.java new file mode 100644 index 0000000000..6163fe84be --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/PersistentStringSeries.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic; + +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Laurent Issertial {@literal } + */ +public final class PersistentStringSeries implements StringSeries { + + private final List values; + + public static PersistentStringSeries copyOf(UpdatingDataframe dataframe, String columnName) { + StringSeries series = dataframe.getStrings(columnName); + if (series == null) { + return null; + } + int rowCount = dataframe.getRowCount(); + List values = new ArrayList<>(rowCount); + for (int row = 0; row < rowCount; row++) { + values.add(series.get(row)); + } + return new PersistentStringSeries(values); + } + + private PersistentStringSeries(List values) { + this.values = values; + } + + @Override + public String get(int index) { + return values.get(index); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractAutomationSystemSeries.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractAutomationSystemSeries.java new file mode 100644 index 0000000000..c5d9e6c20f --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractAutomationSystemSeries.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.models.automationsystems.AbstractAutomationSystemModelBuilder; + +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +abstract class AbstractAutomationSystemSeries> extends AbstractDynamicModelSeries { + + AbstractAutomationSystemSeries(UpdatingDataframe dataframe) { + super(dataframe); + } + + protected void applyOnBuilder(int row, T builder) { + applyIfPresent(dynamicModelIds, row, builder::dynamicModelId); + applyIfPresent(parameterSetIds, row, builder::parameterSetId); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractBlackBoxAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractBlackBoxAdder.java deleted file mode 100644 index 44a192fced..0000000000 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractBlackBoxAdder.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.dataframe.dynamic.adders; - -import java.util.List; - -import com.powsybl.dataframe.SeriesMetadata; -import com.powsybl.dataframe.network.adders.SeriesUtils; -import com.powsybl.dataframe.update.StringSeries; -import com.powsybl.dataframe.update.UpdatingDataframe; -import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; - -/** - * @author Nicolas Pierre - */ -public abstract class AbstractBlackBoxAdder implements DynamicMappingAdder { - private static final List METADATA = List.of( - SeriesMetadata.stringIndex("static_id"), - SeriesMetadata.strings("parameter_set_id")); - - @Override - public List getMetadata() { - return METADATA; - } - - private static final class BlackBoxSeries { - - private final StringSeries staticId; - private final StringSeries parameterSetId; - - BlackBoxSeries(UpdatingDataframe dataframe) { - this.staticId = SeriesUtils.getRequiredStrings(dataframe, "static_id"); - this.parameterSetId = SeriesUtils.getRequiredStrings(dataframe, "parameter_set_id"); - } - - public StringSeries getStaticId() { - return staticId; - } - - public StringSeries getParameterSetId() { - return parameterSetId; - } - - } - - @FunctionalInterface - protected interface AddBlackBoxToModelMapping { - void addToModel(PythonDynamicModelsSupplier modelMapping, String staticId, String parameterSetId); - } - - protected abstract AddBlackBoxToModelMapping getAddBlackBoxToModelMapping(); - - @Override - public void addElements(PythonDynamicModelsSupplier modelMapping, UpdatingDataframe dataframe) { - BlackBoxSeries series = new BlackBoxSeries(dataframe); - AddBlackBoxToModelMapping adder = getAddBlackBoxToModelMapping(); - for (int row = 0; row < dataframe.getRowCount(); row++) { - adder.addToModel(modelMapping, series.getStaticId().get(row), series.getParameterSetId().get(row)); - } - } - -} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractDynamicModelSeries.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractDynamicModelSeries.java new file mode 100644 index 0000000000..5485d616a6 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractDynamicModelSeries.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynamicsimulation.DynamicModel; +import com.powsybl.dynawo.builders.ModelBuilder; +import com.powsybl.iidm.network.Network; + +import java.util.function.BiFunction; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; + +/** + * @author Laurent Issertial {@literal } + */ +abstract class AbstractDynamicModelSeries> implements DynamicModelSeries { + + protected final StringSeries dynamicModelIds; + protected final StringSeries parameterSetIds; + protected final StringSeries modelsNames; + + AbstractDynamicModelSeries(UpdatingDataframe dataframe) { + this.dynamicModelIds = PersistentStringSeries.copyOf(dataframe, DYNAMIC_MODEL_ID); + this.parameterSetIds = PersistentStringSeries.copyOf(dataframe, PARAMETER_SET_ID); + this.modelsNames = PersistentStringSeries.copyOf(dataframe, MODEL_NAME); + } + + @Override + public BiFunction getModelSupplier(int row) { + return (network, reportNode) -> { + T builder = getBuilder(network, reportNode, row); + if (builder == null) { + return null; + } + applyOnBuilder(row, builder); + return builder.build(); + }; + } + + protected abstract void applyOnBuilder(int row, T builder); + + protected T getBuilder(Network network, ReportNode reportNode, int row) { + String modelName = modelsNames != null ? modelsNames.get(row) : null; + return modelName == null || modelName.isEmpty() ? createBuilder(network, reportNode) + : createBuilder(network, modelName, reportNode); + } + + protected abstract T createBuilder(Network network, ReportNode reportNode); + + protected abstract T createBuilder(Network network, String modelName, ReportNode reportNode); +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEquipmentAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEquipmentAdder.java new file mode 100644 index 0000000000..5c64a79658 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEquipmentAdder.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.dataframe.SeriesMetadata; + +import java.util.Collections; +import java.util.List; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; + +/** + * @author Laurent Issertial {@literal } + */ +abstract class AbstractEquipmentAdder extends AbstractSimpleDynamicModelAdder { + + protected static final List EQUIPMENT_METADATA = List.of( + SeriesMetadata.stringIndex(STATIC_ID), + SeriesMetadata.strings(PARAMETER_SET_ID), + SeriesMetadata.strings(DYNAMIC_MODEL_ID), + SeriesMetadata.strings(MODEL_NAME)); + + @Override + public List> getMetadata() { + return Collections.singletonList(EQUIPMENT_METADATA); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEquipmentSeries.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEquipmentSeries.java new file mode 100644 index 0000000000..5e133274cb --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEquipmentSeries.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.EquipmentModelBuilder; +import com.powsybl.iidm.network.Identifiable; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +abstract class AbstractEquipmentSeries, B extends EquipmentModelBuilder> extends AbstractDynamicModelSeries { + + protected final StringSeries staticIds; + + AbstractEquipmentSeries(UpdatingDataframe dataframe) { + super(dataframe); + this.staticIds = PersistentStringSeries.copyOf(dataframe, STATIC_ID); + } + + protected void applyOnBuilder(int row, B builder) { + applyIfPresent(staticIds, row, builder::staticId); + applyIfPresent(parameterSetIds, row, builder::parameterSetId); + applyIfPresent(dynamicModelIds, row, builder::dynamicModelId); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEventModelAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEventModelAdder.java new file mode 100644 index 0000000000..805644fa54 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEventModelAdder.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.python.dynamic.PythonEventModelsSupplier; + +/** + * @author Laurent Issertial {@literal } + */ +abstract class AbstractEventModelAdder implements EventMappingAdder { + + @Override + public void addElements(PythonEventModelsSupplier modelMapping, UpdatingDataframe dataframe) { + EventModelSeries series = createEventModelSeries(dataframe); + for (int row = 0; row < dataframe.getRowCount(); row++) { + modelMapping.addModel(series.getModelSupplier(row)); + } + } + + protected abstract EventModelSeries createEventModelSeries(UpdatingDataframe dataframe); +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEventModelSeries.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEventModelSeries.java new file mode 100644 index 0000000000..85fff326d6 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractEventModelSeries.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dynamicsimulation.EventModel; +import com.powsybl.dynawo.builders.ModelBuilder; +import com.powsybl.iidm.network.Network; + +import java.util.function.BiFunction; + +/** + * @author Laurent Issertial {@literal } + */ +abstract class AbstractEventModelSeries> implements EventModelSeries { + + @Override + public BiFunction getModelSupplier(int row) { + return (network, reportNode) -> { + T builder = createBuilder(network, reportNode); + if (builder == null) { + return null; + } + applyOnBuilder(row, builder); + return builder.build(); + }; + } + + protected abstract void applyOnBuilder(int row, T builder); + + protected abstract T createBuilder(Network network, ReportNode reportNode); +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractSimpleDynamicModelAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractSimpleDynamicModelAdder.java new file mode 100644 index 0000000000..0c4280e011 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AbstractSimpleDynamicModelAdder.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; + +import java.util.List; + +/** + * @author Laurent Issertial {@literal } + */ +abstract class AbstractSimpleDynamicModelAdder implements DynamicMappingAdder { + + @Override + public void addElements(PythonDynamicModelsSupplier modelMapping, List dataframes) { + if (dataframes.size() != 1) { + throw new IllegalArgumentException("Expected only one input dataframe"); + } + UpdatingDataframe dataframe = dataframes.get(0); + DynamicModelSeries series = createDynamicModelSeries(dataframe); + for (int row = 0; row < dataframe.getRowCount(); row++) { + modelMapping.addModel(series.getModelSupplier(row)); + } + } + + protected abstract DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe); +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/ActivePowerVariationAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/ActivePowerVariationAdder.java new file mode 100644 index 0000000000..ea89bf3c2b --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/ActivePowerVariationAdder.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.dynamic.PersistentDoubleSeries; +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.DoubleSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.models.events.EventActivePowerVariationBuilder; +import com.powsybl.iidm.network.Network; + +import java.util.List; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +public class ActivePowerVariationAdder extends AbstractEventModelAdder { + + protected static final List METADATA = List.of( + SeriesMetadata.stringIndex(STATIC_ID), + SeriesMetadata.doubles(START_TIME), + SeriesMetadata.doubles(DELTA_P)); + + @Override + public List getMetadata() { + return METADATA; + } + + private static class ActivePowerVariationSeries extends AbstractEventModelSeries { + + private final StringSeries staticIds; + private final DoubleSeries startTimes; + private final DoubleSeries deltaPs; + + ActivePowerVariationSeries(UpdatingDataframe dataframe) { + this.staticIds = PersistentStringSeries.copyOf(dataframe, STATIC_ID); + this.startTimes = PersistentDoubleSeries.copyOf(dataframe, START_TIME); + this.deltaPs = PersistentDoubleSeries.copyOf(dataframe, DELTA_P); + } + + @Override + protected void applyOnBuilder(int row, EventActivePowerVariationBuilder builder) { + applyIfPresent(staticIds, row, builder::staticId); + applyIfPresent(startTimes, row, builder::startTime); + applyIfPresent(deltaPs, row, builder::deltaP); + } + + @Override + protected EventActivePowerVariationBuilder createBuilder(Network network, ReportNode reportNode) { + return EventActivePowerVariationBuilder.of(network, reportNode); + } + } + + @Override + protected EventModelSeries createEventModelSeries(UpdatingDataframe dataframe) { + return new ActivePowerVariationSeries(dataframe); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AlphaBetaLoadAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AlphaBetaLoadAdder.java deleted file mode 100644 index af77710715..0000000000 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/AlphaBetaLoadAdder.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.dataframe.dynamic.adders; - -import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; - -/** - * @author Nicolas Pierre - */ -public class AlphaBetaLoadAdder extends AbstractBlackBoxAdder { - - @Override - protected AddBlackBoxToModelMapping getAddBlackBoxToModelMapping() { - return PythonDynamicModelsSupplier::addAlphaBetaLoad; - } - -} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/BaseBusAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/BaseBusAdder.java new file mode 100644 index 0000000000..3019cb1e89 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/BaseBusAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.buses.StandardBusBuilder; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class BaseBusAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return StandardBusBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected StandardBusBuilder createBuilder(Network network, ReportNode reportNode) { + return StandardBusBuilder.of(network, reportNode); + } + + @Override + protected StandardBusBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return StandardBusBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/BaseGeneratorAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/BaseGeneratorAdder.java new file mode 100644 index 0000000000..c2afde3a6f --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/BaseGeneratorAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.generators.BaseGeneratorBuilder; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class BaseGeneratorAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return BaseGeneratorBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected BaseGeneratorBuilder createBuilder(Network network, ReportNode reportNode) { + return BaseGeneratorBuilder.of(network, reportNode); + } + + @Override + protected BaseGeneratorBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return BaseGeneratorBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/BaseLoadAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/BaseLoadAdder.java new file mode 100644 index 0000000000..bd9d94b95a --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/BaseLoadAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.loads.BaseLoadBuilder; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class BaseLoadAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return BaseLoadBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected BaseLoadBuilder createBuilder(Network network, ReportNode reportNode) { + return BaseLoadBuilder.of(network, reportNode); + } + + @Override + protected BaseLoadBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return BaseLoadBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/CurrentLimitAutomatonAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/CurrentLimitAutomatonAdder.java deleted file mode 100644 index 77192951b5..0000000000 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/CurrentLimitAutomatonAdder.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.dataframe.dynamic.adders; - -import java.util.List; - -import com.powsybl.dataframe.SeriesMetadata; -import com.powsybl.dataframe.network.adders.SeriesUtils; -import com.powsybl.dataframe.update.IntSeries; -import com.powsybl.dataframe.update.StringSeries; -import com.powsybl.dataframe.update.UpdatingDataframe; -import com.powsybl.python.commons.PyPowsyblApiHeader; -import com.powsybl.python.commons.Util; -import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; - -/** - * @author Nicolas Pierre - */ -public class CurrentLimitAutomatonAdder implements DynamicMappingAdder { - private static final List METADATA = List.of( - SeriesMetadata.stringIndex("static_id"), - SeriesMetadata.strings("parameter_set_id"), - SeriesMetadata.ints("branch_side")); - - @Override - public List getMetadata() { - return METADATA; - } - - private static final class CurrentLimitAutomatonSeries { - - private final StringSeries staticId; - private final StringSeries parameterSetId; - private final IntSeries branchSide; - - CurrentLimitAutomatonSeries(UpdatingDataframe dataframe) { - this.staticId = SeriesUtils.getRequiredStrings(dataframe, "static_id"); - this.parameterSetId = SeriesUtils.getRequiredStrings(dataframe, "parameter_set_id"); - this.branchSide = SeriesUtils.getRequiredInts(dataframe, "branch_side"); - } - - public StringSeries getStaticId() { - return staticId; - } - - public StringSeries getParameterSetId() { - return parameterSetId; - } - - public IntSeries getBranchSide() { - return branchSide; - } - - } - - @Override - public void addElements(PythonDynamicModelsSupplier modelMapping, UpdatingDataframe dataframe) { - CurrentLimitAutomatonSeries series = new CurrentLimitAutomatonSeries(dataframe); - for (int row = 0; row < dataframe.getRowCount(); row++) { - modelMapping.addCurrentLimitAutomaton( - series.getStaticId().get(row), - series.getParameterSetId().get(row), - Util.convert(PyPowsyblApiHeader.ThreeSideType.fromCValue(series.getBranchSide().get(row))).toTwoSides()); - } - } - -} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DisconnectAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DisconnectAdder.java new file mode 100644 index 0000000000..aabd39af02 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DisconnectAdder.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.dynamic.PersistentDoubleSeries; +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.DoubleSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.models.events.EventDisconnectionBuilder; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoSides; + +import java.util.List; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +public class DisconnectAdder extends AbstractEventModelAdder { + + protected static final List METADATA = List.of( + SeriesMetadata.stringIndex(STATIC_ID), + SeriesMetadata.doubles(START_TIME), + SeriesMetadata.strings(DISCONNECT_ONLY)); + + @Override + public List getMetadata() { + return METADATA; + } + + private static class DisconnectSeries extends AbstractEventModelSeries { + + private final StringSeries staticIds; + private final DoubleSeries startTimes; + private final StringSeries disconnectOnly; + + DisconnectSeries(UpdatingDataframe dataframe) { + this.staticIds = PersistentStringSeries.copyOf(dataframe, STATIC_ID); + this.startTimes = PersistentDoubleSeries.copyOf(dataframe, START_TIME); + this.disconnectOnly = PersistentStringSeries.copyOf(dataframe, DISCONNECT_ONLY); + } + + @Override + protected void applyOnBuilder(int row, EventDisconnectionBuilder builder) { + applyIfPresent(staticIds, row, builder::staticId); + applyIfPresent(startTimes, row, builder::startTime); + applyIfPresent(disconnectOnly, row, TwoSides.class, builder::disconnectOnly); + } + + @Override + protected EventDisconnectionBuilder createBuilder(Network network, ReportNode reportNode) { + return EventDisconnectionBuilder.of(network, reportNode); + } + } + + @Override + protected EventModelSeries createEventModelSeries(UpdatingDataframe dataframe) { + return new DisconnectSeries(dataframe); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicMappingAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicMappingAdder.java index 262642b3c2..e46ff57f1d 100644 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicMappingAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicMappingAdder.java @@ -9,25 +9,31 @@ import com.powsybl.dataframe.SeriesMetadata; import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; +import java.util.Collection; import java.util.List; /** - * @author Nicolas PIERRE + * @author Nicolas PIERRE {@literal } */ public interface DynamicMappingAdder { /** - * Get the list of metadata + * Get the list of metadata: one list of columns metadata for each input dataframe. */ - List getMetadata(); + List> getMetadata(); /** * Adds elements to the dynamic model mapping, based on a list of dataframes. * The first dataframe is considered the "primary" dataframe, other dataframes - * can provide additional data. + * can provide additional data (e.g. list of transformers id for a Tap Changer Blocking Automation System). */ - void addElements(PythonDynamicModelsSupplier modelMapping, UpdatingDataframe dataframe); + void addElements(PythonDynamicModelsSupplier modelMapping, List dataframe); + /** + * Returns supported model names for the given adder + */ + Collection getSupportedModels(); } diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicMappingAdderFactory.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicMappingAdderFactory.java deleted file mode 100644 index f63896bc69..0000000000 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicMappingAdderFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.dataframe.dynamic.adders; - -import java.util.Map; - -import com.powsybl.python.commons.PyPowsyblApiHeader.DynamicMappingType; - -/** - * @author Nicolas Pierre - */ -public final class DynamicMappingAdderFactory { - - private static final Map ADDERS = Map.ofEntries( - Map.entry(DynamicMappingType.ALPHA_BETA_LOAD, new AlphaBetaLoadAdder()), - Map.entry(DynamicMappingType.ONE_TRANSFORMER_LOAD, new OneTransformerLoadAdder()), - Map.entry(DynamicMappingType.GENERATOR_SYNCHRONOUS_THREE_WINDINGS, - new GeneratorSynchronousThreeWindingsAdder()), - Map.entry(DynamicMappingType.GENERATOR_SYNCHRONOUS_THREE_WINDINGS_PROPORTIONAL_REGULATIONS, - new GeneratorSynchronousThreeWindingsProportionalRegulationsAdder()), - Map.entry(DynamicMappingType.GENERATOR_SYNCHRONOUS_FOUR_WINDINGS, - new GeneratorSynchronousFourWindingsAdder()), - Map.entry(DynamicMappingType.GENERATOR_SYNCHRONOUS_FOUR_WINDINGS_PROPORTIONAL_REGULATIONS, - new GeneratorSynchronousFourWindingsProportionalRegulationsAdder()), - Map.entry(DynamicMappingType.GENERATOR_SYNCHRONOUS, new GeneratorSynchronousAdder()), - Map.entry(DynamicMappingType.CURRENT_LIMIT_AUTOMATON, new CurrentLimitAutomatonAdder())); - - public static DynamicMappingAdder getAdder(DynamicMappingType type) { - return ADDERS.get(type); - } - - private DynamicMappingAdderFactory() { - } -} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicMappingHandler.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicMappingHandler.java new file mode 100644 index 0000000000..2dcc0eb839 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicMappingHandler.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.python.commons.PyPowsyblApiHeader.DynamicMappingType; +import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; + +/** + * @author Nicolas Pierre {@literal } + */ +public final class DynamicMappingHandler { + + private static final Map ADDERS = Map.ofEntries( + // Equipments + Map.entry(DynamicMappingType.BASE_LOAD, new BaseLoadAdder()), + Map.entry(DynamicMappingType.LOAD_ONE_TRANSFORMER, new LoadOneTransformerAdder()), + Map.entry(DynamicMappingType.LOAD_ONE_TRANSFORMER_TAP_CHANGER, new LoadOneTransformerTapChangerAdder()), + Map.entry(DynamicMappingType.LOAD_TWO_TRANSFORMERS, new LoadTwoTransformersAdder()), + Map.entry(DynamicMappingType.LOAD_TWO_TRANSFORMERS_TAP_CHANGERS, new LoadTwoTransformersTapChangersAdder()), + Map.entry(DynamicMappingType.BASE_GENERATOR, new BaseGeneratorAdder()), + Map.entry(DynamicMappingType.SYNCHRONIZED_GENERATOR, new SynchronizedGeneratorAdder()), + Map.entry(DynamicMappingType.SYNCHRONOUS_GENERATOR, new SynchronousGeneratorAdder()), + Map.entry(DynamicMappingType.WECC, new WeccAdder()), + Map.entry(DynamicMappingType.GRID_FORMING_CONVERTER, new GridFormingConverterAdder()), + Map.entry(DynamicMappingType.SIGNAL_N_GENERATOR, new SignalNGeneratorAdder()), + Map.entry(DynamicMappingType.HVDC_P, new HvdcPAdder()), + Map.entry(DynamicMappingType.HVDC_VSC, new HvdcVscAdder()), + Map.entry(DynamicMappingType.BASE_TRANSFORMER, new TransformerAdder()), + Map.entry(DynamicMappingType.BASE_STATIC_VAR_COMPENSATOR, new StaticVarCompensatorAdder()), + Map.entry(DynamicMappingType.BASE_LINE, new LineAdder()), + Map.entry(DynamicMappingType.BASE_BUS, new BaseBusAdder()), + Map.entry(DynamicMappingType.INFINITE_BUS, new InfiniteBusAdder()), + // Automation systems + Map.entry(DynamicMappingType.OVERLOAD_MANAGEMENT_SYSTEM, new DynamicOverloadManagementSystemAdder()), + Map.entry(DynamicMappingType.TWO_LEVELS_OVERLOAD_MANAGEMENT_SYSTEM, new DynamicTwoLevelsOverloadManagementSystemAdder()), + Map.entry(DynamicMappingType.PHASE_SHIFTER_I, new PhaseShifterIAdder()), + Map.entry(DynamicMappingType.PHASE_SHIFTER_P, new PhaseShifterPAdder()), + Map.entry(DynamicMappingType.PHASE_SHIFTER_BLOCKING_I, new PhaseShifterBlockingIAdder()), + Map.entry(DynamicMappingType.UNDER_VOLTAGE, new UnderVoltageAutomationSystemAdder()), + Map.entry(DynamicMappingType.TAP_CHANGER, new TapChangerAutomationSystemAdder()), + Map.entry(DynamicMappingType.TAP_CHANGER_BLOCKING, new TapChangerBlockingAutomationSystemAdder())); + + public static void addElements(DynamicMappingType type, PythonDynamicModelsSupplier modelMapping, List dataframes) { + ADDERS.get(type).addElements(modelMapping, dataframes); + } + + public static List> getMetadata(DynamicMappingType type) { + return ADDERS.get(type).getMetadata(); + } + + public static Collection getSupportedModels(DynamicMappingType type) { + return ADDERS.get(type).getSupportedModels().stream().map(ModelInfo::name).toList(); + } + + private DynamicMappingHandler() { + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicModelDataframeConstants.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicModelDataframeConstants.java new file mode 100644 index 0000000000..c3524f0881 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicModelDataframeConstants.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +/** + * @author Laurent Issertial {@literal } + */ +public final class DynamicModelDataframeConstants { + + public static final String DYNAMIC_MODEL_ID = "dynamic_model_id"; + public static final String STATIC_ID = "static_id"; + public static final String PARAMETER_SET_ID = "parameter_set_id"; + public static final String MODEL_NAME = "model_name"; + public static final String CONTROLLED_BRANCH = "controlled_branch"; + public static final String I_MEASUREMENT = "i_measurement"; + public static final String I_MEASUREMENT_SIDE = "i_measurement_side"; + public static final String I_MEASUREMENT_1 = "i_measurement_1"; + public static final String I_MEASUREMENT_1_SIDE = "i_measurement_1_side"; + public static final String I_MEASUREMENT_2 = "i_measurement_2"; + public static final String I_MEASUREMENT_2_SIDE = "i_measurement_2_side"; + public static final String GENERATOR = "generator"; + public static final String TRANSFORMER = "transformer"; + public static final String SIDE = "side"; + public static final String MEASUREMENT_POINT_ID = "measurement_point_id"; + public static final String TRANSFORMER_ID = "transformer_id"; + public static final String START_TIME = "start_time"; + public static final String DISCONNECT_ONLY = "disconnect_only"; + public static final String DELTA_P = "delta_p"; + public static final String FAULT_TIME = "fault_time"; + public static final String X_PU = "x_pu"; + public static final String R_PU = "r_pu"; + public static final String PHASE_SHIFTER_ID = "phase_shifter_id"; + + private DynamicModelDataframeConstants() { + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicModelSeries.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicModelSeries.java new file mode 100644 index 0000000000..f0b2fab169 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicModelSeries.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dynamicsimulation.DynamicModel; +import com.powsybl.iidm.network.Network; + +import java.util.function.BiFunction; + +/** + * @author Laurent Issertial {@literal } + */ +public interface DynamicModelSeries { + + BiFunction getModelSupplier(int row); +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicOverloadManagementSystemAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicOverloadManagementSystemAdder.java new file mode 100644 index 0000000000..b6931429ea --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicOverloadManagementSystemAdder.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.automationsystems.overloadmanagments.DynamicOverloadManagementSystemBuilder; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoSides; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +public class DynamicOverloadManagementSystemAdder extends AbstractSimpleDynamicModelAdder { + + protected static final List METADATA = List.of( + SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID), + SeriesMetadata.strings(PARAMETER_SET_ID), + SeriesMetadata.strings(MODEL_NAME), + SeriesMetadata.strings(CONTROLLED_BRANCH), + SeriesMetadata.strings(I_MEASUREMENT), + SeriesMetadata.strings(I_MEASUREMENT_SIDE)); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + @Override + public Collection getSupportedModels() { + return DynamicOverloadManagementSystemBuilder.getSupportedModelInfos(); + } + + private static class OverloadManagementSystemSeries extends AbstractAutomationSystemSeries { + + private final StringSeries controlledBranch; + private final StringSeries iMeasurement; + private final StringSeries iMeasurementSide; + + OverloadManagementSystemSeries(UpdatingDataframe dataframe) { + super(dataframe); + this.controlledBranch = PersistentStringSeries.copyOf(dataframe, CONTROLLED_BRANCH); + this.iMeasurement = PersistentStringSeries.copyOf(dataframe, I_MEASUREMENT); + this.iMeasurementSide = PersistentStringSeries.copyOf(dataframe, I_MEASUREMENT_SIDE); + } + + @Override + protected void applyOnBuilder(int row, DynamicOverloadManagementSystemBuilder builder) { + super.applyOnBuilder(row, builder); + applyIfPresent(controlledBranch, row, builder::controlledBranch); + applyIfPresent(iMeasurement, row, builder::iMeasurement); + applyIfPresent(iMeasurementSide, row, TwoSides.class, builder::iMeasurementSide); + } + + @Override + protected DynamicOverloadManagementSystemBuilder createBuilder(Network network, ReportNode reportNode) { + return DynamicOverloadManagementSystemBuilder.of(network, reportNode); + } + + @Override + protected DynamicOverloadManagementSystemBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return DynamicOverloadManagementSystemBuilder.of(network, modelName, reportNode); + } + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new OverloadManagementSystemSeries(dataframe); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicTwoLevelsOverloadManagementSystemAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicTwoLevelsOverloadManagementSystemAdder.java new file mode 100644 index 0000000000..f1f8c0ab1b --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/DynamicTwoLevelsOverloadManagementSystemAdder.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.automationsystems.overloadmanagments.DynamicTwoLevelsOverloadManagementSystemBuilder; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoSides; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +public class DynamicTwoLevelsOverloadManagementSystemAdder extends AbstractSimpleDynamicModelAdder { + + protected static final List METADATA = List.of( + SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID), + SeriesMetadata.strings(PARAMETER_SET_ID), + SeriesMetadata.strings(MODEL_NAME), + SeriesMetadata.strings(CONTROLLED_BRANCH), + SeriesMetadata.strings(I_MEASUREMENT_1), + SeriesMetadata.strings(I_MEASUREMENT_1_SIDE), + SeriesMetadata.strings(I_MEASUREMENT_2), + SeriesMetadata.strings(I_MEASUREMENT_2_SIDE)); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + @Override + public Collection getSupportedModels() { + return DynamicTwoLevelsOverloadManagementSystemBuilder.getSupportedModelInfos(); + } + + private static class OverloadManagementSystemSeries extends AbstractAutomationSystemSeries { + + private final StringSeries controlledBranch; + private final StringSeries iMeasurement1; + private final StringSeries iMeasurement1Side; + private final StringSeries iMeasurement2; + private final StringSeries iMeasurement2Side; + + OverloadManagementSystemSeries(UpdatingDataframe dataframe) { + super(dataframe); + this.controlledBranch = PersistentStringSeries.copyOf(dataframe, CONTROLLED_BRANCH); + this.iMeasurement1 = PersistentStringSeries.copyOf(dataframe, I_MEASUREMENT_1); + this.iMeasurement1Side = PersistentStringSeries.copyOf(dataframe, I_MEASUREMENT_1_SIDE); + this.iMeasurement2 = PersistentStringSeries.copyOf(dataframe, I_MEASUREMENT_2); + this.iMeasurement2Side = PersistentStringSeries.copyOf(dataframe, I_MEASUREMENT_2_SIDE); + } + + @Override + protected void applyOnBuilder(int row, DynamicTwoLevelsOverloadManagementSystemBuilder builder) { + super.applyOnBuilder(row, builder); + applyIfPresent(controlledBranch, row, builder::controlledBranch); + applyIfPresent(iMeasurement1, row, builder::iMeasurement1); + applyIfPresent(iMeasurement1Side, row, TwoSides.class, builder::iMeasurement1Side); + applyIfPresent(iMeasurement2, row, builder::iMeasurement2); + applyIfPresent(iMeasurement2Side, row, TwoSides.class, builder::iMeasurement2Side); + } + + @Override + protected DynamicTwoLevelsOverloadManagementSystemBuilder createBuilder(Network network, ReportNode reportNode) { + return DynamicTwoLevelsOverloadManagementSystemBuilder.of(network, reportNode); + } + + @Override + protected DynamicTwoLevelsOverloadManagementSystemBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return DynamicTwoLevelsOverloadManagementSystemBuilder.of(network, modelName, reportNode); + } + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new OverloadManagementSystemSeries(dataframe); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/EventMappingAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/EventMappingAdder.java new file mode 100644 index 0000000000..a5224b902c --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/EventMappingAdder.java @@ -0,0 +1,31 @@ +/** +* Copyright (c) 2024, RTE (http://www.rte-france.com) +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. +* SPDX-License-Identifier: MPL-2.0 +*/ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.python.dynamic.PythonEventModelsSupplier; + +import java.util.List; + +/** + * @author Laurent Issertial {@literal } + */ +public interface EventMappingAdder { + + /** + * Get the list of metadata + */ + List getMetadata(); + + /** + * Adds elements to the event model mapping, based on a list of dataframes. + */ + void addElements(PythonEventModelsSupplier modelMapping, UpdatingDataframe dataframe); + +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/EventMappingHandler.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/EventMappingHandler.java new file mode 100644 index 0000000000..46cccaf03a --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/EventMappingHandler.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.python.commons.PyPowsyblApiHeader.EventMappingType; +import com.powsybl.python.dynamic.PythonEventModelsSupplier; + +import java.util.List; +import java.util.Map; + +/** + * @author Laurent Issertial {@literal } + */ +public final class EventMappingHandler { + + private static final Map ADDERS = Map.ofEntries( + Map.entry(EventMappingType.DISCONNECT, new DisconnectAdder()), + Map.entry(EventMappingType.NODE_FAULT, new NodeFaultAdder()), + Map.entry(EventMappingType.ACTIVE_POWER_VARIATION, new ActivePowerVariationAdder()) + ); + + public static void addElements(EventMappingType type, PythonEventModelsSupplier modelMapping, UpdatingDataframe dataframe) { + ADDERS.get(type).addElements(modelMapping, dataframe); + } + + public static List getMetadata(EventMappingType type) { + return ADDERS.get(type).getMetadata(); + } + + private EventMappingHandler() { + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/EventModelSeries.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/EventModelSeries.java new file mode 100644 index 0000000000..77acc5fa50 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/EventModelSeries.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dynamicsimulation.EventModel; +import com.powsybl.iidm.network.Network; + +import java.util.function.BiFunction; + +/** + * @author Laurent Issertial {@literal } + */ +public interface EventModelSeries { + + BiFunction getModelSupplier(int row); +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousAdder.java deleted file mode 100644 index 053e7e9fe7..0000000000 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousAdder.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.dataframe.dynamic.adders; - -import com.powsybl.dataframe.SeriesMetadata; -import com.powsybl.dataframe.update.StringSeries; -import com.powsybl.dataframe.update.UpdatingDataframe; -import com.powsybl.dynamicsimulation.DynamicModel; -import com.powsybl.dynawo.models.generators.SynchronizedGeneratorBuilder; -import com.powsybl.iidm.network.Network; -import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; - -import java.util.List; -import java.util.function.Function; - -import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; - -/** - * @author Nicolas Pierre - * @author Laurent Issertial {@literal } - */ -public class GeneratorSynchronousAdder implements DynamicMappingAdder { - private static final List METADATA = List.of( - SeriesMetadata.stringIndex("static_id"), - SeriesMetadata.strings("parameter_set_id"), - SeriesMetadata.strings("generator_lib")); - - @Override - public List getMetadata() { - return METADATA; - } - - private static final class GeneratorSynchronousSeries { - - private final StringSeries staticIds; - private final StringSeries parameterSetIds; - private final StringSeries generatorLibs; - - GeneratorSynchronousSeries(UpdatingDataframe dataframe) { - this.staticIds = dataframe.getStrings("static_id"); - this.parameterSetIds = dataframe.getStrings("parameter_set_id"); - this.generatorLibs = dataframe.getStrings("generator_lib"); - } - - public Function getModelSupplier(int row) { - return network -> { - SynchronizedGeneratorBuilder builder = getBuilder(network, row); - applyIfPresent(staticIds, row, builder::staticId); - applyIfPresent(parameterSetIds, row, builder::parameterSetId); - return builder.build(); - }; - } - - private SynchronizedGeneratorBuilder getBuilder(Network network, int row) { - String lib = generatorLibs != null ? generatorLibs.get(row) : null; - return lib != null ? SynchronizedGeneratorBuilder.of(network, lib) - : SynchronizedGeneratorBuilder.of(network); - } - } - - @Override - public void addElements(PythonDynamicModelsSupplier modelMapping, UpdatingDataframe dataframe) { - GeneratorSynchronousSeries series = new GeneratorSynchronousSeries(dataframe); - for (int row = 0; row < dataframe.getRowCount(); row++) { - modelMapping.addModel(series.getModelSupplier(row)); - } - } - -} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousFourWindingsAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousFourWindingsAdder.java deleted file mode 100644 index 466738d95d..0000000000 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousFourWindingsAdder.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.dataframe.dynamic.adders; - -import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; - -/** - * @author Nicolas Pierre - */ -public class GeneratorSynchronousFourWindingsAdder extends AbstractBlackBoxAdder { - - @Override - protected AddBlackBoxToModelMapping getAddBlackBoxToModelMapping() { - return PythonDynamicModelsSupplier::addGeneratorSynchronousFourWindings; - } - -} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousFourWindingsProportionalRegulationsAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousFourWindingsProportionalRegulationsAdder.java deleted file mode 100644 index 9faec1ff6c..0000000000 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousFourWindingsProportionalRegulationsAdder.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.dataframe.dynamic.adders; - -import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; - -/** - * @author Nicolas Pierre - */ -public class GeneratorSynchronousFourWindingsProportionalRegulationsAdder extends AbstractBlackBoxAdder { - - @Override - protected AddBlackBoxToModelMapping getAddBlackBoxToModelMapping() { - return PythonDynamicModelsSupplier::addGeneratorSynchronousFourWindingsProportionalRegulations; - } - -} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousThreeWindingsAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousThreeWindingsAdder.java deleted file mode 100644 index 7cfc62f435..0000000000 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousThreeWindingsAdder.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.dataframe.dynamic.adders; - -import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; - -/** - * @author Nicolas Pierre - */ -public class GeneratorSynchronousThreeWindingsAdder extends AbstractBlackBoxAdder { - - @Override - protected AddBlackBoxToModelMapping getAddBlackBoxToModelMapping() { - return PythonDynamicModelsSupplier::addGeneratorSynchronousThreeWindings; - } - -} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousThreeWindingsProportionalRegulationsAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousThreeWindingsProportionalRegulationsAdder.java deleted file mode 100644 index 95be2a23b9..0000000000 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GeneratorSynchronousThreeWindingsProportionalRegulationsAdder.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.dataframe.dynamic.adders; - -import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; - -/** - * @author Nicolas Pierre - */ -public class GeneratorSynchronousThreeWindingsProportionalRegulationsAdder extends AbstractBlackBoxAdder { - - @Override - protected AddBlackBoxToModelMapping getAddBlackBoxToModelMapping() { - return PythonDynamicModelsSupplier::addGeneratorSynchronousThreeWindingsProportionalRegulations; - } - -} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GridFormingConverterAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GridFormingConverterAdder.java new file mode 100644 index 0000000000..481fa4ecb8 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/GridFormingConverterAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.generators.GridFormingConverterBuilder; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class GridFormingConverterAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return GridFormingConverterBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected GridFormingConverterBuilder createBuilder(Network network, ReportNode reportNode) { + return GridFormingConverterBuilder.of(network, reportNode); + } + + @Override + protected GridFormingConverterBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return GridFormingConverterBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/HvdcPAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/HvdcPAdder.java new file mode 100644 index 0000000000..262fc01a6b --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/HvdcPAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.hvdc.HvdcPBuilder; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class HvdcPAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return HvdcPBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected HvdcPBuilder createBuilder(Network network, ReportNode reportNode) { + return HvdcPBuilder.of(network, reportNode); + } + + @Override + protected HvdcPBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return HvdcPBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/HvdcVscAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/HvdcVscAdder.java new file mode 100644 index 0000000000..056d96335b --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/HvdcVscAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.hvdc.HvdcVscBuilder; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class HvdcVscAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return HvdcVscBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected HvdcVscBuilder createBuilder(Network network, ReportNode reportNode) { + return HvdcVscBuilder.of(network, reportNode); + } + + @Override + protected HvdcVscBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return HvdcVscBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/InfiniteBusAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/InfiniteBusAdder.java new file mode 100644 index 0000000000..0313b23f6e --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/InfiniteBusAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.buses.InfiniteBusBuilder; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class InfiniteBusAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return InfiniteBusBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected InfiniteBusBuilder createBuilder(Network network, ReportNode reportNode) { + return InfiniteBusBuilder.of(network, reportNode); + } + + @Override + protected InfiniteBusBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return InfiniteBusBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LineAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LineAdder.java new file mode 100644 index 0000000000..7ad4c4007a --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LineAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.lines.LineBuilder; +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class LineAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return LineBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected LineBuilder createBuilder(Network network, ReportNode reportNode) { + return LineBuilder.of(network, reportNode); + } + + @Override + protected LineBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return LineBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadOneTransformerAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadOneTransformerAdder.java new file mode 100644 index 0000000000..dcad274cd2 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadOneTransformerAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.loads.LoadOneTransformerBuilder; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class LoadOneTransformerAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return LoadOneTransformerBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected LoadOneTransformerBuilder createBuilder(Network network, ReportNode reportNode) { + return LoadOneTransformerBuilder.of(network, reportNode); + } + + @Override + protected LoadOneTransformerBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return LoadOneTransformerBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadOneTransformerTapChangerAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadOneTransformerTapChangerAdder.java new file mode 100644 index 0000000000..f269f90c61 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadOneTransformerTapChangerAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.loads.LoadOneTransformerTapChangerBuilder; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class LoadOneTransformerTapChangerAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return LoadOneTransformerTapChangerBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected LoadOneTransformerTapChangerBuilder createBuilder(Network network, ReportNode reportNode) { + return LoadOneTransformerTapChangerBuilder.of(network, reportNode); + } + + @Override + protected LoadOneTransformerTapChangerBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return LoadOneTransformerTapChangerBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadTwoTransformersAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadTwoTransformersAdder.java new file mode 100644 index 0000000000..680f3b7064 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadTwoTransformersAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.loads.LoadTwoTransformersBuilder; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class LoadTwoTransformersAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return LoadTwoTransformersBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected LoadTwoTransformersBuilder createBuilder(Network network, ReportNode reportNode) { + return LoadTwoTransformersBuilder.of(network, reportNode); + } + + @Override + protected LoadTwoTransformersBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return LoadTwoTransformersBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadTwoTransformersTapChangersAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadTwoTransformersTapChangersAdder.java new file mode 100644 index 0000000000..48ea11f8d6 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/LoadTwoTransformersTapChangersAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.loads.LoadTwoTransformersTapChangersBuilder; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class LoadTwoTransformersTapChangersAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return LoadTwoTransformersTapChangersBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected LoadTwoTransformersTapChangersBuilder createBuilder(Network network, ReportNode reportNode) { + return LoadTwoTransformersTapChangersBuilder.of(network, reportNode); + } + + @Override + protected LoadTwoTransformersTapChangersBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return LoadTwoTransformersTapChangersBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/NodeFaultAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/NodeFaultAdder.java new file mode 100644 index 0000000000..ab4208b503 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/NodeFaultAdder.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.dynamic.PersistentDoubleSeries; +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.DoubleSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.models.events.NodeFaultEventBuilder; +import com.powsybl.iidm.network.Network; + +import java.util.List; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +public class NodeFaultAdder extends AbstractEventModelAdder { + + protected static final List METADATA = List.of( + SeriesMetadata.stringIndex(STATIC_ID), + SeriesMetadata.doubles(START_TIME), + SeriesMetadata.doubles(FAULT_TIME), + SeriesMetadata.doubles(X_PU), + SeriesMetadata.doubles(R_PU)); + + @Override + public List getMetadata() { + return METADATA; + } + + private static class NodeFaultSeries extends AbstractEventModelSeries { + + private final StringSeries staticIds; + private final DoubleSeries startTimes; + private final DoubleSeries faultTimes; + private final DoubleSeries rPu; + private final DoubleSeries xPu; + + NodeFaultSeries(UpdatingDataframe dataframe) { + this.staticIds = PersistentStringSeries.copyOf(dataframe, STATIC_ID); + this.startTimes = PersistentDoubleSeries.copyOf(dataframe, START_TIME); + this.faultTimes = PersistentDoubleSeries.copyOf(dataframe, FAULT_TIME); + this.rPu = PersistentDoubleSeries.copyOf(dataframe, R_PU); + this.xPu = PersistentDoubleSeries.copyOf(dataframe, X_PU); + } + + @Override + protected void applyOnBuilder(int row, NodeFaultEventBuilder builder) { + applyIfPresent(staticIds, row, builder::staticId); + applyIfPresent(startTimes, row, builder::startTime); + applyIfPresent(faultTimes, row, builder::faultTime); + applyIfPresent(rPu, row, builder::rPu); + applyIfPresent(xPu, row, builder::xPu); + } + + @Override + protected NodeFaultEventBuilder createBuilder(Network network, ReportNode reportNode) { + return NodeFaultEventBuilder.of(network, reportNode); + } + } + + @Override + protected EventModelSeries createEventModelSeries(UpdatingDataframe dataframe) { + return new NodeFaultSeries(dataframe); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/OneTransformerLoadAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/OneTransformerLoadAdder.java deleted file mode 100644 index c15aa34180..0000000000 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/OneTransformerLoadAdder.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.dataframe.dynamic.adders; - -import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; - -/** - * @author Nicolas Pierre - */ -public class OneTransformerLoadAdder extends AbstractBlackBoxAdder { - - @Override - protected AddBlackBoxToModelMapping getAddBlackBoxToModelMapping() { - return PythonDynamicModelsSupplier::addOneTransformerLoad; - } - -} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/PhaseShifterBlockingIAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/PhaseShifterBlockingIAdder.java new file mode 100644 index 0000000000..a9f6cac702 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/PhaseShifterBlockingIAdder.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.automationsystems.phaseshifters.PhaseShifterBlockingIAutomationSystemBuilder; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +public class PhaseShifterBlockingIAdder extends AbstractSimpleDynamicModelAdder { + + protected static final List METADATA = List.of( + SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID), + SeriesMetadata.strings(PARAMETER_SET_ID), + SeriesMetadata.strings(MODEL_NAME), + SeriesMetadata.strings(PHASE_SHIFTER_ID)); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + @Override + public Collection getSupportedModels() { + return PhaseShifterBlockingIAutomationSystemBuilder.getSupportedModelInfos(); + } + + private static class PhaseShifterBlockingISeries extends AbstractAutomationSystemSeries { + + private final StringSeries phaseShifterId; + + PhaseShifterBlockingISeries(UpdatingDataframe dataframe) { + super(dataframe); + this.phaseShifterId = PersistentStringSeries.copyOf(dataframe, PHASE_SHIFTER_ID); + } + + @Override + protected void applyOnBuilder(int row, PhaseShifterBlockingIAutomationSystemBuilder builder) { + super.applyOnBuilder(row, builder); + applyIfPresent(phaseShifterId, row, builder::phaseShifterId); + } + + @Override + protected PhaseShifterBlockingIAutomationSystemBuilder createBuilder(Network network, ReportNode reportNode) { + return PhaseShifterBlockingIAutomationSystemBuilder.of(network, reportNode); + } + + @Override + protected PhaseShifterBlockingIAutomationSystemBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return PhaseShifterBlockingIAutomationSystemBuilder.of(network, modelName, reportNode); + } + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new PhaseShifterBlockingISeries(dataframe); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/PhaseShifterIAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/PhaseShifterIAdder.java new file mode 100644 index 0000000000..46d3b04b06 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/PhaseShifterIAdder.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.automationsystems.phaseshifters.PhaseShifterIAutomationSystemBuilder; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.TRANSFORMER; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +public class PhaseShifterIAdder extends AbstractSimpleDynamicModelAdder { + + protected static final List METADATA = List.of( + SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID), + SeriesMetadata.strings(PARAMETER_SET_ID), + SeriesMetadata.strings(MODEL_NAME), + SeriesMetadata.strings(TRANSFORMER)); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + @Override + public Collection getSupportedModels() { + return PhaseShifterIAutomationSystemBuilder.getSupportedModelInfos(); + } + + private static class PhaseShifterISeries extends AbstractAutomationSystemSeries { + + private final StringSeries transformers; + + PhaseShifterISeries(UpdatingDataframe dataframe) { + super(dataframe); + this.transformers = PersistentStringSeries.copyOf(dataframe, TRANSFORMER); + } + + @Override + protected void applyOnBuilder(int row, PhaseShifterIAutomationSystemBuilder builder) { + super.applyOnBuilder(row, builder); + applyIfPresent(transformers, row, builder::transformer); + } + + @Override + protected PhaseShifterIAutomationSystemBuilder createBuilder(Network network, ReportNode reportNode) { + return PhaseShifterIAutomationSystemBuilder.of(network, reportNode); + } + + @Override + protected PhaseShifterIAutomationSystemBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return PhaseShifterIAutomationSystemBuilder.of(network, modelName, reportNode); + } + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new PhaseShifterISeries(dataframe); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/PhaseShifterPAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/PhaseShifterPAdder.java new file mode 100644 index 0000000000..702420c6e8 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/PhaseShifterPAdder.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.automationsystems.phaseshifters.PhaseShifterPAutomationSystemBuilder; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +public class PhaseShifterPAdder extends AbstractSimpleDynamicModelAdder { + + protected static final List METADATA = List.of( + SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID), + SeriesMetadata.strings(PARAMETER_SET_ID), + SeriesMetadata.strings(MODEL_NAME), + SeriesMetadata.strings(TRANSFORMER)); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + @Override + public Collection getSupportedModels() { + return PhaseShifterPAutomationSystemBuilder.getSupportedModelInfos(); + } + + private static class PhaseShifterPSeries extends AbstractAutomationSystemSeries { + + private final StringSeries transformers; + + PhaseShifterPSeries(UpdatingDataframe dataframe) { + super(dataframe); + this.transformers = PersistentStringSeries.copyOf(dataframe, TRANSFORMER); + } + + @Override + protected void applyOnBuilder(int row, PhaseShifterPAutomationSystemBuilder builder) { + super.applyOnBuilder(row, builder); + applyIfPresent(transformers, row, builder::transformer); + } + + @Override + protected PhaseShifterPAutomationSystemBuilder createBuilder(Network network, ReportNode reportNode) { + return PhaseShifterPAutomationSystemBuilder.of(network, reportNode); + } + + @Override + protected PhaseShifterPAutomationSystemBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return PhaseShifterPAutomationSystemBuilder.of(network, modelName, reportNode); + } + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new PhaseShifterPSeries(dataframe); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/SignalNGeneratorAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/SignalNGeneratorAdder.java new file mode 100644 index 0000000000..07aaab886a --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/SignalNGeneratorAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.generators.SignalNGeneratorBuilder; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class SignalNGeneratorAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return SignalNGeneratorBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected SignalNGeneratorBuilder createBuilder(Network network, ReportNode reportNode) { + return SignalNGeneratorBuilder.of(network, reportNode); + } + + @Override + protected SignalNGeneratorBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return SignalNGeneratorBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/StaticVarCompensatorAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/StaticVarCompensatorAdder.java new file mode 100644 index 0000000000..2353f4a822 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/StaticVarCompensatorAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.svarcs.BaseStaticVarCompensatorBuilder; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.StaticVarCompensator; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class StaticVarCompensatorAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return BaseStaticVarCompensatorBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected BaseStaticVarCompensatorBuilder createBuilder(Network network, ReportNode reportNode) { + return BaseStaticVarCompensatorBuilder.of(network, reportNode); + } + + @Override + protected BaseStaticVarCompensatorBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return BaseStaticVarCompensatorBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/SynchronizedGeneratorAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/SynchronizedGeneratorAdder.java new file mode 100644 index 0000000000..d4c88f33bf --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/SynchronizedGeneratorAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.generators.SynchronizedGeneratorBuilder; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class SynchronizedGeneratorAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return SynchronizedGeneratorBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected SynchronizedGeneratorBuilder createBuilder(Network network, ReportNode reportNode) { + return SynchronizedGeneratorBuilder.of(network, reportNode); + } + + @Override + protected SynchronizedGeneratorBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return SynchronizedGeneratorBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/SynchronousGeneratorAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/SynchronousGeneratorAdder.java new file mode 100644 index 0000000000..df817d9900 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/SynchronousGeneratorAdder.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.generators.SynchronousGeneratorBuilder; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Nicolas Pierre + * @author Laurent Issertial {@literal } + */ +public class SynchronousGeneratorAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return SynchronousGeneratorBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected SynchronousGeneratorBuilder createBuilder(Network network, ReportNode reportNode) { + return SynchronousGeneratorBuilder.of(network, reportNode); + } + + @Override + protected SynchronousGeneratorBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return SynchronousGeneratorBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/TapChangerAutomationSystemAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/TapChangerAutomationSystemAdder.java new file mode 100644 index 0000000000..b13a4401c5 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/TapChangerAutomationSystemAdder.java @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.TransformerSide; +import com.powsybl.dynawo.models.automationsystems.TapChangerAutomationSystemBuilder; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +public class TapChangerAutomationSystemAdder extends AbstractSimpleDynamicModelAdder { + + protected static final List METADATA = List.of( + SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID), + SeriesMetadata.strings(PARAMETER_SET_ID), + SeriesMetadata.strings(MODEL_NAME), + SeriesMetadata.strings(STATIC_ID), + SeriesMetadata.strings(SIDE)); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + @Override + public Collection getSupportedModels() { + return TapChangerAutomationSystemBuilder.getSupportedModelInfos(); + } + + private static class TapChangerSeries extends AbstractAutomationSystemSeries { + + private final StringSeries staticIds; + private final StringSeries sides; + + TapChangerSeries(UpdatingDataframe dataframe) { + super(dataframe); + this.staticIds = PersistentStringSeries.copyOf(dataframe, STATIC_ID); + this.sides = PersistentStringSeries.copyOf(dataframe, SIDE); + } + + @Override + protected void applyOnBuilder(int row, TapChangerAutomationSystemBuilder builder) { + super.applyOnBuilder(row, builder); + applyIfPresent(staticIds, row, builder::staticId); + applyIfPresent(sides, row, TransformerSide.class, builder::side); + } + + @Override + protected TapChangerAutomationSystemBuilder createBuilder(Network network, ReportNode reportNode) { + return TapChangerAutomationSystemBuilder.of(network, reportNode); + } + + @Override + protected TapChangerAutomationSystemBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return TapChangerAutomationSystemBuilder.of(network, modelName, reportNode); + } + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new TapChangerSeries(dataframe); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/TapChangerBlockingAutomationSystemAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/TapChangerBlockingAutomationSystemAdder.java new file mode 100644 index 0000000000..062fad9c2b --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/TapChangerBlockingAutomationSystemAdder.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.automationsystems.TapChangerBlockingAutomationSystemBuilder; +import com.powsybl.iidm.network.Network; +import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; + +import java.util.*; + +import static com.powsybl.dataframe.dynamic.DynamicModelSeriesUtils.applyIfPresent; +import static com.powsybl.dataframe.dynamic.DynamicModelSeriesUtils.createIdMap; +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; + +/** + * @author Laurent Issertial {@literal } + */ +public class TapChangerBlockingAutomationSystemAdder implements DynamicMappingAdder { + + private static final List METADATA = List.of( + SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID), + SeriesMetadata.strings(PARAMETER_SET_ID), + SeriesMetadata.strings(MODEL_NAME)); + + private static final List TRANSFORMER_METADATA = List.of( + SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID), + SeriesMetadata.strings(TRANSFORMER_ID)); + + private static final List U_MEASUREMENT_METADATA = List.of( + SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID), + SeriesMetadata.strings(MEASUREMENT_POINT_ID)); + + private static final List> METADATA_LIST = List.of(METADATA, + TRANSFORMER_METADATA, + U_MEASUREMENT_METADATA, + U_MEASUREMENT_METADATA, + U_MEASUREMENT_METADATA, + U_MEASUREMENT_METADATA, + U_MEASUREMENT_METADATA); + + private static final int MAX_MEASUREMENTS = 5; + + @Override + public List> getMetadata() { + return METADATA_LIST; + } + + @Override + public Collection getSupportedModels() { + return TapChangerBlockingAutomationSystemBuilder.getSupportedModelInfos(); + } + + @Override + public void addElements(PythonDynamicModelsSupplier modelMapping, List dataframes) { + if (dataframes.size() < 3) { + throw new IllegalArgumentException("Expected at least 3 dataframes: one for TCB, one for transformers and one for measurement points."); + } + UpdatingDataframe dataframe = dataframes.get(0); + UpdatingDataframe tfoDataframe = dataframes.get(1); + List measurementDataframe = new ArrayList<>(MAX_MEASUREMENTS); + for (int i = 2; i < dataframes.size(); i++) { + measurementDataframe.add(dataframes.get(i)); + } + + DynamicModelSeries series = new TapChangerBlockingSeries(dataframe, tfoDataframe, measurementDataframe); + for (int row = 0; row < dataframe.getRowCount(); row++) { + modelMapping.addModel(series.getModelSupplier(row)); + } + } + + private static class TapChangerBlockingSeries extends AbstractAutomationSystemSeries { + + private final Map> transformers; + private final List>> uMeasurements = new ArrayList<>(MAX_MEASUREMENTS); + + TapChangerBlockingSeries(UpdatingDataframe tcbDataframe, UpdatingDataframe tfoDataframe, List measurementDataframe) { + super(tcbDataframe); + this.transformers = createIdMap(tfoDataframe, DYNAMIC_MODEL_ID, TRANSFORMER_ID); + measurementDataframe.forEach(mdf -> uMeasurements.add(createIdMap(mdf, DYNAMIC_MODEL_ID, MEASUREMENT_POINT_ID))); + } + + @Override + protected void applyOnBuilder(int row, TapChangerBlockingAutomationSystemBuilder builder) { + super.applyOnBuilder(row, builder); + String tcbId = dynamicModelIds.get(row); + applyIfPresent(transformers, tcbId, builder::transformers); + List> id2dList = new ArrayList<>(); + uMeasurements.forEach(idMap -> applyIfPresent(idMap, tcbId, id2dList::add)); + if (!id2dList.isEmpty()) { + builder.uMeasurements(id2dList.toArray(new Collection[0])); + } + } + + @Override + protected TapChangerBlockingAutomationSystemBuilder createBuilder(Network network, ReportNode reportNode) { + return TapChangerBlockingAutomationSystemBuilder.of(network, reportNode); + } + + @Override + protected TapChangerBlockingAutomationSystemBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return TapChangerBlockingAutomationSystemBuilder.of(network, modelName, reportNode); + } + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/TransformerAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/TransformerAdder.java new file mode 100644 index 0000000000..9e83040e86 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/TransformerAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.transformers.TransformerFixedRatioBuilder; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoWindingsTransformer; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class TransformerAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return TransformerFixedRatioBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected TransformerFixedRatioBuilder createBuilder(Network network, ReportNode reportNode) { + return TransformerFixedRatioBuilder.of(network, reportNode); + } + + @Override + protected TransformerFixedRatioBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return TransformerFixedRatioBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/UnderVoltageAutomationSystemAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/UnderVoltageAutomationSystemAdder.java new file mode 100644 index 0000000000..3e61e4aaee --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/UnderVoltageAutomationSystemAdder.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.dynamic.PersistentStringSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.automationsystems.UnderVoltageAutomationSystemBuilder; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Laurent Issertial {@literal } + */ +public class UnderVoltageAutomationSystemAdder extends AbstractSimpleDynamicModelAdder { + + protected static final List METADATA = List.of( + SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID), + SeriesMetadata.strings(PARAMETER_SET_ID), + SeriesMetadata.strings(MODEL_NAME), + SeriesMetadata.strings(GENERATOR)); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + @Override + public Collection getSupportedModels() { + return UnderVoltageAutomationSystemBuilder.getSupportedModelInfos(); + } + + private static class UnderVoltageSeries extends AbstractAutomationSystemSeries { + + private final StringSeries generators; + + UnderVoltageSeries(UpdatingDataframe dataframe) { + super(dataframe); + this.generators = PersistentStringSeries.copyOf(dataframe, GENERATOR); + } + + @Override + protected void applyOnBuilder(int row, UnderVoltageAutomationSystemBuilder builder) { + super.applyOnBuilder(row, builder); + applyIfPresent(generators, row, builder::generator); + } + + @Override + protected UnderVoltageAutomationSystemBuilder createBuilder(Network network, ReportNode reportNode) { + return UnderVoltageAutomationSystemBuilder.of(network, reportNode); + } + + @Override + protected UnderVoltageAutomationSystemBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return UnderVoltageAutomationSystemBuilder.of(network, modelName, reportNode); + } + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new UnderVoltageSeries(dataframe); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/adders/WeccAdder.java b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/WeccAdder.java new file mode 100644 index 0000000000..6a98c8fe1a --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/adders/WeccAdder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynawo.builders.ModelInfo; +import com.powsybl.dynawo.models.generators.WeccBuilder; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; + +import java.util.Collection; + +/** + * @author Laurent Issertial {@literal } + */ +public class WeccAdder extends AbstractEquipmentAdder { + + @Override + public Collection getSupportedModels() { + return WeccBuilder.getSupportedModelInfos(); + } + + @Override + protected DynamicModelSeries createDynamicModelSeries(UpdatingDataframe dataframe) { + return new AbstractEquipmentSeries(dataframe) { + + @Override + protected WeccBuilder createBuilder(Network network, ReportNode reportNode) { + return WeccBuilder.of(network, reportNode); + } + + @Override + protected WeccBuilder createBuilder(Network network, String modelName, ReportNode reportNode) { + return WeccBuilder.of(network, modelName, reportNode); + } + }; + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/impl/DefaultDataframeHandler.java b/java/src/main/java/com/powsybl/dataframe/impl/DefaultDataframeHandler.java index 96c8225d78..2605fb317d 100644 --- a/java/src/main/java/com/powsybl/dataframe/impl/DefaultDataframeHandler.java +++ b/java/src/main/java/com/powsybl/dataframe/impl/DefaultDataframeHandler.java @@ -15,7 +15,7 @@ /** * Writes series data to POJOs. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class DefaultDataframeHandler implements DataframeHandler { diff --git a/java/src/main/java/com/powsybl/dataframe/impl/package-info.java b/java/src/main/java/com/powsybl/dataframe/impl/package-info.java index 0a28d115a4..1c0e55b3ce 100644 --- a/java/src/main/java/com/powsybl/dataframe/impl/package-info.java +++ b/java/src/main/java/com/powsybl/dataframe/impl/package-info.java @@ -8,6 +8,6 @@ /** * Implementation of dataframe input/output as POJOs. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ package com.powsybl.dataframe.impl; diff --git a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/BranchValidationData.java b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/BranchValidationData.java index 6ef23d594d..efacd798f6 100644 --- a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/BranchValidationData.java +++ b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/BranchValidationData.java @@ -1,7 +1,7 @@ package com.powsybl.dataframe.loadflow.validation; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ class BranchValidationData { String branchId; diff --git a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/BusValidationData.java b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/BusValidationData.java index 0b79dd92d4..70543e3c95 100644 --- a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/BusValidationData.java +++ b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/BusValidationData.java @@ -1,7 +1,7 @@ package com.powsybl.dataframe.loadflow.validation; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ public class BusValidationData { String id; diff --git a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/GeneratorValidationData.java b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/GeneratorValidationData.java index 3f6ba1655d..1f91296828 100644 --- a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/GeneratorValidationData.java +++ b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/GeneratorValidationData.java @@ -1,7 +1,7 @@ package com.powsybl.dataframe.loadflow.validation; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ public class GeneratorValidationData { String id; diff --git a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/InMemoryValidationWriter.java b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/InMemoryValidationWriter.java index 43b2f5805e..1f43aaddfc 100644 --- a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/InMemoryValidationWriter.java +++ b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/InMemoryValidationWriter.java @@ -17,7 +17,7 @@ import java.util.List; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ public class InMemoryValidationWriter implements ValidationWriter { diff --git a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/ShuntValidationData.java b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/ShuntValidationData.java index 2de1e33841..3a019f08dc 100644 --- a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/ShuntValidationData.java +++ b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/ShuntValidationData.java @@ -1,7 +1,7 @@ package com.powsybl.dataframe.loadflow.validation; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ class ShuntValidationData { String shuntId; diff --git a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/SvcValidationData.java b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/SvcValidationData.java index 9aed41520d..a5be11ee9b 100644 --- a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/SvcValidationData.java +++ b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/SvcValidationData.java @@ -3,7 +3,7 @@ import com.powsybl.iidm.network.StaticVarCompensator; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ class SvcValidationData { String svcId; diff --git a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/T3wtValidationData.java b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/T3wtValidationData.java index ce30b96f2b..b9699db097 100644 --- a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/T3wtValidationData.java +++ b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/T3wtValidationData.java @@ -3,7 +3,7 @@ import com.powsybl.iidm.network.util.TwtData; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ class T3wtValidationData { String id; diff --git a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/TwtValidationData.java b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/TwtValidationData.java index b5e2b78208..17db6920a0 100644 --- a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/TwtValidationData.java +++ b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/TwtValidationData.java @@ -5,7 +5,7 @@ import java.util.Optional; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ class TwtValidationData { String id; diff --git a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/Validations.java b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/Validations.java index 5391fc3f05..f60a531254 100644 --- a/java/src/main/java/com/powsybl/dataframe/loadflow/validation/Validations.java +++ b/java/src/main/java/com/powsybl/dataframe/loadflow/validation/Validations.java @@ -15,7 +15,7 @@ import java.util.function.Function; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ public final class Validations { diff --git a/java/src/main/java/com/powsybl/dataframe/network/AbstractNetworkDataframeMapper.java b/java/src/main/java/com/powsybl/dataframe/network/AbstractNetworkDataframeMapper.java index bb31d23852..b2d8c45464 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/AbstractNetworkDataframeMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/network/AbstractNetworkDataframeMapper.java @@ -14,12 +14,13 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public abstract class AbstractNetworkDataframeMapper extends AbstractDataframeMapper implements NetworkDataframeMapper { @@ -42,10 +43,11 @@ public void createDataframe(Network network, DataframeHandler dataframeHandler, } protected List getFilteredItems(Network network, DataframeFilter dataframeFilter, NetworkDataframeContext context) { - if (dataframeFilter.getSelectingDataframe().isEmpty()) { + Optional optionalUpdatingDataframe = dataframeFilter.getSelectingDataframe(); + if (optionalUpdatingDataframe.isEmpty()) { return getItems(network, context); } else { - UpdatingDataframe selectedDataframe = dataframeFilter.getSelectingDataframe().get(); + UpdatingDataframe selectedDataframe = optionalUpdatingDataframe.get(); return IntStream.range(0, selectedDataframe.getRowCount()).mapToObj(i -> getItem(network, selectedDataframe, i, context)).collect(Collectors.toList()); } } diff --git a/java/src/main/java/com/powsybl/dataframe/network/ExtensionInformation.java b/java/src/main/java/com/powsybl/dataframe/network/ExtensionInformation.java index 7b2dc98ca1..882db3431a 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/ExtensionInformation.java +++ b/java/src/main/java/com/powsybl/dataframe/network/ExtensionInformation.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe.network; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class ExtensionInformation { private final String id; diff --git a/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframeMapper.java b/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframeMapper.java index c4516b1835..03e2c8ac64 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframeMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframeMapper.java @@ -17,7 +17,7 @@ * The dataframe data can be read by a {@link DataframeHandler}, * and provided by variants of "indexed series". * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public interface NetworkDataframeMapper extends DataframeMapper { diff --git a/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframeMapperBuilder.java b/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframeMapperBuilder.java index 346233c945..25a86218ab 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframeMapperBuilder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframeMapperBuilder.java @@ -20,7 +20,7 @@ * Specific build for network mappers : * it provides network-specific features, in particular the {@link #addProperties()} method. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class NetworkDataframeMapperBuilder extends BaseDataframeMapperBuilder> { diff --git a/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java b/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java index 854f103e18..a49ba4d650 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java +++ b/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java @@ -15,6 +15,7 @@ import com.powsybl.dataframe.network.extensions.ExtensionDataframeKey; import com.powsybl.dataframe.update.UpdatingDataframe; import com.powsybl.iidm.network.*; +import com.powsybl.python.commons.PyPowsyblApiHeader; import com.powsybl.python.network.NetworkUtil; import com.powsybl.python.network.SideEnum; import com.powsybl.python.network.TemporaryLimitData; @@ -33,8 +34,8 @@ * defines the mappings for all elements of the network. * * @author Geoffroy Jamgotchian {@literal } - * @author Yichen TANG - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Sylvain Leclerc {@literal } */ public final class NetworkDataframes { @@ -42,6 +43,8 @@ public final class NetworkDataframes { private static final Map EXTENSIONS_MAPPERS = NetworkExtensions.createExtensionsMappers(); + private static final String DEFAULT_OPERATIONAL_LIMIT_GROUP_ID = "DEFAULT"; + private NetworkDataframes() { } @@ -52,6 +55,9 @@ public static NetworkDataframeMapper getDataframeMapper(DataframeElementType typ private static Map createMappers() { Map mappers = new EnumMap<>(DataframeElementType.class); mappers.put(DataframeElementType.SUB_NETWORK, subNetworks()); + mappers.put(DataframeElementType.AREA, areas()); + mappers.put(DataframeElementType.AREA_VOLTAGE_LEVELS, areaVoltageLevels()); + mappers.put(DataframeElementType.AREA_BOUNDARIES, areaBoundaries()); mappers.put(DataframeElementType.BUS, buses(false)); mappers.put(DataframeElementType.BUS_FROM_BUS_BREAKER_VIEW, buses(true)); mappers.put(DataframeElementType.LINE, lines()); @@ -60,10 +66,12 @@ private static Map createMappers() mappers.put(DataframeElementType.GENERATOR, generators()); mappers.put(DataframeElementType.LOAD, loads()); mappers.put(DataframeElementType.BATTERY, batteries()); + mappers.put(DataframeElementType.GROUND, grounds()); mappers.put(DataframeElementType.SHUNT_COMPENSATOR, shunts()); mappers.put(DataframeElementType.NON_LINEAR_SHUNT_COMPENSATOR_SECTION, shuntsNonLinear()); mappers.put(DataframeElementType.LINEAR_SHUNT_COMPENSATOR_SECTION, linearShuntsSections()); mappers.put(DataframeElementType.DANGLING_LINE, danglingLines()); + mappers.put(DataframeElementType.DANGLING_LINE_GENERATION, danglingLinesGeneration()); mappers.put(DataframeElementType.TIE_LINE, tieLines()); mappers.put(DataframeElementType.LCC_CONVERTER_STATION, lccs()); mappers.put(DataframeElementType.VSC_CONVERTER_STATION, vscs()); @@ -71,14 +79,15 @@ private static Map createMappers() mappers.put(DataframeElementType.SWITCH, switches()); mappers.put(DataframeElementType.VOLTAGE_LEVEL, voltageLevels()); mappers.put(DataframeElementType.SUBSTATION, substations()); - mappers.put(DataframeElementType.BUSBAR_SECTION, busBars()); + mappers.put(DataframeElementType.BUSBAR_SECTION, busbarSections()); mappers.put(DataframeElementType.HVDC_LINE, hvdcs()); mappers.put(DataframeElementType.RATIO_TAP_CHANGER_STEP, rtcSteps()); mappers.put(DataframeElementType.PHASE_TAP_CHANGER_STEP, ptcSteps()); mappers.put(DataframeElementType.RATIO_TAP_CHANGER, rtcs()); mappers.put(DataframeElementType.PHASE_TAP_CHANGER, ptcs()); mappers.put(DataframeElementType.REACTIVE_CAPABILITY_CURVE_POINT, reactiveCapabilityCurves()); - mappers.put(DataframeElementType.OPERATIONAL_LIMITS, operationalLimits()); + mappers.put(DataframeElementType.OPERATIONAL_LIMITS, operationalLimits(false)); + mappers.put(DataframeElementType.SELECTED_OPERATIONAL_LIMITS, operationalLimits(true)); mappers.put(DataframeElementType.ALIAS, aliases()); mappers.put(DataframeElementType.IDENTIFIABLE, identifiables()); mappers.put(DataframeElementType.INJECTION, injections()); @@ -260,7 +269,7 @@ public static ToDoubleBiFunction ifExistsDoub static NetworkDataframeMapper generators() { return NetworkDataframeMapperBuilder.ofStream(Network::getGeneratorStream, getOrThrow(Network::getGenerator, "Generator")) .stringsIndex("id", Generator::getId) - .strings("name", g -> g.getOptionalName().orElse("")) + .strings("name", g -> g.getOptionalName().orElse(""), Identifiable::setName) .enums("energy_source", EnergySource.class, Generator::getEnergySource, Generator::setEnergySource) .doubles("target_p", (g, context) -> perUnitPQ(context, g.getTargetP()), (g, targetP, context) -> g.setTargetP(unPerUnitPQ(context, targetP))) .doubles("min_p", (g, context) -> perUnitPQ(context, g.getMinP()), (g, minP, context) -> g.setMinP(unPerUnitPQ(context, minP))) @@ -301,18 +310,36 @@ private static NetworkDataframeMapper subNetworks() { .build(); } + private static NetworkDataframeMapper areas() { + return NetworkDataframeMapperBuilder.ofStream(Network::getAreaStream, + getOrThrow(Network::getArea, "Area")) + .stringsIndex("id", Identifiable::getId) + .strings("name", a -> a.getOptionalName().orElse(""), Identifiable::setName) + .strings("area_type", Area::getAreaType) + .doubles("interchange_target", (a, context) -> perUnitPQ(context, a.getInterchangeTarget().orElse(Double.NaN)), (a, p, context) -> a.setInterchangeTarget(unPerUnitPQ(context, p))) + .doubles("interchange", (a, context) -> perUnitPQ(context, a.getInterchange())) + .doubles("ac_interchange", (a, context) -> perUnitPQ(context, a.getAcInterchange())) + .doubles("dc_interchange", (a, context) -> perUnitPQ(context, a.getDcInterchange())) + .booleans("fictitious", Identifiable::isFictitious, Identifiable::setFictitious, false) + .addProperties() + .build(); + } + static NetworkDataframeMapper buses(boolean busBreakerView) { - return NetworkDataframeMapperBuilder.ofStream(n -> busBreakerView ? n.getBusBreakerView().getBusStream() : n.getBusView().getBusStream(), + var builder = NetworkDataframeMapperBuilder.ofStream(n -> busBreakerView ? n.getBusBreakerView().getBusStream() : n.getBusView().getBusStream(), getOrThrow((b, id) -> b.getBusView().getBus(id), "Bus")) .stringsIndex("id", Bus::getId) - .strings("name", b -> b.getOptionalName().orElse("")) + .strings("name", b -> b.getOptionalName().orElse(""), Identifiable::setName) .doubles("v_mag", (b, context) -> perUnitV(context, b.getV(), b), (b, v, context) -> b.setV(unPerUnitV(context, v, b))) .doubles("v_angle", (b, context) -> perUnitAngle(context, b.getAngle()), (b, vAngle, context) -> b.setAngle(unPerUnitAngle(context, vAngle))) .ints("connected_component", ifExistsInt(Bus::getConnectedComponent, Component::getNum)) .ints("synchronous_component", ifExistsInt(Bus::getSynchronousComponent, Component::getNum)) - .strings("voltage_level_id", b -> b.getVoltageLevel().getId()) - .booleans("fictitious", Identifiable::isFictitious, Identifiable::setFictitious, false) + .strings("voltage_level_id", b -> b.getVoltageLevel().getId()); + if (busBreakerView) { + builder.strings("bus_id", b -> NetworkUtil.getBusViewBus(b).map(Bus::getId).orElse("")); + } + return builder.booleans("fictitious", Identifiable::isFictitious, Identifiable::setFictitious, false) .addProperties() .build(); } @@ -320,7 +347,7 @@ static NetworkDataframeMapper buses(boolean busBreakerView) { static NetworkDataframeMapper loads() { return NetworkDataframeMapperBuilder.ofStream(Network::getLoadStream, getOrThrow(Network::getLoad, "Load")) .stringsIndex("id", Load::getId) - .strings("name", l -> l.getOptionalName().orElse("")) + .strings("name", l -> l.getOptionalName().orElse(""), Identifiable::setName) .enums("type", LoadType.class, Load::getLoadType) .doubles("p0", (l, context) -> perUnitPQ(context, l.getP0()), (l, p, context) -> l.setP0(unPerUnitPQ(context, p))) .doubles("q0", (l, context) -> perUnitPQ(context, l.getQ0()), (l, q, context) -> l.setQ0(unPerUnitPQ(context, q))) @@ -340,7 +367,7 @@ static NetworkDataframeMapper loads() { static NetworkDataframeMapper batteries() { return NetworkDataframeMapperBuilder.ofStream(Network::getBatteryStream, getOrThrow(Network::getBattery, "Battery")) .stringsIndex("id", Battery::getId) - .strings("name", b -> b.getOptionalName().orElse("")) + .strings("name", b -> b.getOptionalName().orElse(""), Identifiable::setName) .doubles("max_p", (b, context) -> perUnitPQ(context, b.getMaxP()), (b, maxP, context) -> b.setMaxP(unPerUnitPQ(context, maxP))) .doubles("min_p", (b, context) -> perUnitPQ(context, b.getMinP()), (b, minP, context) -> b.setMinP(unPerUnitPQ(context, minP))) .doubles("min_q", ifExistsDoublePerUnitPQ(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMinQ), @@ -363,10 +390,23 @@ static NetworkDataframeMapper batteries() { .build(); } + static NetworkDataframeMapper grounds() { + return NetworkDataframeMapperBuilder.ofStream(Network::getGroundStream, getOrThrow(Network::getGround, "Ground")) + .stringsIndex("id", Ground::getId) + .strings("name", b -> b.getOptionalName().orElse(""), Identifiable::setName) + .strings("voltage_level_id", getVoltageLevelId()) + .strings("bus_id", b -> getBusId(b.getTerminal())) + .strings("bus_breaker_bus_id", getBusBreakerViewBusId(), NetworkDataframes::setBusBreakerViewBusId, false) + .ints("node", b -> getNode(b.getTerminal()), false) + .booleans("connected", b -> b.getTerminal().isConnected(), connectInjection()) + .addProperties() + .build(); + } + static NetworkDataframeMapper shunts() { return NetworkDataframeMapperBuilder.ofStream(Network::getShuntCompensatorStream, getOrThrow(Network::getShuntCompensator, "Shunt compensator")) .stringsIndex("id", ShuntCompensator::getId) - .strings("name", sc -> sc.getOptionalName().orElse("")) + .strings("name", sc -> sc.getOptionalName().orElse(""), Identifiable::setName) .doubles("g", (shunt, context) -> perUnitG(context, shunt)) .doubles("b", (shunt, context) -> perUnitB(context, shunt)) .enums("model_type", ShuntCompensatorModelType.class, ShuntCompensator::getModelType) @@ -455,7 +495,7 @@ private static ShuntCompensatorLinearModel checkLinearModel(Network network, Str static NetworkDataframeMapper lines() { return NetworkDataframeMapperBuilder.ofStream(Network::getLineStream, getOrThrow(Network::getLine, "Line")) .stringsIndex("id", Line::getId) - .strings("name", l -> l.getOptionalName().orElse("")) + .strings("name", l -> l.getOptionalName().orElse(""), Identifiable::setName) .doubles("r", (line, context) -> perUnitR(context, line), (line, r, context) -> line.setR(unPerUnitRX(context, line, r))) .doubles("x", (line, context) -> PerUnitUtil.perUnitX(context, line), @@ -485,6 +525,10 @@ static NetworkDataframeMapper lines() { .booleans("connected1", l -> l.getTerminal1().isConnected(), connectBranchSide1()) .booleans("connected2", l -> l.getTerminal2().isConnected(), connectBranchSide2()) .booleans("fictitious", Identifiable::isFictitious, Identifiable::setFictitious, false) + .strings("selected_limits_group_1", line -> line.getSelectedOperationalLimitsGroupId1().orElse(DEFAULT_OPERATIONAL_LIMIT_GROUP_ID), + Line::setSelectedOperationalLimitsGroup1, false) + .strings("selected_limits_group_2", line -> line.getSelectedOperationalLimitsGroupId2().orElse(DEFAULT_OPERATIONAL_LIMIT_GROUP_ID), + Line::setSelectedOperationalLimitsGroup2, false) .addProperties() .build(); } @@ -492,7 +536,7 @@ static NetworkDataframeMapper lines() { static NetworkDataframeMapper twoWindingTransformers() { return NetworkDataframeMapperBuilder.ofStream(Network::getTwoWindingsTransformerStream, getOrThrow(Network::getTwoWindingsTransformer, "Two windings transformer")) .stringsIndex("id", TwoWindingsTransformer::getId) - .strings("name", twt -> twt.getOptionalName().orElse("")) + .strings("name", twt -> twt.getOptionalName().orElse(""), Identifiable::setName) .doubles("r", (twt, context) -> perUnitRX(context, twt.getR(), twt), (twt, r, context) -> twt.setR(unPerUnitRX(context, twt, r))) .doubles("x", (twt, context) -> perUnitRX(context, twt.getX(), twt), (twt, x, context) -> twt.setX(unPerUnitRX(context, twt, x))) .doubles("g", (twt, context) -> perUnitBG(context, twt, twt.getG()), (twt, g, context) -> twt.setG(unPerUnitBG(context, twt, g))) @@ -519,6 +563,10 @@ static NetworkDataframeMapper twoWindingTransformers() { .booleans("connected1", twt -> twt.getTerminal1().isConnected(), connectBranchSide1()) .booleans("connected2", twt -> twt.getTerminal2().isConnected(), connectBranchSide2()) .booleans("fictitious", Identifiable::isFictitious, Identifiable::setFictitious, false) + .strings("selected_limits_group_1", twt -> twt.getSelectedOperationalLimitsGroupId1().orElse(DEFAULT_OPERATIONAL_LIMIT_GROUP_ID), + TwoWindingsTransformer::setSelectedOperationalLimitsGroup1, false) + .strings("selected_limits_group_2", twt -> twt.getSelectedOperationalLimitsGroupId2().orElse(DEFAULT_OPERATIONAL_LIMIT_GROUP_ID), + TwoWindingsTransformer::setSelectedOperationalLimitsGroup2, false) .addProperties() .build(); } @@ -526,7 +574,7 @@ static NetworkDataframeMapper twoWindingTransformers() { static NetworkDataframeMapper threeWindingTransformers() { return NetworkDataframeMapperBuilder.ofStream(Network::getThreeWindingsTransformerStream, getOrThrow(Network::getThreeWindingsTransformer, "Three windings transformer")) .stringsIndex("id", ThreeWindingsTransformer::getId) - .strings("name", twt -> twt.getOptionalName().orElse("")) + .strings("name", twt -> twt.getOptionalName().orElse(""), Identifiable::setName) .doubles("rated_u0", (twt, context) -> context.isPerUnit() ? 1 : twt.getRatedU0()) .doubles("r1", (twt, context) -> perUnitRX(context, twt.getLeg1().getR(), twt), (twt, r1, context) -> twt.getLeg1().setR(unPerUnitRX(context, twt, r1))) .doubles("x1", (twt, context) -> perUnitRX(context, twt.getLeg1().getX(), twt), (twt, x1, context) -> twt.getLeg1().setX(unPerUnitRX(context, twt, x1))) @@ -544,6 +592,8 @@ static NetworkDataframeMapper threeWindingTransformers() { .strings("bus_breaker_bus1_id", twt -> getBusBreakerViewBusId(twt.getLeg1().getTerminal()), (twt, id) -> setBusBreakerViewBusId(twt.getLeg1().getTerminal(), id), false) .ints("node1", twt -> getNode(twt.getLeg1().getTerminal()), false) .booleans("connected1", g -> g.getLeg1().getTerminal().isConnected(), connectLeg1()) + .strings("selected_limits_group_1", twt -> twt.getLeg1().getSelectedOperationalLimitsGroupId().orElse(DEFAULT_OPERATIONAL_LIMIT_GROUP_ID), + (twt, groupId) -> twt.getLeg1().setSelectedOperationalLimitsGroup(groupId), false) .doubles("r2", (twt, context) -> perUnitRX(context, twt.getLeg2().getR(), twt), (twt, r2, context) -> twt.getLeg2().setR(unPerUnitRX(context, twt, r2))) .doubles("x2", (twt, context) -> perUnitRX(context, twt.getLeg2().getX(), twt), (twt, x2, context) -> twt.getLeg2().setX(unPerUnitRX(context, twt, x2))) .doubles("g2", (twt, context) -> perUnitBG(context, twt.getLeg2().getG(), twt), (twt, g2, context) -> twt.getLeg2().setG(unPerUnitBG(context, twt, g2))) @@ -560,6 +610,8 @@ static NetworkDataframeMapper threeWindingTransformers() { .strings("bus_breaker_bus2_id", twt -> getBusBreakerViewBusId(twt.getLeg2().getTerminal()), (twt, id) -> setBusBreakerViewBusId(twt.getLeg2().getTerminal(), id), false) .ints("node2", twt -> getNode(twt.getLeg2().getTerminal()), false) .booleans("connected2", g -> g.getLeg2().getTerminal().isConnected(), connectLeg2()) + .strings("selected_limits_group_2", twt -> twt.getLeg2().getSelectedOperationalLimitsGroupId().orElse(DEFAULT_OPERATIONAL_LIMIT_GROUP_ID), + (twt, groupId) -> twt.getLeg2().setSelectedOperationalLimitsGroup(groupId), false) .doubles("r3", (twt, context) -> perUnitRX(context, twt.getLeg3().getR(), twt), (twt, r3, context) -> twt.getLeg3().setR(unPerUnitRX(context, twt, r3))) .doubles("x3", (twt, context) -> perUnitRX(context, twt.getLeg3().getX(), twt), (twt, x3, context) -> twt.getLeg3().setX(unPerUnitRX(context, twt, x3))) .doubles("g3", (twt, context) -> perUnitBG(context, twt.getLeg3().getG(), twt), (twt, g3, context) -> twt.getLeg3().setG(unPerUnitBG(context, twt, g3))) @@ -576,6 +628,8 @@ static NetworkDataframeMapper threeWindingTransformers() { .strings("bus_breaker_bus3_id", twt -> getBusBreakerViewBusId(twt.getLeg3().getTerminal()), (twt, id) -> setBusBreakerViewBusId(twt.getLeg3().getTerminal(), id), false) .ints("node3", twt -> getNode(twt.getLeg3().getTerminal()), false) .booleans("connected3", twt -> twt.getLeg3().getTerminal().isConnected(), connectLeg3()) + .strings("selected_limits_group_3", twt -> twt.getLeg3().getSelectedOperationalLimitsGroupId().orElse(DEFAULT_OPERATIONAL_LIMIT_GROUP_ID), + (twt, groupId) -> twt.getLeg3().setSelectedOperationalLimitsGroup(groupId), false) .booleans("fictitious", Identifiable::isFictitious, Identifiable::setFictitious, false) .addProperties() .build(); @@ -584,7 +638,7 @@ static NetworkDataframeMapper threeWindingTransformers() { static NetworkDataframeMapper danglingLines() { return NetworkDataframeMapperBuilder.ofStream(network -> network.getDanglingLineStream(), getOrThrow(Network::getDanglingLine, "Dangling line")) .stringsIndex("id", DanglingLine::getId) - .strings("name", dl -> dl.getOptionalName().orElse("")) + .strings("name", dl -> dl.getOptionalName().orElse(""), Identifiable::setName) .doubles("r", (dl, context) -> perUnitRX(context, dl.getR(), dl.getTerminal()), (dl, r, context) -> dl.setR(unPerUnitRX(context, dl.getTerminal(), r))) .doubles("x", (dl, context) -> perUnitRX(context, dl.getX(), dl.getTerminal()), (dl, x, context) -> dl.setX(unPerUnitRX(context, dl.getTerminal(), x))) .doubles("g", (dl, context) -> perUnitG(context, dl), (dl, g, context) -> dl.setG(unPerUnitG(context, dl, g))) @@ -608,14 +662,35 @@ static NetworkDataframeMapper danglingLines() { .booleans("paired", DanglingLine::isPaired) .booleans("fictitious", Identifiable::isFictitious, Identifiable::setFictitious, false) .strings("tie_line_id", dl -> dl.getTieLine().map(Identifiable::getId).orElse("")) + .strings("selected_limits_group", dl -> dl.getSelectedOperationalLimitsGroupId().orElse(DEFAULT_OPERATIONAL_LIMIT_GROUP_ID), + DanglingLine::setSelectedOperationalLimitsGroup, false) .addProperties() .build(); } + static NetworkDataframeMapper danglingLinesGeneration() { + return NetworkDataframeMapperBuilder.ofStream(network -> network.getDanglingLineStream().filter(dl -> Optional.ofNullable(dl.getGeneration()).isPresent()), + getOrThrow(Network::getDanglingLine, "Dangling line with generation")) + .stringsIndex("id", DanglingLine::getId) + .doubles("min_p", (dl, context) -> perUnitPQ(context, dl.getGeneration().getMinP()), + (dl, minP, context) -> dl.getGeneration().setMinP(unPerUnitPQ(context, minP))) + .doubles("max_p", (dl, context) -> perUnitPQ(context, dl.getGeneration().getMaxP()), + (dl, maxP, context) -> dl.getGeneration().setMaxP(unPerUnitPQ(context, maxP))) + .doubles("target_p", (dl, context) -> perUnitPQ(context, dl.getGeneration().getTargetP()), + (dl, targetP, context) -> dl.getGeneration().setTargetP(unPerUnitPQ(context, targetP))) + .doubles("target_q", (dl, context) -> perUnitPQ(context, dl.getGeneration().getTargetQ()), + (dl, targetQ, context) -> dl.getGeneration().setTargetQ(unPerUnitPQ(context, targetQ))) + .doubles("target_v", (dl, context) -> perUnitV(context, dl.getGeneration().getTargetV(), dl.getTerminal()), + (dl, targetV, context) -> dl.getGeneration().setTargetV(unPerUnitV(context, targetV, dl.getTerminal()))) + .booleans("voltage_regulator_on", dl -> dl.getGeneration().isVoltageRegulationOn(), + (dl, voltageRegulatorOn) -> dl.getGeneration().setVoltageRegulationOn(voltageRegulatorOn)) + .build(); + } + static NetworkDataframeMapper tieLines() { return NetworkDataframeMapperBuilder.ofStream(Network::getTieLineStream, getOrThrow(Network::getTieLine, "Tie line")) .stringsIndex("id", TieLine::getId) - .strings("name", tl -> tl.getOptionalName().orElse("")) + .strings("name", tl -> tl.getOptionalName().orElse(""), Identifiable::setName) .strings("dangling_line1_id", tl -> tl.getDanglingLine1().getId()) .strings("dangling_line2_id", tl -> tl.getDanglingLine2().getId()) .strings("pairing_key", tl -> Objects.toString(tl.getPairingKey(), "")) @@ -628,7 +703,7 @@ static NetworkDataframeMapper tieLines() { static NetworkDataframeMapper lccs() { return NetworkDataframeMapperBuilder.ofStream(Network::getLccConverterStationStream, getOrThrow(Network::getLccConverterStation, "LCC converter station")) .stringsIndex("id", LccConverterStation::getId) - .strings("name", st -> st.getOptionalName().orElse("")) + .strings("name", st -> st.getOptionalName().orElse(""), Identifiable::setName) .doubles("power_factor", (st, context) -> st.getPowerFactor(), (lcc, v, context) -> lcc.setPowerFactor((float) v)) .doubles("loss_factor", (st, context) -> st.getLossFactor(), (lcc, v, context) -> lcc.setLossFactor((float) v)) .doubles("p", getPerUnitP(), setPerUnitP()) @@ -647,7 +722,7 @@ static NetworkDataframeMapper lccs() { static NetworkDataframeMapper vscs() { return NetworkDataframeMapperBuilder.ofStream(Network::getVscConverterStationStream, getOrThrow(Network::getVscConverterStation, "VSC converter station")) .stringsIndex("id", VscConverterStation::getId) - .strings("name", st -> st.getOptionalName().orElse("")) + .strings("name", st -> st.getOptionalName().orElse(""), Identifiable::setName) .doubles("loss_factor", (vsc, context) -> vsc.getLossFactor(), (vscConverterStation, lf, context) -> vscConverterStation.setLossFactor((float) lf)) .doubles("min_q", ifExistsDoublePerUnitPQ(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMinQ), setPerUnitMinQ()) .doubles("max_q", ifExistsDoublePerUnitPQ(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMaxQ), setPerUnitMaxQ()) @@ -677,7 +752,7 @@ static NetworkDataframeMapper vscs() { private static NetworkDataframeMapper svcs() { return NetworkDataframeMapperBuilder.ofStream(Network::getStaticVarCompensatorStream, getOrThrow(Network::getStaticVarCompensator, "Static var compensator")) .stringsIndex("id", StaticVarCompensator::getId) - .strings("name", svc -> svc.getOptionalName().orElse("")) + .strings("name", svc -> svc.getOptionalName().orElse(""), Identifiable::setName) .doubles("b_min", (svc, context) -> svc.getBmin(), (svc, bMin, context) -> svc.setBmin(bMin)) .doubles("b_max", (svc, context) -> svc.getBmax(), (svc, bMax, context) -> svc.setBmax(bMax)) .doubles("target_v", (svc, context) -> perUnitTargetV(context, svc.getVoltageSetpoint(), svc.getRegulatingTerminal(), svc.getTerminal()), @@ -738,7 +813,7 @@ private static int getNode2(Switch s) { private static NetworkDataframeMapper switches() { return NetworkDataframeMapperBuilder.ofStream(Network::getSwitchStream, getOrThrow(Network::getSwitch, "Switch")) .stringsIndex("id", Switch::getId) - .strings("name", s -> s.getOptionalName().orElse("")) + .strings("name", s -> s.getOptionalName().orElse(""), Identifiable::setName) .enums("kind", SwitchKind.class, Switch::getKind) .booleans("open", Switch::isOpen, Switch::setOpen) .booleans("retained", Switch::isRetained, Switch::setRetained) @@ -755,7 +830,7 @@ private static NetworkDataframeMapper switches() { private static NetworkDataframeMapper voltageLevels() { return NetworkDataframeMapperBuilder.ofStream(Network::getVoltageLevelStream, getOrThrow(Network::getVoltageLevel, "Voltage level")) .stringsIndex("id", VoltageLevel::getId) - .strings("name", vl -> vl.getOptionalName().orElse("")) + .strings("name", vl -> vl.getOptionalName().orElse(""), Identifiable::setName) .strings("substation_id", vl -> vl.getSubstation().map(Identifiable::getId).orElse("")) .doubles("nominal_v", (vl, context) -> vl.getNominalV(), (vl, nominalV, context) -> vl.setNominalV(nominalV)) .doubles("high_voltage_limit", (vl, context) -> perUnitV(context, vl.getHighVoltageLimit(), vl.getNominalV()), @@ -771,7 +846,7 @@ private static NetworkDataframeMapper voltageLevels() { private static NetworkDataframeMapper substations() { return NetworkDataframeMapperBuilder.ofStream(Network::getSubstationStream, getOrThrow(Network::getSubstation, "Substation")) .stringsIndex("id", Identifiable::getId) - .strings("name", s -> s.getOptionalName().orElse("")) + .strings("name", s -> s.getOptionalName().orElse(""), Identifiable::setName) .strings("TSO", Substation::getTso, Substation::setTso) .strings("geo_tags", substation -> String.join(",", substation.getGeographicalTags())) .enums("country", Country.class, s -> s.getCountry().orElse(null), Substation::setCountry) @@ -780,10 +855,10 @@ private static NetworkDataframeMapper substations() { .build(); } - private static NetworkDataframeMapper busBars() { + private static NetworkDataframeMapper busbarSections() { return NetworkDataframeMapperBuilder.ofStream(Network::getBusbarSectionStream, getOrThrow(Network::getBusbarSection, "Bus bar section")) .stringsIndex("id", BusbarSection::getId) - .strings("name", bbs -> bbs.getOptionalName().orElse("")) + .strings("name", bbs -> bbs.getOptionalName().orElse(""), Identifiable::setName) .doubles("v", (busbar, context) -> perUnitV(context, busbar.getV(), busbar.getTerminal())) .doubles("angle", (busbar, context) -> perUnitAngle(context, busbar.getAngle())) .strings("voltage_level_id", bbs -> bbs.getTerminal().getVoltageLevel().getId()) @@ -798,7 +873,7 @@ private static NetworkDataframeMapper hvdcs() { return NetworkDataframeMapperBuilder.ofStream(Network::getHvdcLineStream, getOrThrow(Network::getHvdcLine, "HVDC line")) .stringsIndex("id", HvdcLine::getId) - .strings("name", l -> l.getOptionalName().orElse("")) + .strings("name", l -> l.getOptionalName().orElse(""), Identifiable::setName) .enums("converters_mode", HvdcLine.ConvertersMode.class, HvdcLine::getConvertersMode, HvdcLine::setConvertersMode) .doubles("target_p", (hvdc, context) -> perUnitPQ(context, hvdc.getActivePowerSetpoint()), (hvdc, aps, context) -> hvdc.setActivePowerSetpoint(unPerUnitPQ(context, aps))) @@ -823,16 +898,16 @@ private static NetworkDataframeMapper rtcSteps() { return NetworkDataframeMapperBuilder.ofStream(ratioTapChangerSteps, NetworkDataframes::getRatioTapChangers) .stringsIndex("id", triple -> triple.getLeft().getId()) .intsIndex("position", Triple::getRight) - .doubles("rho", (p, context) -> perUnitRho(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getRho()), - (p, rho, context) -> p.getMiddle().getStep(p.getRight()).setRho(unPerUnitRho(context, p.getLeft(), rho))) - .doubles("r", (p, context) -> perUnitRX(context, p.getMiddle().getStep(p.getRight()).getR(), p.getLeft()), - (p, r, context) -> p.getMiddle().getStep(p.getRight()).setR(unPerUnitRX(context, p.getLeft(), r))) - .doubles("x", (p, context) -> perUnitRX(context, p.getMiddle().getStep(p.getRight()).getX(), p.getLeft()), - (p, x, context) -> p.getMiddle().getStep(p.getRight()).setX(unPerUnitRX(context, p.getLeft(), x))) - .doubles("g", (p, context) -> perUnitBG(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getG()), - (p, g, context) -> p.getMiddle().getStep(p.getRight()).setG(unPerUnitBG(context, p.getLeft(), g))) - .doubles("b", (p, context) -> perUnitBG(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getB()), - (p, b, context) -> p.getMiddle().getStep(p.getRight()).setB(unPerUnitBG(context, p.getLeft(), b))) + .doubles("rho", (p, context) -> p.getMiddle().getStep(p.getRight()).getRho(), + (p, rho, context) -> p.getMiddle().getStep(p.getRight()).setRho(rho)) + .doubles("r", (p, context) -> p.getMiddle().getStep(p.getRight()).getR(), + (p, r, context) -> p.getMiddle().getStep(p.getRight()).setR(r)) + .doubles("x", (p, context) -> p.getMiddle().getStep(p.getRight()).getX(), + (p, x, context) -> p.getMiddle().getStep(p.getRight()).setX(x)) + .doubles("g", (p, context) -> p.getMiddle().getStep(p.getRight()).getG(), + (p, g, context) -> p.getMiddle().getStep(p.getRight()).setG(g)) + .doubles("b", (p, context) -> p.getMiddle().getStep(p.getRight()).getB(), + (p, b, context) -> p.getMiddle().getStep(p.getRight()).setB(b)) .build(); } @@ -854,18 +929,18 @@ private static NetworkDataframeMapper ptcSteps() { return NetworkDataframeMapperBuilder.ofStream(phaseTapChangerSteps, NetworkDataframes::getPhaseTapChangers) .stringsIndex("id", triple -> triple.getLeft().getId()) .intsIndex("position", Triple::getRight) - .doubles("rho", (p, context) -> perUnitRho(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getRho()), - (p, rho, context) -> p.getMiddle().getStep(p.getRight()).setRho(unPerUnitRho(context, p.getLeft(), rho))) + .doubles("rho", (p, context) -> p.getMiddle().getStep(p.getRight()).getRho(), + (p, rho, context) -> p.getMiddle().getStep(p.getRight()).setRho(rho)) .doubles("alpha", (p, context) -> perUnitAngle(context, p.getMiddle().getStep(p.getRight()).getAlpha()), (p, alpha, context) -> p.getMiddle().getStep(p.getRight()).setAlpha(unPerUnitAngle(context, alpha))) - .doubles("r", (p, context) -> perUnitRX(context, p.getMiddle().getStep(p.getRight()).getR(), p.getLeft()), - (p, r, context) -> p.getMiddle().getStep(p.getRight()).setR(unPerUnitRX(context, p.getLeft(), r))) - .doubles("x", (p, context) -> perUnitRX(context, p.getMiddle().getStep(p.getRight()).getX(), p.getLeft()), - (p, x, context) -> p.getMiddle().getStep(p.getRight()).setX(unPerUnitRX(context, p.getLeft(), x))) - .doubles("g", (p, context) -> perUnitBG(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getG()), - (p, g, context) -> p.getMiddle().getStep(p.getRight()).setG(unPerUnitBG(context, p.getLeft(), g))) - .doubles("b", (p, context) -> perUnitBG(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getB()), - (p, b, context) -> p.getMiddle().getStep(p.getRight()).setB(unPerUnitBG(context, p.getLeft(), b))) + .doubles("r", (p, context) -> p.getMiddle().getStep(p.getRight()).getR(), + (p, r, context) -> p.getMiddle().getStep(p.getRight()).setR(r)) + .doubles("x", (p, context) -> p.getMiddle().getStep(p.getRight()).getX(), + (p, x, context) -> p.getMiddle().getStep(p.getRight()).setX(x)) + .doubles("g", (p, context) -> p.getMiddle().getStep(p.getRight()).getG(), + (p, g, context) -> p.getMiddle().getStep(p.getRight()).setG(g)) + .doubles("b", (p, context) -> p.getMiddle().getStep(p.getRight()).getB(), + (p, b, context) -> p.getMiddle().getStep(p.getRight()).setB(b)) .build(); } @@ -989,6 +1064,10 @@ static NetworkDataframeMapper branches() { .doubles("p2", getPerUnitP2(), setPerUnitP2()) .doubles("q2", getPerUnitQ2(), setPerUnitQ2()) .doubles("i2", (branch, context) -> perUnitI(context, branch.getTerminal2())) + .strings("selected_limits_group_1", branch -> (String) branch.getSelectedOperationalLimitsGroupId1().orElse(DEFAULT_OPERATIONAL_LIMIT_GROUP_ID), + Branch::setSelectedOperationalLimitsGroup1, false) + .strings("selected_limits_group_2", branch -> (String) branch.getSelectedOperationalLimitsGroupId2().orElse(DEFAULT_OPERATIONAL_LIMIT_GROUP_ID), + Branch::setSelectedOperationalLimitsGroup2, false) .build(); } @@ -1081,12 +1160,32 @@ private static String getTerminalSideStr(Branch branch, Terminal terminal) { return ""; } + private static String getTerminalSideStr(ThreeWindingsTransformer t3wt, Terminal terminal) { + if (terminal == t3wt.getLeg1().getTerminal()) { + return ThreeSides.ONE.name(); + } else if (terminal == t3wt.getLeg2().getTerminal()) { + return ThreeSides.TWO.name(); + } else if (terminal == t3wt.getLeg3().getTerminal()) { + return ThreeSides.THREE.name(); + } + return ""; + } + + private static String getTerminalSideStr(Identifiable identifiable, Terminal terminal) { + if (identifiable instanceof Branch branch) { + return getTerminalSideStr(branch, terminal); + } else if (identifiable instanceof ThreeWindingsTransformer t3wt) { + return getTerminalSideStr(t3wt, terminal); + } + return ""; + } + static void setPhaseTapChangerRegulatedSide(TwoWindingsTransformer transformer, String side) { transformer.getPhaseTapChanger().setRegulationTerminal(getBranchTerminal(transformer, side)); } - private static NetworkDataframeMapper operationalLimits() { - return NetworkDataframeMapperBuilder.ofStream(NetworkUtil::getLimits) + private static NetworkDataframeMapper operationalLimits(boolean onlyActive) { + return NetworkDataframeMapperBuilder.ofStream(onlyActive ? NetworkUtil::getSelectedLimits : NetworkUtil::getLimits) .stringsIndex("element_id", TemporaryLimitData::getId) .enums("element_type", IdentifiableType.class, TemporaryLimitData::getElementType) .enums("side", TemporaryLimitData.Side.class, TemporaryLimitData::getSide) @@ -1095,6 +1194,8 @@ private static NetworkDataframeMapper operationalLimits() { .doubles("value", TemporaryLimitData::getValue) .ints("acceptable_duration", TemporaryLimitData::getAcceptableDuration) .booleans("fictitious", TemporaryLimitData::isFictitious, false) + .strings("group_name", TemporaryLimitData::getGroupId, false) + .booleans("selected", TemporaryLimitData::isSelected, false) .build(); } @@ -1168,7 +1269,9 @@ private static > Function getBusBreakerViewBus } private static void setBusBreakerViewBusId(Terminal t, String busId) { - Objects.requireNonNull(t).getBusBreakerView().setConnectableBus(busId); + if (!busId.isEmpty()) { + Objects.requireNonNull(t).getBusBreakerView().setConnectableBus(busId); + } } private static > void setBusBreakerViewBusId(T i, String busId) { @@ -1230,5 +1333,58 @@ private static Stream, String>> getAliasesData(Network netw .map(alias -> Pair.of(identifiable, alias))); } + private static NetworkDataframeMapper areaVoltageLevels() { + return NetworkDataframeMapperBuilder.ofStream(NetworkDataframes::areaVoltageLevelsData) + .stringsIndex("id", pair -> pair.getLeft().getId()) + .strings("voltage_level_id", pair -> pair.getRight().getId()) + .build(); + } + + private static Stream> areaVoltageLevelsData(Network network) { + return network.getAreaStream() + .flatMap(area -> area.getVoltageLevelStream() + .map(voltageLevel -> Pair.of(area, voltageLevel))); + } + + private static NetworkDataframeMapper areaBoundaries() { + return NetworkDataframeMapperBuilder.ofStream(NetworkDataframes::areaBoundariesData) + .stringsIndex("id", pair -> pair.getLeft().getId()) + .strings("boundary_type", pair -> getAreaBoundaryType(pair.getRight()), false) + .strings("element", pair -> getAreaBoundaryElement(pair.getRight())) + .strings("side", pair -> getAreaBoundarySide(pair.getRight()), false) + .booleans("ac", pair -> pair.getRight().isAc()) + .doubles("p", (pair, context) -> perUnitPQ(context, pair.getRight().getP())) + .doubles("q", (pair, context) -> perUnitPQ(context, pair.getRight().getQ())) + .build(); + } + + private static String getAreaBoundaryType(AreaBoundary areaBoundary) { + Objects.requireNonNull(areaBoundary); + return areaBoundary.getBoundary().map( + b -> PyPowsyblApiHeader.ElementType.DANGLING_LINE.name() + ).orElse(PyPowsyblApiHeader.ElementType.TERMINAL.name()); + } + + private static String getAreaBoundaryElement(AreaBoundary areaBoundary) { + Objects.requireNonNull(areaBoundary); + return areaBoundary.getBoundary().map( + b -> b.getDanglingLine().getId() + ).orElseGet(() -> areaBoundary.getTerminal().orElseThrow().getConnectable().getId()); + } + + private static String getAreaBoundarySide(AreaBoundary areaBoundary) { + Objects.requireNonNull(areaBoundary); + if (areaBoundary.getBoundary().isPresent()) { + return ""; + } + Terminal terminal = areaBoundary.getTerminal().orElseThrow(); + return getTerminalSideStr(terminal.getConnectable(), terminal); + } + + private static Stream> areaBoundariesData(Network network) { + return network.getAreaStream() + .flatMap(area -> area.getAreaBoundaryStream() + .map(areaBoundary -> Pair.of(area, areaBoundary))); + } } diff --git a/java/src/main/java/com/powsybl/dataframe/network/PerUnitUtil.java b/java/src/main/java/com/powsybl/dataframe/network/PerUnitUtil.java index e561fc99b6..770de41b07 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/PerUnitUtil.java +++ b/java/src/main/java/com/powsybl/dataframe/network/PerUnitUtil.java @@ -274,16 +274,12 @@ public static double perUnitRho(NetworkDataframeContext context, TwoWindingsTran return context.isPerUnit() ? rho * twt.getTerminal1().getVoltageLevel().getNominalV() / twt.getTerminal2().getVoltageLevel().getNominalV() : rho; } - public static double unPerUnitRho(NetworkDataframeContext context, TwoWindingsTransformer twt, double rho) { - return context.isPerUnit() ? rho * twt.getTerminal2().getVoltageLevel().getNominalV() / twt.getTerminal1().getVoltageLevel().getNominalV() : rho; - } - public static double perUnitAngle(NetworkDataframeContext context, double angle) { - return context.isPerUnit() ? toRadians(angle) : angle; + return context.isPerUnit() ? toRadians(angle) : angle; // this is not per-uniting but a convention to have radian in per unit view } public static double unPerUnitAngle(NetworkDataframeContext context, double angle) { - return context.isPerUnit() ? toDegrees(angle) : angle; + return context.isPerUnit() ? toDegrees(angle) : angle; // this is not per-uniting but a convention to have radian in per unit view } private static Complex computeY(double r, double x) { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/AbstractBranchSeries.java b/java/src/main/java/com/powsybl/dataframe/network/adders/AbstractBranchSeries.java index 0472154603..871733a5c0 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/AbstractBranchSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/AbstractBranchSeries.java @@ -20,7 +20,7 @@ /** * Common series for all branches. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ abstract class AbstractBranchSeries extends IdentifiableSeries { @@ -52,12 +52,28 @@ abstract class AbstractBranchSeries extends IdentifiableSeries { protected void setBranchAttributes(BranchAdder adder, int row) { setIdentifiableAttributes(adder, row); applyIfPresent(voltageLevels1, row, adder::setVoltageLevel1); - applyIfPresent(connectableBuses1, row, adder::setConnectableBus1); - applyIfPresent(buses1, row, adder::setBus1); + applyIfPresent(connectableBuses1, row, connectableBusId1 -> { + if (!connectableBusId1.isEmpty()) { + adder.setConnectableBus1(connectableBusId1); + } + }); + applyIfPresent(buses1, row, bus1 -> { + if (!bus1.isEmpty()) { + adder.setBus1(bus1); + } + }); applyIfPresent(nodes1, row, adder::setNode1); applyIfPresent(voltageLevels2, row, adder::setVoltageLevel2); - applyIfPresent(connectableBuses2, row, adder::setConnectableBus2); - applyIfPresent(buses2, row, adder::setBus2); + applyIfPresent(connectableBuses2, row, connectableBusId2 -> { + if (!connectableBusId2.isEmpty()) { + adder.setConnectableBus2(connectableBusId2); + } + }); + applyIfPresent(buses2, row, bus2 -> { + if (!bus2.isEmpty()) { + adder.setBus2(bus2); + } + }); applyIfPresent(nodes2, row, adder::setNode2); } diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/AbstractSimpleAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/AbstractSimpleAdder.java index fc6784e6e5..78a0a4d939 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/AbstractSimpleAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/AbstractSimpleAdder.java @@ -18,7 +18,7 @@ import java.util.OptionalInt; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public abstract class AbstractSimpleAdder implements NetworkElementAdder { @@ -63,6 +63,8 @@ private void add(InjectionAdder injectionAdder) { lccConverterStationAdder.add(); } else if (injectionAdder instanceof VscConverterStationAdder vscConverterStationAdder) { vscConverterStationAdder.add(); + } else if (injectionAdder instanceof GroundAdder groundAdder) { + groundAdder.add(); } else { throw new AssertionError("Given InjectionAdder not supported: " + injectionAdder.getClass().getName()); } diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/AliasDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/AliasDataframeAdder.java index 1531dc3fda..bc43201877 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/AliasDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/AliasDataframeAdder.java @@ -19,7 +19,7 @@ import java.util.List; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class AliasDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/AreaBoundariesDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/AreaBoundariesDataframeAdder.java new file mode 100644 index 0000000000..b0f34645eb --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/AreaBoundariesDataframeAdder.java @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.network.adders; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.update.IntSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.iidm.network.*; +import com.powsybl.python.commons.PyPowsyblApiHeader; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.*; + +import static com.powsybl.dataframe.network.adders.SeriesUtils.getRequiredStrings; + +/** + * @author Damien Jeandemange {@literal } + */ +public class AreaBoundariesDataframeAdder implements NetworkElementAdder { + + private static final List METADATA = List.of( + SeriesMetadata.stringIndex("id"), + SeriesMetadata.strings("boundary_type"), + SeriesMetadata.strings("element"), + SeriesMetadata.strings("side"), + SeriesMetadata.booleans("ac") + ); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + private static final class AreaBoundaries { + + private final StringSeries ids; + private final StringSeries boundaryTypes; + private final StringSeries elements; + private final StringSeries sides; + private final IntSeries acs; + + AreaBoundaries(UpdatingDataframe dataframe) { + this.ids = getRequiredStrings(dataframe, "id"); + this.boundaryTypes = dataframe.getStrings("boundary_type"); + this.elements = getRequiredStrings(dataframe, "element"); + this.sides = dataframe.getStrings("side"); + this.acs = dataframe.getInts("ac"); + } + + public StringSeries getIds() { + return ids; + } + + public StringSeries getBoundaryTypes() { + return boundaryTypes; + } + + public StringSeries getElements() { + return elements; + } + + public StringSeries getSides() { + return sides; + } + + public IntSeries getAcs() { + return acs; + } + } + + @Override + public void addElements(Network network, List dataframes) { + UpdatingDataframe primaryTable = dataframes.get(0); + AreaBoundaries series = new AreaBoundaries(primaryTable); + + Map>> danglingLineBoundaries = new HashMap<>(); + Map>> terminalBoundaries = new HashMap<>(); + for (int i = 0; i < primaryTable.getRowCount(); i++) { + String areaId = series.getIds().get(i); + String boundaryTypeStr = series.getBoundaryTypes() == null ? "DANGLING_LINE" : series.getBoundaryTypes().get(i); + String element = series.getElements().get(i); + String side = series.getSides() == null ? "" : series.getSides().get(i); + boolean ac = series.getAcs() == null || series.getAcs().get(i) == 1; + Area area = NetworkUtils.getAreaOrThrow(network, areaId); + // an empty element alone for an area indicates remove all boundaries in area + Connectable connectable = element.isEmpty() ? null : NetworkUtils.getConnectableOrThrow(network, element); + if (connectable == null) { + // add an entry so that everything will be deleted for the area + danglingLineBoundaries.computeIfAbsent(area, k -> new ArrayList<>()).add(Pair.of(null, null)); + terminalBoundaries.computeIfAbsent(area, k -> new ArrayList<>()).add(Pair.of(null, null)); + continue; + } + PyPowsyblApiHeader.ElementType boundaryType = PyPowsyblApiHeader.ElementType.valueOf(boundaryTypeStr); + if (Objects.equals(boundaryType, PyPowsyblApiHeader.ElementType.DANGLING_LINE)) { + // Boundary modeled by a dangling line + DanglingLine danglingLine = NetworkUtils.getDanglingLineOrThrow(network, element); + danglingLineBoundaries.computeIfAbsent(area, k -> new ArrayList<>()).add(Pair.of(danglingLine, ac)); + } else if (Objects.equals(boundaryType, PyPowsyblApiHeader.ElementType.TERMINAL)) { + // Boundary modeled by a terminal + Terminal terminal = NetworkUtils.getTerminalOrThrow(network, element, side); + terminalBoundaries.computeIfAbsent(area, k -> new ArrayList<>()).add(Pair.of(terminal, ac)); + } else { + throw new PowsyblException("Area boundary boundary_type must be either DANGLING_LINE or TERMINAL"); + } + } + // delete boundaries of involved areas + // If a given area has *only* a null boundary in the updating dataframe, this results + // in the area having all its boundaries unlinked. + Set areas = new HashSet<>(danglingLineBoundaries.keySet()); + areas.addAll(terminalBoundaries.keySet()); + areas.forEach(a -> a.getAreaBoundaryStream().toList() + .forEach(areaBoundary -> { + areaBoundary.getBoundary().ifPresent(a::removeAreaBoundary); + areaBoundary.getTerminal().ifPresent(a::removeAreaBoundary); + })); + // create new boundaries + danglingLineBoundaries.forEach((area, list) -> list.stream() + .filter(pair -> pair.getLeft() != null) + .forEach(pair -> area.newAreaBoundary() + .setBoundary(pair.getLeft().getBoundary()) + .setAc(pair.getRight()) + .add())); + terminalBoundaries.forEach((area, list) -> list.stream() + .filter(pair -> pair.getLeft() != null) + .forEach(pair -> area.newAreaBoundary() + .setTerminal(pair.getLeft()) + .setAc(pair.getRight()) + .add())); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/AreaDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/AreaDataframeAdder.java new file mode 100644 index 0000000000..91aa53faec --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/AreaDataframeAdder.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.network.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.update.DoubleSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.iidm.network.AreaAdder; +import com.powsybl.iidm.network.Network; + +import java.util.Collections; +import java.util.List; + +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; + +/** + * @author Damien Jeandemange {@literal } + */ +public class AreaDataframeAdder extends AbstractSimpleAdder { + + private static final List METADATA = List.of( + SeriesMetadata.stringIndex("id"), + SeriesMetadata.strings("name"), + SeriesMetadata.strings("area_type"), + SeriesMetadata.doubles("interchange_target") + ); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + private static class AreaSeries extends IdentifiableSeries { + + private final StringSeries areaTypes; + private final DoubleSeries interchangeTargets; + + AreaSeries(UpdatingDataframe dataframe) { + super(dataframe); + this.areaTypes = dataframe.getStrings("area_type"); + this.interchangeTargets = dataframe.getDoubles("interchange_target"); + } + + AreaAdder create(Network network, int row) { + AreaAdder adder = network.newArea(); + setIdentifiableAttributes(adder, row); + applyIfPresent(areaTypes, row, adder::setAreaType); + applyIfPresent(interchangeTargets, row, adder::setInterchangeTarget); + return adder; + } + } + + @Override + public void addElements(Network network, UpdatingDataframe dataframe, AdditionStrategy additionStrategy, boolean throwException, ReportNode reportNode) { + AreaSeries series = new AreaSeries(dataframe); + for (int row = 0; row < dataframe.getRowCount(); row++) { + AreaAdder adder = series.create(network, row); + adder.add(); + } + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/AreaVoltageLevelsDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/AreaVoltageLevelsDataframeAdder.java new file mode 100644 index 0000000000..c99315f349 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/AreaVoltageLevelsDataframeAdder.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.network.adders; + +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.iidm.network.Area; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.VoltageLevel; + +import java.util.*; + +import static com.powsybl.dataframe.network.adders.SeriesUtils.*; + +/** + * @author Damien Jeandemange {@literal } + */ +public class AreaVoltageLevelsDataframeAdder implements NetworkElementAdder { + + private static final List METADATA = List.of( + SeriesMetadata.stringIndex("id"), + SeriesMetadata.strings("voltage_level_id") + ); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + private static final class AreaVoltageLevels { + + private final StringSeries ids; + private final StringSeries voltageLevels; + + AreaVoltageLevels(UpdatingDataframe dataframe) { + this.ids = getRequiredStrings(dataframe, "id"); + this.voltageLevels = getRequiredStrings(dataframe, "voltage_level_id"); + } + + public StringSeries getIds() { + return ids; + } + + public StringSeries getVoltageLevels() { + return voltageLevels; + } + } + + @Override + public void addElements(Network network, List dataframes) { + UpdatingDataframe primaryTable = dataframes.get(0); + AreaVoltageLevels series = new AreaVoltageLevels(primaryTable); + + Map> areaVoltageLevels = new HashMap<>(); + for (int i = 0; i < primaryTable.getRowCount(); i++) { + String areaId = series.getIds().get(i); + String voltageLevelId = series.getVoltageLevels().get(i); + Area area = NetworkUtils.getAreaOrThrow(network, areaId); + // an empty voltageLevelId alone for an area indicates remove all voltage levels in area + VoltageLevel voltageLevel = voltageLevelId.isEmpty() ? null : NetworkUtils.getVoltageLevelOrThrow(network, voltageLevelId); + areaVoltageLevels.computeIfAbsent(area, k -> new ArrayList<>()).add(voltageLevel); + } + areaVoltageLevels.forEach((area, voltageLevels) -> { + // For each area, remove all existing voltageLevels and add new ones. + // If a given area has *only* a null voltageLevel in the updating dataframe, this results + // in the area having all its voltage levels unlinked. + area.getVoltageLevelStream().toList().forEach(area::removeVoltageLevel); + voltageLevels.stream().filter(Objects::nonNull).forEach(area::addVoltageLevel); + }); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/BatteryDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/BatteryDataframeAdder.java index 5065ed4e15..4f161c1b29 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/BatteryDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/BatteryDataframeAdder.java @@ -24,9 +24,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class BatteryDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/BusBarDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/BusBarDataframeAdder.java index e16473d303..32ba3f431e 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/BusBarDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/BusBarDataframeAdder.java @@ -22,9 +22,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class BusBarDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/BusDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/BusDataframeAdder.java index c77890f3e5..05986c61ce 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/BusDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/BusDataframeAdder.java @@ -20,9 +20,9 @@ import static com.powsybl.dataframe.network.adders.NetworkUtils.getVoltageLevelOrThrow; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class BusDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/CurveReactiveLimitsDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/CurveReactiveLimitsDataframeAdder.java index 6a98c31ae1..fd21d20f28 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/CurveReactiveLimitsDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/CurveReactiveLimitsDataframeAdder.java @@ -21,7 +21,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.getRequiredStrings; /** - * @author Massimo Ferraro + * @author Massimo Ferraro {@literal } */ public class CurveReactiveLimitsDataframeAdder implements NetworkElementAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/DanglingLineDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/DanglingLineDataframeAdder.java index e34c845810..649b4275bd 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/DanglingLineDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/DanglingLineDataframeAdder.java @@ -7,28 +7,32 @@ */ package com.powsybl.dataframe.network.adders; +import com.powsybl.commons.PowsyblException; import com.powsybl.commons.report.ReportNode; import com.powsybl.dataframe.SeriesMetadata; import com.powsybl.dataframe.update.DoubleSeries; +import com.powsybl.dataframe.update.IntSeries; import com.powsybl.dataframe.update.StringSeries; import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.iidm.modification.topology.CreateFeederBay; +import com.powsybl.iidm.modification.topology.CreateFeederBayBuilder; import com.powsybl.iidm.network.DanglingLineAdder; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.iidm.network.extensions.ConnectablePosition; -import java.util.Collections; -import java.util.List; -import java.util.Optional; +import java.util.*; import static com.powsybl.dataframe.network.adders.NetworkUtils.getVoltageLevelOrThrowWithBusOrBusbarSectionId; +import static com.powsybl.dataframe.network.adders.SeriesUtils.applyBooleanIfPresent; import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ -public class DanglingLineDataframeAdder extends AbstractSimpleAdder { +public class DanglingLineDataframeAdder implements NetworkElementAdder { private static final List METADATA = List.of( SeriesMetadata.stringIndex("id"), @@ -46,9 +50,19 @@ public class DanglingLineDataframeAdder extends AbstractSimpleAdder { SeriesMetadata.strings("pairing_key") ); + private static final List GENERATION_METADATA = List.of( + SeriesMetadata.stringIndex("id"), + SeriesMetadata.doubles("min_p"), + SeriesMetadata.doubles("max_p"), + SeriesMetadata.doubles("target_p"), + SeriesMetadata.doubles("target_q"), + SeriesMetadata.doubles("target_v"), + SeriesMetadata.booleans("voltage_regulator_on") + ); + @Override public List> getMetadata() { - return Collections.singletonList(METADATA); + return List.of(METADATA, GENERATION_METADATA); } private static class DanglingLineSeries extends InjectionSeries { @@ -63,7 +77,15 @@ private static class DanglingLineSeries extends InjectionSeries { private final StringSeries busOrBusbarSections; private final StringSeries pairingKey; - DanglingLineSeries(UpdatingDataframe dataframe) { + private final Map generationIndexes; + private final DoubleSeries minP; + private final DoubleSeries maxP; + private final DoubleSeries targetP; + private final DoubleSeries targetQ; + private final DoubleSeries targetV; + private final IntSeries voltageRegulatorOn; + + DanglingLineSeries(UpdatingDataframe dataframe, UpdatingDataframe generationDataframe) { super(dataframe); this.voltageLevels = dataframe.getStrings("voltage_level_id"); this.p0 = dataframe.getDoubles("p0"); @@ -74,6 +96,24 @@ private static class DanglingLineSeries extends InjectionSeries { this.b = dataframe.getDoubles("b"); this.busOrBusbarSections = dataframe.getStrings("bus_or_busbar_section_id"); this.pairingKey = dataframe.getStrings("pairing_key"); + + if (generationDataframe != null && generationDataframe.getRowCount() > 0) { + this.minP = generationDataframe.getDoubles("min_p"); + this.maxP = generationDataframe.getDoubles("max_p"); + this.targetP = generationDataframe.getDoubles("target_p"); + this.targetQ = generationDataframe.getDoubles("target_q"); + this.targetV = generationDataframe.getDoubles("target_v"); + this.voltageRegulatorOn = generationDataframe.getInts("voltage_regulator_on"); + this.generationIndexes = getGenerationIndexes(generationDataframe); + } else { + this.minP = null; + this.maxP = null; + this.targetP = null; + this.targetQ = null; + this.targetV = null; + this.voltageRegulatorOn = null; + this.generationIndexes = null; + } } Optional createAdder(Network network, int row, boolean throwException) { @@ -89,21 +129,91 @@ Optional createAdder(Network network, int row, boolean throwE applyIfPresent(g, row, adder::setG); applyIfPresent(b, row, adder::setB); applyIfPresent(pairingKey, row, adder::setPairingKey); + addGenerationIfPresent(adder, row); return Optional.of(adder); } else { return Optional.empty(); } } + + private void addGenerationIfPresent(DanglingLineAdder adder, int row) { + if (generationIndexes == null) { + return; + } + String id = ids.get(row); + Integer generationRow = generationIndexes.get(id); + if (generationRow != null) { + DanglingLineAdder.GenerationAdder genAdder = adder.newGeneration(); + applyIfPresent(minP, generationRow, genAdder::setMinP); + applyIfPresent(maxP, generationRow, genAdder::setMaxP); + applyIfPresent(targetP, generationRow, genAdder::setTargetP); + applyIfPresent(targetQ, generationRow, genAdder::setTargetQ); + applyIfPresent(targetV, generationRow, genAdder::setTargetV); + applyBooleanIfPresent(voltageRegulatorOn, generationRow, genAdder::setVoltageRegulationOn); + genAdder.add(); + } + } + + /** + * Mapping shunt ID --> index of line in dataframe + */ + private static Map getGenerationIndexes(UpdatingDataframe generationDf) { + StringSeries ids = generationDf.getStrings("id"); + if (ids == null) { + throw new PowsyblException("Dangling line generation dataframe: id is not set"); + } + Map indexes = new HashMap<>(); + for (int generationIndex = 0; generationIndex < generationDf.getRowCount(); generationIndex++) { + String danglingLineId = ids.get(generationIndex); + indexes.put(danglingLineId, generationIndex); + } + return indexes; + } + + void create(Network network, int row, boolean throwException) { + Optional adder = createAdder(network, row, throwException); + adder.ifPresent(DanglingLineAdder::add); + } + + void createWithBay(Network network, int row, UpdatingDataframe primaryDataframe, boolean throwException, ReportNode reportNode) { + Optional adder = createAdder(network, row, throwException); + adder.ifPresent(presentAdder -> addWithBay(network, row, primaryDataframe, presentAdder, throwException, reportNode)); + } + + void addWithBay(Network network, int row, UpdatingDataframe dataframe, DanglingLineAdder adder, boolean throwException, ReportNode reportNode) { + String busOrBusbarSectionId = busOrBusbarSections.get(row); + OptionalInt injectionPositionOrder = dataframe.getIntValue("position_order", row); + ConnectablePosition.Direction direction = ConnectablePosition.Direction.valueOf(dataframe.getStringValue("direction", row).orElse("BOTTOM")); + CreateFeederBayBuilder builder = new CreateFeederBayBuilder() + .withInjectionAdder(adder) + .withBusOrBusbarSectionId(busOrBusbarSectionId) + .withInjectionDirection(direction); + if (injectionPositionOrder.isPresent()) { + builder.withInjectionPositionOrder(injectionPositionOrder.getAsInt()); + } + CreateFeederBay modification = builder.build(); + modification.apply(network, throwException, reportNode == null ? ReportNode.NO_OP : reportNode); + } } @Override - public void addElements(Network network, UpdatingDataframe dataframe, AdditionStrategy additionStrategy, boolean throwException, ReportNode reportNode) { - DanglingLineSeries series = new DanglingLineSeries(dataframe); + public void addElements(Network network, List dataframes) { + UpdatingDataframe dataframe = dataframes.get(0); + UpdatingDataframe generationDataframe = dataframes.get(1); + DanglingLineSeries series = new DanglingLineSeries(dataframe, generationDataframe); for (int row = 0; row < dataframe.getRowCount(); row++) { - Optional adder = series.createAdder(network, row, throwException); - if (adder.isPresent()) { - additionStrategy.add(network, dataframe, adder.get(), row, throwException, reportNode); - } + series.create(network, row, true); } } + + @Override + public void addElementsWithBay(Network network, List dataframes, boolean throwException, ReportNode reportNode) { + UpdatingDataframe dataframe = dataframes.get(0); + UpdatingDataframe generationDataframe = dataframes.get(1); + DanglingLineSeries series = new DanglingLineSeries(dataframe, generationDataframe); + for (int row = 0; row < dataframe.getRowCount(); row++) { + series.createWithBay(network, row, dataframe, true, reportNode); + } + } + } diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/GeneratorDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/GeneratorDataframeAdder.java index 627f55abf2..ad1e08fe4d 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/GeneratorDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/GeneratorDataframeAdder.java @@ -28,9 +28,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class GeneratorDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/GroundDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/GroundDataframeAdder.java new file mode 100644 index 0000000000..cce62ddd6e --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/GroundDataframeAdder.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.network.adders; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.iidm.network.*; + +import java.util.List; +import java.util.Optional; + +import static com.powsybl.dataframe.network.adders.NetworkUtils.getVoltageLevelOrThrowWithBusOrBusbarSectionId; + +/** + * @author Hugo Kulesza {@literal } + */ +public class GroundDataframeAdder extends AbstractSimpleAdder { + + private static final List METADATA = List.of( + SeriesMetadata.stringIndex("id"), + SeriesMetadata.strings("voltage_level_id"), + SeriesMetadata.strings("bus_id"), + SeriesMetadata.strings("connectable_bus_id"), + SeriesMetadata.ints("node"), + SeriesMetadata.strings("name") + ); + + @Override + public List> getMetadata() { + return List.of(METADATA); + } + + private static class GroundSeries extends InjectionSeries { + + private final StringSeries voltageLevels; + private final StringSeries busOrBusbarSections; + + GroundSeries(UpdatingDataframe dataframe) { + super(dataframe); + this.voltageLevels = dataframe.getStrings("voltage_level_id"); + this.busOrBusbarSections = dataframe.getStrings("bus_id"); + } + + Optional createAdder(Network network, int row, boolean throwException) { + Optional vl = getVoltageLevelOrThrowWithBusOrBusbarSectionId(network, row, voltageLevels, busOrBusbarSections, throwException); + if (vl.isPresent()) { + GroundAdder adder = vl.get().newGround(); + setInjectionAttributes(adder, row); + return Optional.of(adder); + } + return Optional.empty(); + } + } + + @Override + public void addElements(Network network, UpdatingDataframe dataframe, AdditionStrategy addition, boolean throwException, ReportNode reportNode) { + GroundSeries series = new GroundSeries(dataframe); + for (int row = 0; row < dataframe.getRowCount(); row++) { + Optional adder = series.createAdder(network, row, throwException); + if (adder.isPresent()) { + addition.add(network, dataframe, adder.get(), row, throwException, reportNode); + } + } + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/HvdcDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/HvdcDataframeAdder.java index d30f89dab1..7ea782535e 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/HvdcDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/HvdcDataframeAdder.java @@ -19,9 +19,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class HvdcDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/IdentifiableSeries.java b/java/src/main/java/com/powsybl/dataframe/network/adders/IdentifiableSeries.java index 703bfae1f6..c35168d11d 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/IdentifiableSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/IdentifiableSeries.java @@ -17,7 +17,7 @@ * This hierarchy of classes aims at gathering usual columns on initialization * and set the corresponding data into IIDM adders. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ class IdentifiableSeries { protected final StringSeries ids; diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/InjectionSeries.java b/java/src/main/java/com/powsybl/dataframe/network/adders/InjectionSeries.java index 3714be2e01..489292c5fc 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/InjectionSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/InjectionSeries.java @@ -17,7 +17,7 @@ /** * Common series for all injections. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ class InjectionSeries extends IdentifiableSeries { @@ -34,8 +34,16 @@ class InjectionSeries extends IdentifiableSeries { protected void setInjectionAttributes(InjectionAdder adder, int row) { setIdentifiableAttributes(adder, row); - applyIfPresent(connectableBuses, row, adder::setConnectableBus); - applyIfPresent(buses, row, adder::setBus); + applyIfPresent(connectableBuses, row, connectableBusId -> { + if (!connectableBusId.isEmpty()) { + adder.setConnectableBus(connectableBusId); + } + }); + applyIfPresent(buses, row, bus -> { + if (!bus.isEmpty()) { + adder.setBus(bus); + } + }); applyIfPresent(nodes, row, adder::setNode); } } diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/InternalConnectionDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/InternalConnectionDataframeAdder.java new file mode 100644 index 0000000000..fc15ec8e2d --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/InternalConnectionDataframeAdder.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2024, Artelys (http://www.artelys.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.network.adders; + +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.update.IntSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.VoltageLevel; + +import java.util.Collections; +import java.util.List; + +/** + * @author Damien Jeandemange {@literal } + */ +public class InternalConnectionDataframeAdder extends AbstractSimpleAdder { + + private static final List METADATA = List.of( + SeriesMetadata.stringIndex("voltage_level_id"), + SeriesMetadata.ints("node1"), + SeriesMetadata.ints("node2") + ); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + private static class InternalConnectionSeries { + private final StringSeries voltageLevelIds; + private final IntSeries nodes1; + private final IntSeries nodes2; + + InternalConnectionSeries(UpdatingDataframe dataframe) { + this.voltageLevelIds = SeriesUtils.getRequiredStrings(dataframe, "voltage_level_id"); + this.nodes1 = SeriesUtils.getRequiredInts(dataframe, "node1"); + this.nodes2 = SeriesUtils.getRequiredInts(dataframe, "node2"); + } + + void create(Network network, int row) { + VoltageLevel.NodeBreakerView view = NetworkUtils.getVoltageLevelNodeBreakerViewOrThrow(network, voltageLevelIds.get(row)); + view.newInternalConnection().setNode1(nodes1.get(row)).setNode2(nodes2.get(row)).add(); + } + + void delete(Network network, int row) { + VoltageLevel.NodeBreakerView view = NetworkUtils.getVoltageLevelNodeBreakerViewOrThrow(network, voltageLevelIds.get(row)); + view.removeInternalConnections(nodes1.get(row), nodes2.get(row)); + } + } + + @Override + public void addElements(Network network, UpdatingDataframe dataframe) { + InternalConnectionSeries series = new InternalConnectionSeries(dataframe); + for (int row = 0; row < dataframe.getRowCount(); row++) { + series.create(network, row); + } + } + + public static void deleteElements(Network network, UpdatingDataframe dataframe) { + InternalConnectionSeries series = new InternalConnectionSeries(dataframe); + for (int row = 0; row < dataframe.getRowCount(); row++) { + series.delete(network, row); + } + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/LccStationDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/LccStationDataframeAdder.java index 4f31b3ca06..fe6b8fc357 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/LccStationDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/LccStationDataframeAdder.java @@ -24,9 +24,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class LccStationDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/LimitsDataframeAdderKey.java b/java/src/main/java/com/powsybl/dataframe/network/adders/LimitsDataframeAdderKey.java index 3094874206..40d4f7def1 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/LimitsDataframeAdderKey.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/LimitsDataframeAdderKey.java @@ -6,11 +6,13 @@ public class LimitsDataframeAdderKey { private final String elementId; private final String side; private final String limitType; + private final String groupId; - public LimitsDataframeAdderKey(String elementId, String side, String limitType) { + public LimitsDataframeAdderKey(String elementId, String side, String limitType, String groupId) { this.elementId = elementId; this.side = side; this.limitType = limitType; + this.groupId = groupId; } public String getElementId() { @@ -25,12 +27,17 @@ public String getLimitType() { return limitType; } + public String getGroupId() { + return groupId; + } + @Override public String toString() { return "LimitsDataframeAdderKey{" + "elementId='" + elementId + '\'' + ", side='" + side + '\'' + ", limitType='" + limitType + '\'' + + ", groupId='" + groupId + '\'' + '}'; } @@ -43,11 +50,11 @@ public boolean equals(Object o) { return false; } LimitsDataframeAdderKey that = (LimitsDataframeAdderKey) o; - return Objects.equals(elementId, that.elementId) && Objects.equals(side, that.side) && Objects.equals(limitType, that.limitType); + return Objects.equals(elementId, that.elementId) && Objects.equals(side, that.side) && Objects.equals(limitType, that.limitType) && Objects.equals(groupId, that.groupId); } @Override public int hashCode() { - return Objects.hash(elementId, side, limitType); + return Objects.hash(elementId, side, limitType, groupId); } } diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/LineDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/LineDataframeAdder.java index b5ca4b168c..29ce713322 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/LineDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/LineDataframeAdder.java @@ -15,9 +15,9 @@ import java.util.List; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class LineDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/LoadDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/LoadDataframeAdder.java index 5a1a638db2..af312f5b46 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/LoadDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/LoadDataframeAdder.java @@ -25,9 +25,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class LoadDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/MinMaxReactiveLimitsDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/MinMaxReactiveLimitsDataframeAdder.java index ee9e9f21b3..8567c81c10 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/MinMaxReactiveLimitsDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/MinMaxReactiveLimitsDataframeAdder.java @@ -24,7 +24,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.getRequiredStrings; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public class MinMaxReactiveLimitsDataframeAdder implements NetworkElementAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkElementAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkElementAdder.java index 67f487ff74..e3df64f6fb 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkElementAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkElementAdder.java @@ -15,7 +15,7 @@ import java.util.List; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public interface NetworkElementAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkElementAdders.java b/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkElementAdders.java index 2362e6a238..6f9d7c9055 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkElementAdders.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkElementAdders.java @@ -20,7 +20,7 @@ import static com.powsybl.dataframe.DataframeElementType.*; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public final class NetworkElementAdders { @@ -32,6 +32,7 @@ public final class NetworkElementAdders { Map.entry(TWO_WINDINGS_TRANSFORMER, new TwtDataframeAdder()), Map.entry(THREE_WINDINGS_TRANSFORMER, new ThreeWindingsTransformerDataframeAdder()), Map.entry(LOAD, new LoadDataframeAdder()), + Map.entry(GROUND, new GroundDataframeAdder()), Map.entry(VSC_CONVERTER_STATION, new VscStationDataframeAdder()), Map.entry(LCC_CONVERTER_STATION, new LccStationDataframeAdder()), Map.entry(BUSBAR_SECTION, new BusBarDataframeAdder()), @@ -48,7 +49,11 @@ public final class NetworkElementAdders { Map.entry(MINMAX_REACTIVE_LIMITS, new MinMaxReactiveLimitsDataframeAdder()), Map.entry(REACTIVE_CAPABILITY_CURVE_POINT, new CurveReactiveLimitsDataframeAdder()), Map.entry(ALIAS, new AliasDataframeAdder()), - Map.entry(TIE_LINE, new TieLineDataframeAdder()) + Map.entry(TIE_LINE, new TieLineDataframeAdder()), + Map.entry(AREA, new AreaDataframeAdder()), + Map.entry(AREA_VOLTAGE_LEVELS, new AreaVoltageLevelsDataframeAdder()), + Map.entry(AREA_BOUNDARIES, new AreaBoundariesDataframeAdder()), + Map.entry(INTERNAL_CONNECTION, new InternalConnectionDataframeAdder()) ); private static final Map EXTENSIONS_ADDERS = NetworkExtensions.createExtensionsAdders(); diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkUtils.java b/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkUtils.java index 2da0eb537b..dbcfee4a02 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkUtils.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/NetworkUtils.java @@ -14,7 +14,7 @@ import java.util.Optional; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public final class NetworkUtils { @@ -31,6 +31,14 @@ public static VoltageLevel getVoltageLevelOrThrow(Network network, String id) { return voltageLevel; } + public static VoltageLevel.NodeBreakerView getVoltageLevelNodeBreakerViewOrThrow(Network network, String id) { + VoltageLevel voltageLevel = getVoltageLevelOrThrow(network, id); + if (voltageLevel.getTopologyKind() != TopologyKind.NODE_BREAKER) { + throw new PowsyblException("Voltage level '" + id + "' is not of Node/Breaker topology kind."); + } + return voltageLevel.getNodeBreakerView(); + } + public static Optional getVoltageLevelOrThrowWithBusOrBusbarSectionId(Network network, int row, StringSeries voltageLevels, StringSeries busOrBusbarSections, boolean throwException) { if (voltageLevels == null) { if (busOrBusbarSections != null) { @@ -73,4 +81,52 @@ public static Identifiable getIdentifiableOrThrow(Network network, String id) } return identifiable; } + + public static Injection getGenOrLoadOrBusbarSectionOrThrow(Network network, String id) { + Identifiable identifiable = getIdentifiableOrThrow(network, id); + if (!(identifiable instanceof Generator) && !(identifiable instanceof Load) && !(identifiable instanceof BusbarSection)) { + throw new PowsyblException("Network element '" + id + "' is not a generator, bus bar section, or load"); + } + return (Injection) identifiable; + } + + public static Area getAreaOrThrow(Network network, String id) { + Area area = network.getArea(id); + if (area == null) { + throw new PowsyblException("Area '" + id + DOES_NOT_EXIST); + } + return area; + } + + public static DanglingLine getDanglingLineOrThrow(Network network, String id) { + DanglingLine danglingLine = network.getDanglingLine(id); + if (danglingLine == null) { + throw new PowsyblException("Dangling Line '" + id + DOES_NOT_EXIST); + } + return danglingLine; + } + + public static Connectable getConnectableOrThrow(Network network, String id) { + Connectable connectable = network.getConnectable(id); + if (connectable == null) { + throw new PowsyblException("Connectable '" + id + DOES_NOT_EXIST); + } + return connectable; + } + + public static Terminal getTerminalOrThrow(Network network, String id, String side) { + Connectable connectable = getConnectableOrThrow(network, id); + Terminal terminal; + if (connectable instanceof Injection injection) { + terminal = injection.getTerminal(); + } else if (connectable instanceof Branch branch) { + terminal = branch.getTerminal(TwoSides.valueOf(side)); + } else if (connectable instanceof ThreeWindingsTransformer t3wt) { + terminal = t3wt.getTerminal(ThreeSides.valueOf(side)); + } else { + // Never supposed to happen + throw new PowsyblException("Connectable '" + id + "' is not an injection, branch, or three windings transformer"); + } + return terminal; + } } diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/OperationalLimitsDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/OperationalLimitsDataframeAdder.java index 642529e7d8..266ee00fbe 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/OperationalLimitsDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/OperationalLimitsDataframeAdder.java @@ -19,12 +19,12 @@ public class OperationalLimitsDataframeAdder implements NetworkElementAdder { private static final List METADATA = List.of( SeriesMetadata.stringIndex("element_id"), SeriesMetadata.strings("name"), - SeriesMetadata.strings("element_type"), SeriesMetadata.strings("side"), SeriesMetadata.strings("type"), SeriesMetadata.doubles("value"), SeriesMetadata.ints("acceptable_duration"), - SeriesMetadata.booleans("is_fictitious") + SeriesMetadata.booleans("fictitious"), + SeriesMetadata.strings("group_name") ); @Override @@ -36,22 +36,22 @@ private static final class OperationalLimitsSeries { private final StringSeries elementIds; private final StringSeries names; - private final StringSeries elementTypes; private final StringSeries sides; private final StringSeries types; private final DoubleSeries values; private final IntSeries acceptableDurations; private final IntSeries fictitious; + private final StringSeries groupNames; OperationalLimitsSeries(UpdatingDataframe dataframe) { this.elementIds = getRequiredStrings(dataframe, "element_id"); this.names = dataframe.getStrings("name"); - this.elementTypes = getRequiredStrings(dataframe, "element_type"); this.sides = getRequiredStrings(dataframe, "side"); this.types = getRequiredStrings(dataframe, "type"); this.values = getRequiredDoubles(dataframe, "value"); this.acceptableDurations = getRequiredInts(dataframe, "acceptable_duration"); - this.fictitious = dataframe.getInts("is_fictitious"); + this.fictitious = dataframe.getInts("fictitious"); + this.groupNames = dataframe.getStrings("group_name"); } public StringSeries getElementIds() { @@ -62,10 +62,6 @@ public StringSeries getNames() { return names; } - public StringSeries getElementTypes() { - return elementTypes; - } - public StringSeries getSides() { return sides; } @@ -85,6 +81,10 @@ public IntSeries getAcceptableDurations() { public IntSeries getFictitious() { return fictitious; } + + public StringSeries getGroupNames() { + return groupNames; + } } @Override @@ -97,7 +97,12 @@ public void addElements(Network network, List dataframes) { String elementId = series.getElementIds().get(i); String side = series.getSides().get(i); String limitType = series.getTypes().get(i); - LimitsDataframeAdderKey key = new LimitsDataframeAdderKey(elementId, side, limitType); + StringSeries groupNames = series.getGroupNames(); + String groupName = null; + if (groupNames != null) { + groupName = series.getGroupNames().get(i); + } + LimitsDataframeAdderKey key = new LimitsDataframeAdderKey(elementId, side, limitType, groupName); indexMap.computeIfAbsent(key, k -> new TIntArrayList()).add(i); } @@ -106,23 +111,22 @@ public void addElements(Network network, List dataframes) { private static void addElements(Network network, OperationalLimitsSeries series, Map indexMap) { indexMap.forEach((key, indexList) -> createLimits(network, series, key.getElementId(), - key.getSide(), key.getLimitType(), indexList)); + key.getSide(), key.getLimitType(), key.getGroupId(), indexList)); } private static void createLimits(Network network, OperationalLimitsSeries series, String elementId, String side, String type, - TIntArrayList indexList) { - IdentifiableType elementType = IdentifiableType.valueOf(series.getElementTypes().get(indexList.get(0))); + String groupId, TIntArrayList indexList) { LimitType limitType = LimitType.valueOf(type); TemporaryLimitData.Side limitSide = TemporaryLimitData.Side.valueOf(side); - LoadingLimitsAdder adder = getAdder(network, elementType, elementId, limitType, limitSide); + LoadingLimitsAdder adder = getAdder(network, elementId, limitType, limitSide, groupId); for (int index : indexList.toArray()) { createLimits(adder, index, series); } adder.add(); } - private static void createLimits(LoadingLimitsAdder adder, int row, OperationalLimitsSeries series) { + private static void createLimits(LoadingLimitsAdder adder, int row, OperationalLimitsSeries series) { int acceptableDuration = series.getAcceptableDurations().get(row); if (acceptableDuration == -1) { applyIfPresent(series.getValues(), row, adder::setPermanentLimit); @@ -249,30 +253,20 @@ private static TwoSides toBranchSide(TemporaryLimitData.Side side) { }; } - private static FlowsLimitsHolder getLimitsHolder(Network network, IdentifiableType identifiableType, String elementId, TemporaryLimitData.Side side) { - switch (identifiableType) { + private static FlowsLimitsHolder getLimitsHolder(Network network, String elementId, TemporaryLimitData.Side side) { + Identifiable identifiable = NetworkUtils.getIdentifiableOrThrow(network, elementId); + switch (identifiable.getType()) { case LINE, TWO_WINDINGS_TRANSFORMER -> { - Branch branch = network.getBranch(elementId); - if (branch == null) { - throw new PowsyblException(String.format("Branch %s does not exist.", elementId)); - } - return getBranchAsFlowsLimitsHolder(branch, toBranchSide(side)); + return getBranchAsFlowsLimitsHolder((Branch) identifiable, toBranchSide(side)); } case DANGLING_LINE -> { - DanglingLine dl = network.getDanglingLine(elementId); - if (dl == null) { - throw new PowsyblException(String.format("Dangling line %s does not exist.", elementId)); - } if (side != TemporaryLimitData.Side.NONE) { throw new PowsyblException(String.format("Invalid value for dangling line side: %s, must be NONE", side)); } - return dl; + return (DanglingLine) identifiable; } case THREE_WINDINGS_TRANSFORMER -> { - ThreeWindingsTransformer transformer = network.getThreeWindingsTransformer(elementId); - if (transformer == null) { - throw new PowsyblException(String.format("Three windings transformer %s does not exist.", elementId)); - } + ThreeWindingsTransformer transformer = (ThreeWindingsTransformer) identifiable; return switch (side) { case ONE -> transformer.getLeg1(); case TWO -> transformer.getLeg2(); @@ -281,16 +275,28 @@ private static FlowsLimitsHolder getLimitsHolder(Network network, IdentifiableTy }; } default -> - throw new PowsyblException("Cannot create operational limits for element of type " + identifiableType); + throw new PowsyblException("Cannot create operational limits for element of type " + identifiable.getType()); } } - private static LoadingLimitsAdder getAdder(Network network, IdentifiableType identifiableType, String elementId, LimitType type, TemporaryLimitData.Side side) { - FlowsLimitsHolder limitsHolder = getLimitsHolder(network, identifiableType, elementId, side); + private static LoadingLimitsAdder getAdder(Network network, String elementId, LimitType type, + TemporaryLimitData.Side side, String groupId) { + FlowsLimitsHolder limitsHolder = getLimitsHolder(network, elementId, side); + OperationalLimitsGroup group; + if (groupId == null) { + String selectedGroupId = limitsHolder.getSelectedOperationalLimitsGroupId().orElse("DEFAULT"); + group = limitsHolder.getSelectedOperationalLimitsGroup() + .orElseGet(() -> limitsHolder.newOperationalLimitsGroup("DEFAULT")); + limitsHolder.setSelectedOperationalLimitsGroup(selectedGroupId); + } else { + group = limitsHolder.getOperationalLimitsGroup(groupId) + .orElseGet(() -> limitsHolder.newOperationalLimitsGroup(groupId)); + } + return switch (type) { - case CURRENT -> limitsHolder.newCurrentLimits(); - case ACTIVE_POWER -> limitsHolder.newActivePowerLimits(); - case APPARENT_POWER -> limitsHolder.newApparentPowerLimits(); + case CURRENT -> group.newCurrentLimits(); + case ACTIVE_POWER -> group.newActivePowerLimits(); + case APPARENT_POWER -> group.newApparentPowerLimits(); default -> throw new PowsyblException(String.format("Limit type %s does not exist.", type)); }; } diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/PhaseTapChangerDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/PhaseTapChangerDataframeAdder.java index 6b5ca2bbb5..dd0233e75d 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/PhaseTapChangerDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/PhaseTapChangerDataframeAdder.java @@ -18,9 +18,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class PhaseTapChangerDataframeAdder implements NetworkElementAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/RatioTapChangerDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/RatioTapChangerDataframeAdder.java index 89435df534..da8be7816b 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/RatioTapChangerDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/RatioTapChangerDataframeAdder.java @@ -18,9 +18,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class RatioTapChangerDataframeAdder implements NetworkElementAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/SeriesUtils.java b/java/src/main/java/com/powsybl/dataframe/network/adders/SeriesUtils.java index 5f1e882f14..dcdfa619a2 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/SeriesUtils.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/SeriesUtils.java @@ -18,7 +18,7 @@ import java.util.function.IntConsumer; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ public final class SeriesUtils { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/ShuntDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/ShuntDataframeAdder.java index 2f9898ecfc..ad4f918630 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/ShuntDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/ShuntDataframeAdder.java @@ -28,9 +28,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class ShuntDataframeAdder implements NetworkElementAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/SubstationDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/SubstationDataframeAdder.java index f079837915..9dabfcfa45 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/SubstationDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/SubstationDataframeAdder.java @@ -20,9 +20,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class SubstationDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/SvcDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/SvcDataframeAdder.java index 839c2c7416..93e2bb698c 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/SvcDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/SvcDataframeAdder.java @@ -26,9 +26,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class SvcDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/SwitchDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/SwitchDataframeAdder.java index 99a89be98f..6fbc213cce 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/SwitchDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/SwitchDataframeAdder.java @@ -22,9 +22,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class SwitchDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/ThreeWindingsTransformerDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/ThreeWindingsTransformerDataframeAdder.java index 95151aac21..ec08437021 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/ThreeWindingsTransformerDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/ThreeWindingsTransformerDataframeAdder.java @@ -15,7 +15,7 @@ import java.util.List; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class ThreeWindingsTransformerDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/ThreeWindingsTransformerSeries.java b/java/src/main/java/com/powsybl/dataframe/network/adders/ThreeWindingsTransformerSeries.java index 6ebbc571be..5b4da15459 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/ThreeWindingsTransformerSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/ThreeWindingsTransformerSeries.java @@ -21,7 +21,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class ThreeWindingsTransformerSeries extends IdentifiableSeries { private static final String COULD_NOT_CREATE_TRANSFORMER = "Could not create transformer "; diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/TieLineDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/TieLineDataframeAdder.java index ae30b69d83..c850b1d749 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/TieLineDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/TieLineDataframeAdder.java @@ -20,7 +20,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class TieLineDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/TwtDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/TwtDataframeAdder.java index 1a9a3ddd19..ba16a856fd 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/TwtDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/TwtDataframeAdder.java @@ -15,9 +15,9 @@ import java.util.List; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class TwtDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/VoltageLevelDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/VoltageLevelDataframeAdder.java index c76967c453..c1c21519f1 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/VoltageLevelDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/VoltageLevelDataframeAdder.java @@ -21,9 +21,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class VoltageLevelDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/adders/VscStationDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/adders/VscStationDataframeAdder.java index d3d50afad3..09c0e95039 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/adders/VscStationDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/adders/VscStationDataframeAdder.java @@ -27,9 +27,9 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ public class VscStationDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/AbstractSingleDataframeNetworkExtension.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/AbstractSingleDataframeNetworkExtension.java index 5473e03fd6..707ea5be26 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/AbstractSingleDataframeNetworkExtension.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/AbstractSingleDataframeNetworkExtension.java @@ -12,7 +12,7 @@ import java.util.*; /** - * @author Hugo Kulesza + * @author Hugo Kulesza {@literal } */ public abstract class AbstractSingleDataframeNetworkExtension implements NetworkExtensionDataframeProvider { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeAdder.java index 5b407da35f..79a1a86fd7 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeAdder.java @@ -23,14 +23,17 @@ import java.util.List; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public class ActivePowerControlDataframeAdder extends AbstractSimpleAdder { private static final List METADATA = List.of( SeriesMetadata.stringIndex("id"), SeriesMetadata.doubles("droop"), - SeriesMetadata.booleans("participate") + SeriesMetadata.booleans("participate"), + SeriesMetadata.doubles("participation_factor"), + SeriesMetadata.doubles("max_target_p"), + SeriesMetadata.doubles("min_target_p") ); @Override @@ -43,11 +46,17 @@ private static class ActivePowerControlSeries { private final StringSeries id; private final DoubleSeries droop; private final IntSeries participate; + private final DoubleSeries participationFactor; + private final DoubleSeries maxTargetP; + private final DoubleSeries minTargetP; ActivePowerControlSeries(UpdatingDataframe dataframe) { this.id = dataframe.getStrings("id"); this.droop = dataframe.getDoubles("droop"); this.participate = dataframe.getInts("participate"); + this.participationFactor = dataframe.getDoubles("participation_factor"); + this.maxTargetP = dataframe.getDoubles("max_target_p"); + this.minTargetP = dataframe.getDoubles("min_target_p"); } void create(Network network, int row) { @@ -57,8 +66,11 @@ void create(Network network, int row) { throw new PowsyblException("Invalid generator id : could not find " + generatorId); } var adder = g.newExtension(ActivePowerControlAdder.class); - SeriesUtils.applyIfPresent(droop, row, x -> adder.withDroop((float) x)); + SeriesUtils.applyIfPresent(droop, row, adder::withDroop); SeriesUtils.applyBooleanIfPresent(participate, row, adder::withParticipate); + SeriesUtils.applyIfPresent(participationFactor, row, adder::withParticipationFactor); + SeriesUtils.applyIfPresent(maxTargetP, row, adder::withMaxTargetP); + SeriesUtils.applyIfPresent(minTargetP, row, adder::withMinTargetP); adder.add(); } } diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeProvider.java index f64584e339..809c6763d1 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeProvider.java @@ -23,7 +23,7 @@ import java.util.stream.Stream; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class ActivePowerControlDataframeProvider extends AbstractSingleDataframeNetworkExtension { @@ -37,7 +37,12 @@ public String getExtensionName() { public ExtensionInformation getExtensionInformation() { return new ExtensionInformation(ActivePowerControl.NAME, "Provides information about the participation of generators to balancing", - "index : id (str), participate (bool), droop (float)"); + "index : id (str), " + + "participate (bool), " + + "droop (float), " + + "participation_factor (float), " + + "max_target_p (float), " + + "min_target_p (float)"); } private Stream itemsStream(Network network) { @@ -62,8 +67,11 @@ private ActivePowerControl getOrThrow(Network network, String id) { public NetworkDataframeMapper createMapper() { return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) .stringsIndex("id", ext -> ((Identifiable) ext.getExtendable()).getId()) - .doubles("droop", (apc, context) -> apc.getDroop(), (apc, droop, context) -> apc.setDroop((float) droop)) + .doubles("droop", (apc, context) -> apc.getDroop(), (apc, droop, context) -> apc.setDroop(droop)) .booleans("participate", ActivePowerControl::isParticipate, ActivePowerControl::setParticipate) + .doubles("participation_factor", (apc, context) -> apc.getParticipationFactor(), (apc, participationFactor, context) -> apc.setParticipationFactor(participationFactor)) + .doubles("max_target_p", (apc, context) -> apc.getMaxTargetP().orElse(Double.NaN), (apc, maxTargetP, context) -> apc.setMaxTargetP(maxTargetP)) + .doubles("min_target_p", (apc, context) -> apc.getMinTargetP().orElse(Double.NaN), (apc, minTargetP, context) -> apc.setMinTargetP(minTargetP)) .build(); } diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeAdder.java index 0680cbd496..3b94d8ddb8 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeAdder.java @@ -23,7 +23,7 @@ import java.util.List; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class BranchObservabilityDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeProvider.java index e398679e78..3dd90a946c 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeProvider.java @@ -22,7 +22,7 @@ import java.util.stream.Stream; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class BranchObservabilityDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/BusBarSectionPositionDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/BusBarSectionPositionDataframeAdder.java index 03227728d6..7f1bed4773 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/BusBarSectionPositionDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/BusBarSectionPositionDataframeAdder.java @@ -22,7 +22,7 @@ import java.util.List; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class BusBarSectionPositionDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/BusBarSectionPositionDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/BusBarSectionPositionDataframeProvider.java index ddd4cc8be1..759f494760 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/BusBarSectionPositionDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/BusBarSectionPositionDataframeProvider.java @@ -22,7 +22,7 @@ import java.util.stream.Stream; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class BusBarSectionPositionDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/CgmesMetadataModelDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/CgmesMetadataModelDataframeAdder.java new file mode 100644 index 0000000000..ccc7038543 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/CgmesMetadataModelDataframeAdder.java @@ -0,0 +1,104 @@ +package com.powsybl.dataframe.network.extensions; + +import com.powsybl.cgmes.extensions.CgmesMetadataModelsAdder; +import com.powsybl.cgmes.model.CgmesSubset; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.network.adders.AbstractSimpleAdder; +import com.powsybl.dataframe.network.adders.SeriesUtils; +import com.powsybl.dataframe.update.IntSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.iidm.network.*; + +import java.util.*; + +/** + * @author Naledi El Cheikh {@literal } + */ + +public class CgmesMetadataModelDataframeAdder extends AbstractSimpleAdder { + private static final List METADATA = List.of( + SeriesMetadata.stringIndex("id"), + SeriesMetadata.strings("cgmes_subset"), + SeriesMetadata.strings("description"), + SeriesMetadata.ints("version"), + SeriesMetadata.strings("modeling_authority_set"), + SeriesMetadata.strings("profiles"), + SeriesMetadata.strings("dependent_on"), + SeriesMetadata.strings("supersedes") + ); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + private static final class CgmesMetadataSeries { + private final StringSeries id; + private final StringSeries subset; + private final StringSeries description; + private final IntSeries version; + private final StringSeries modelingAuthoritySet; + private final StringSeries profiles; + private final StringSeries dependentOn; + private final StringSeries supersedes; + + private CgmesMetadataSeries(UpdatingDataframe dataframe) { + this.id = dataframe.getStrings("id"); + this.subset = dataframe.getStrings("cgmes_subset"); + this.description = dataframe.getStrings("description"); + this.version = dataframe.getInts("version"); + this.modelingAuthoritySet = dataframe.getStrings("modeling_authority_set"); + this.profiles = dataframe.getStrings("profiles"); + this.dependentOn = dataframe.getStrings("dependent_on"); + this.supersedes = dataframe.getStrings("supersedes"); + } + + void create(int row, CgmesMetadataModelsAdder adder) { + CgmesMetadataModelsAdder.ModelAdder modelAdder = adder.newModel(); + SeriesUtils.applyIfPresent(id, row, modelAdder::setId); + SeriesUtils.applyIfPresent(subset, row, subset -> modelAdder.setSubset(CgmesSubset.valueOf(subset))); + SeriesUtils.applyIfPresent(description, row, modelAdder::setDescription); + SeriesUtils.applyIfPresent(version, row, modelAdder::setVersion); + SeriesUtils.applyIfPresent(modelingAuthoritySet, row, modelAdder::setModelingAuthoritySet); + SeriesUtils.applyIfPresent(profiles, row, profile -> parseProfiles(profile, modelAdder)); + SeriesUtils.applyIfPresent(dependentOn, row, dependentOn -> parseDependentOn(dependentOn, modelAdder)); + SeriesUtils.applyIfPresent(supersedes, row, supersedes -> parseSupersedes(supersedes, modelAdder)); + modelAdder.add(); + + } + + private void parseProfiles(String concatenatedProfiles, CgmesMetadataModelsAdder.ModelAdder modelAdder) { + List profilesList = new ArrayList<>(Arrays.asList(concatenatedProfiles.split(","))); + for (String profile : profilesList) { + modelAdder.addProfile(profile); + } + } + + private void parseDependentOn(String concatenatedDependentOn, CgmesMetadataModelsAdder.ModelAdder modelAdder) { + List dependentOnList = new ArrayList<>(Arrays.asList(concatenatedDependentOn.split(","))); + for (String dependentOn : dependentOnList) { + modelAdder.addDependentOn(dependentOn); + } + } + + private void parseSupersedes(String concatenatedSupersedes, CgmesMetadataModelsAdder.ModelAdder modelAdder) { + List supersedesList = new ArrayList<>(Arrays.asList(concatenatedSupersedes.split(","))); + for (String supersedes : supersedesList) { + modelAdder.addSupersedes(supersedes); + } + } + + } + + @Override + public void addElements(Network network, UpdatingDataframe dataframe) { + CgmesMetadataModelsAdder adder = network.newExtension(CgmesMetadataModelsAdder.class); + CgmesMetadataSeries series = new CgmesMetadataSeries(dataframe); + for (int row = 0; row < dataframe.getRowCount(); row++) { + series.create(row, adder); + } + adder.add(); + } + +} diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/CgmesMetadataModelDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/CgmesMetadataModelDataframeProvider.java new file mode 100644 index 0000000000..f40a9a8883 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/CgmesMetadataModelDataframeProvider.java @@ -0,0 +1,64 @@ +package com.powsybl.dataframe.network.extensions; + +import com.google.auto.service.AutoService; + +import com.powsybl.cgmes.model.CgmesMetadataModel; +import com.powsybl.dataframe.network.ExtensionInformation; +import com.powsybl.dataframe.network.NetworkDataframeMapper; +import com.powsybl.dataframe.network.NetworkDataframeMapperBuilder; +import com.powsybl.dataframe.network.adders.NetworkElementAdder; +import com.powsybl.iidm.network.Network; +import com.powsybl.cgmes.extensions.CgmesMetadataModels; + +import java.util.List; +import java.util.stream.Stream; + +/** + * @author Naledi El Cheikh {@literal } + */ + +@AutoService(NetworkExtensionDataframeProvider.class) +public class CgmesMetadataModelDataframeProvider extends AbstractSingleDataframeNetworkExtension { + @Override + public String getExtensionName() { + return CgmesMetadataModels.NAME; + } + + @Override + public ExtensionInformation getExtensionInformation() { + return new ExtensionInformation(CgmesMetadataModels.NAME, "Provides information about CGMES metadata models", + "index : id (str), cgmes_subset (str), description (str), " + + "version (int), modeling_authority_set (str), profiles (str), dependent_on (str), supersedes (str) "); + } + + private Stream itemsStream(Network network) { + if (network.getExtension(CgmesMetadataModels.class) == null) { + return Stream.empty(); + } + return network.getExtension(CgmesMetadataModels.class).getModels().stream(); + } + + @Override + public NetworkDataframeMapper createMapper() { + return NetworkDataframeMapperBuilder.ofStream(this::itemsStream) + .stringsIndex("id", CgmesMetadataModel::getId) + .strings("cgmes_subset", cgmesMetadataModel -> String.valueOf(cgmesMetadataModel.getSubset())) + .strings("description", CgmesMetadataModel::getDescription) + .ints("version", CgmesMetadataModel::getVersion) + .strings("modeling_authority_set", CgmesMetadataModel::getModelingAuthoritySet) + .strings("profiles", cgmesMetadataModel -> String.join(",", cgmesMetadataModel.getProfiles())) + .strings("dependent_on", cgmesMetadataModel -> String.join(",", cgmesMetadataModel.getDependentOn())) + .strings("supersedes", cgmesMetadataModel -> String.join(",", cgmesMetadataModel.getSupersedes())) + .build(); + } + + @Override + public void removeExtensions(Network network, List ids) { + throw new UnsupportedOperationException("Cannot remove CGMES metadata models"); + } + + @Override + public NetworkElementAdder createAdder() { + return new CgmesMetadataModelDataframeAdder(); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionDataframeAdder.java index 2e448f827c..b21a45803f 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionDataframeAdder.java @@ -27,7 +27,7 @@ import java.util.Map; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class ConnectablePositionDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionDataframeProvider.java index 4f9f1fc2f1..c1b1f44101 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionDataframeProvider.java @@ -25,7 +25,7 @@ import java.util.Objects; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class ConnectablePositionDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionFeederData.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionFeederData.java index 9176bc2271..cb05a96395 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionFeederData.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/ConnectablePositionFeederData.java @@ -11,7 +11,7 @@ import com.powsybl.python.network.SideEnum; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class ConnectablePositionFeederData { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeAdder.java index d3ac9cd641..81f848b4ae 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeAdder.java @@ -22,7 +22,7 @@ import java.util.List; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class CoordinatedReactiveControlDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeProvider.java index da78bc608d..8f7375096a 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeProvider.java @@ -22,7 +22,7 @@ import java.util.stream.Stream; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class CoordinatedReactiveControlDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/EntsoeAreaDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/EntsoeAreaDataframeAdder.java index 01f0846a38..e41d50895a 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/EntsoeAreaDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/EntsoeAreaDataframeAdder.java @@ -22,7 +22,7 @@ import java.util.List; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public class EntsoeAreaDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/EntsoeAreaDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/EntsoeAreaDataframeProvider.java index 9ff991061b..f5aa0760af 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/EntsoeAreaDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/EntsoeAreaDataframeProvider.java @@ -23,7 +23,7 @@ import java.util.stream.Stream; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class EntsoeAreaDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/ExtensionDataframeKey.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/ExtensionDataframeKey.java index fae160e811..c70a956687 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/ExtensionDataframeKey.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/ExtensionDataframeKey.java @@ -10,7 +10,7 @@ import java.util.Objects; /** - * @author Hugo Kulesza + * @author Hugo Kulesza {@literal } */ public class ExtensionDataframeKey { private final String extensionName; diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorEntsoeCategoryDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorEntsoeCategoryDataframeAdder.java index 18b6424c32..49613d8874 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorEntsoeCategoryDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorEntsoeCategoryDataframeAdder.java @@ -22,7 +22,7 @@ import java.util.List; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public class GeneratorEntsoeCategoryDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorEntsoeCategoryDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorEntsoeCategoryDataframeProvider.java index 39503e9e06..118ac5ab09 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorEntsoeCategoryDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorEntsoeCategoryDataframeProvider.java @@ -22,7 +22,7 @@ import java.util.stream.Stream; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class GeneratorEntsoeCategoryDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeAdder.java index f74c935c8a..310358597b 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeAdder.java @@ -22,7 +22,7 @@ import java.util.List; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class GeneratorShortCircuitDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeProvider.java index b2551f2645..d11c71a480 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeProvider.java @@ -22,7 +22,7 @@ import java.util.stream.Stream; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class GeneratorShortCircuitDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeAdder.java index 27966d991d..4aa3d00612 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeAdder.java @@ -25,7 +25,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyBooleanIfPresent; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public class HvdcAngleDroopActivePowerControlDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeProvider.java index 2550bcd67c..ea8282e23b 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeProvider.java @@ -22,7 +22,7 @@ import java.util.stream.Stream; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class HvdcAngleDroopActivePowerControlDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeAdder.java index ad7a17b720..8ba2601c9e 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeAdder.java @@ -22,7 +22,7 @@ import java.util.List; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public class HvdcOperatorActivePowerRangeDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeProvider.java index 5fb854c487..ae28de7b88 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeProvider.java @@ -22,7 +22,7 @@ import java.util.stream.Stream; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class HvdcOperatorActivePowerRangeDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeAdder.java index 92215ca0d4..ebf53f2303 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeAdder.java @@ -22,7 +22,7 @@ import java.util.List; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class IdentifiableShortCircuitDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeProvider.java index 50b61b5be6..ca0b101256 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeProvider.java @@ -22,7 +22,7 @@ import java.util.stream.Stream; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class IdentifiableShortCircuitDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeAdder.java index 5702c98319..6cd49375d0 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeAdder.java @@ -24,7 +24,7 @@ import java.util.List; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class InjectionObservabilityDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeProvider.java index dc05f98565..6d3d8bb7dc 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeProvider.java @@ -23,7 +23,7 @@ import java.util.stream.Stream; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class InjectionObservabilityDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/LinePositionDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/LinePositionDataframeAdder.java index 322287fcc9..e74ea2c478 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/LinePositionDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/LinePositionDataframeAdder.java @@ -22,7 +22,7 @@ import java.util.*; /** - * @author Geoffroy Jamgotchian + * @author Geoffroy Jamgotchian {@literal } */ public class LinePositionDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/LinePositionDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/LinePositionDataframeProvider.java index 0b47edd04e..c486c89040 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/LinePositionDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/LinePositionDataframeProvider.java @@ -27,7 +27,7 @@ import java.util.stream.Stream; /** - * @author Geoffroy Jamgotchian + * @author Geoffroy Jamgotchian {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class LinePositionDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeAdder.java index 7aa15dc78f..cdd3acee38 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeAdder.java @@ -24,7 +24,7 @@ import static com.powsybl.dataframe.network.extensions.LoadDetailDataframeProvider.*; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class LoadDetailDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeProvider.java index 7e2e17c86a..ca77dad01e 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeProvider.java @@ -22,7 +22,7 @@ import java.util.stream.Stream; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class LoadDetailDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/MeasurementsDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/MeasurementsDataframeAdder.java index d65cd92d8e..7c5743970b 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/MeasurementsDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/MeasurementsDataframeAdder.java @@ -26,7 +26,7 @@ import static com.powsybl.dataframe.network.extensions.MeasurementsDataframeProvider.*; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class MeasurementsDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/MeasurementsDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/MeasurementsDataframeProvider.java index 83375ad85d..b2b5db784f 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/MeasurementsDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/MeasurementsDataframeProvider.java @@ -24,7 +24,7 @@ import java.util.stream.Stream; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class MeasurementsDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensionDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensionDataframeProvider.java index 64aa873585..d3800f49a1 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensionDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensionDataframeProvider.java @@ -21,7 +21,7 @@ /** * SPI for defining dataframes of network extensions. * - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public interface NetworkExtensionDataframeProvider { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensions.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensions.java index 9a60bcdc2e..6ab52c3a11 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensions.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensions.java @@ -26,7 +26,7 @@ import java.util.stream.Collectors; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public final class NetworkExtensions { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/ReferencePrioritiesDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/ReferencePrioritiesDataframeAdder.java new file mode 100644 index 0000000000..4247aece83 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/ReferencePrioritiesDataframeAdder.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.network.extensions; + +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.network.adders.AbstractSimpleAdder; +import com.powsybl.dataframe.network.adders.NetworkUtils; +import com.powsybl.dataframe.network.adders.SeriesUtils; +import com.powsybl.dataframe.update.IntSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.iidm.network.Injection; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.extensions.ReferencePriority; + +import java.util.Collections; +import java.util.List; + +/** + * @author Damien Jeandemange {@literal } + */ +public class ReferencePrioritiesDataframeAdder extends AbstractSimpleAdder { + + private static final List METADATA = List.of( + SeriesMetadata.stringIndex("id"), + SeriesMetadata.ints("priority") + ); + + @Override + public List> getMetadata() { + return Collections.singletonList(METADATA); + } + + private static class ReferencePrioritySeries { + + private final StringSeries id; + private final IntSeries priority; + + ReferencePrioritySeries(UpdatingDataframe dataframe) { + this.id = dataframe.getStrings("id"); + this.priority = SeriesUtils.getRequiredInts(dataframe, "priority"); + } + + void create(Network network, int row) { + String id = this.id.get(row); + Injection injection = NetworkUtils.getGenOrLoadOrBusbarSectionOrThrow(network, id); + SeriesUtils.applyIfPresent(this.priority, row, priority -> ReferencePriority.set(injection, priority)); + } + } + + @Override + public void addElements(Network network, UpdatingDataframe dataframe) { + ReferencePrioritySeries series = new ReferencePrioritySeries(dataframe); + for (int row = 0; row < dataframe.getRowCount(); row++) { + series.create(network, row); + } + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/ReferencePrioritiesDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/ReferencePrioritiesDataframeProvider.java new file mode 100644 index 0000000000..5745da8f0d --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/ReferencePrioritiesDataframeProvider.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.network.extensions; + +import com.google.auto.service.AutoService; +import com.powsybl.commons.PowsyblException; +import com.powsybl.dataframe.network.ExtensionInformation; +import com.powsybl.dataframe.network.NetworkDataframeMapper; +import com.powsybl.dataframe.network.NetworkDataframeMapperBuilder; +import com.powsybl.dataframe.network.adders.NetworkElementAdder; +import com.powsybl.dataframe.network.adders.NetworkUtils; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.ReferencePriorities; +import com.powsybl.iidm.network.extensions.ReferencePriority; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * @author Damien Jeandemange {@literal } + */ +@AutoService(NetworkExtensionDataframeProvider.class) +public class ReferencePrioritiesDataframeProvider extends AbstractSingleDataframeNetworkExtension { + + @Override + public String getExtensionName() { + return ReferencePriorities.NAME; + } + + @Override + public ExtensionInformation getExtensionInformation() { + return new ExtensionInformation(ReferencePriorities.NAME, + "Defines the angle reference generator, busbar section or load of a power flow calculation, " + + "i.e. which bus will be used with a zero-voltage angle.", + "index : id (str), " + + "priority (int)"); + } + + private Stream itemsStream(Network network) { + return Stream.of(network.getGenerators(), network.getBusbarSections(), network.getLoads()) + .flatMap(i -> StreamSupport.stream(i.spliterator(), false)) + .map(g -> (ReferencePriorities) g.getExtension(ReferencePriorities.class)) + .filter(Objects::nonNull); + } + + private ReferencePriorities getOrThrow(Network network, String id) { + Identifiable identifiable = NetworkUtils.getGenOrLoadOrBusbarSectionOrThrow(network, id); + ReferencePriorities referencePriorities = identifiable.getExtension(ReferencePriorities.class); + if (referencePriorities == null) { + throw new PowsyblException("Injection '" + id + "' has no ReferencePriorities extension"); + } + return referencePriorities; + } + + @Override + public NetworkDataframeMapper createMapper() { + return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) + .stringsIndex("id", ext -> ((Identifiable) ext.getExtendable()).getId()) + .ints("priority", + rp -> ((List) rp.getReferencePriorities()).get(0).getPriority(), + (rp, priority) -> ReferencePriority.set((Injection) (((List) rp.getReferencePriorities()).get(0).getTerminal().getConnectable()), priority)) + .build(); + } + + @Override + public void removeExtensions(Network network, List ids) { + ids.stream().filter(Objects::nonNull) + .map(network::getIdentifiable) + .filter(Objects::nonNull) + .forEach(i -> i.removeExtension(ReferencePriorities.class)); + } + + @Override + public NetworkElementAdder createAdder() { + return new ReferencePrioritiesDataframeAdder(); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeAdder.java index 87a7875503..c01a556348 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeAdder.java @@ -20,7 +20,7 @@ import java.util.List; /** - * @author Hugo Kulesza + * @author Hugo Kulesza {@literal } */ public class SecondaryVoltageControlDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeProvider.java index 608e88343c..5ae9170b52 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeProvider.java @@ -26,7 +26,7 @@ import static java.util.stream.Collectors.toList; /** - * @author Hugo Kulesza + * @author Hugo Kulesza {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class SecondaryVoltageControlDataframeProvider implements NetworkExtensionDataframeProvider { @@ -60,9 +60,7 @@ private Stream unitsStream(Network network) { .forEach(zone -> { units.addAll(zone.getControlUnits() .stream() - .map(unit -> { - return new ControlUnitWithZone(unit, zone.getName()); - }) + .map(unit -> new ControlUnitWithZone(unit, zone.getName())) .collect(toList()) ); }); @@ -100,23 +98,26 @@ public ControlUnitWithZone getItem(Network network, UpdatingDataframe updatingDa throw new PowsyblException("Network " + network.getId() + " has no SecondaryVoltageControl extension."); } String id = updatingDataframe.getStringValue("unit_id", lineNumber).orElse(null); + ControlZone zone = ext.getControlZones().stream() - .filter(controlZone -> { - return controlZone.getControlUnits().stream() - .anyMatch(controlUnit -> { - return controlUnit.getId().equals(id); - }); - }) + .filter(controlZone -> + controlZone.getControlUnits().stream() + .anyMatch(controlUnit -> controlUnit.getId().equals(id)) + ) .findAny() .orElse(null); if (zone == null) { throw new PowsyblException("No secondary voltage control zone containing control unit " + id + " found."); } - return new ControlUnitWithZone(zone.getControlUnits().stream().filter(controlUnit -> { - return controlUnit.getId().equals(id); - }).findAny().get(), - zone.getName()); + ControlUnit unit = zone.getControlUnits() + .stream() + .filter(controlUnit -> controlUnit.getId().equals(id)) + .findAny().orElse(null); + if (unit == null) { + throw new PowsyblException("No control unit " + id + " found in secondary voltage control zone " + zone.getName() + "."); + } + return new ControlUnitWithZone(unit, zone.getName()); } } diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/SlackTerminalDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/SlackTerminalDataframeAdder.java index acfb84eb56..371d548a1e 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/SlackTerminalDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/SlackTerminalDataframeAdder.java @@ -19,7 +19,7 @@ import java.util.List; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class SlackTerminalDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/SlackTerminalDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/SlackTerminalDataframeProvider.java index cf17d10636..7bb1fbde81 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/SlackTerminalDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/SlackTerminalDataframeProvider.java @@ -20,7 +20,7 @@ import java.util.stream.Stream; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class SlackTerminalDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeAdder.java index b8cd4aa56a..ed22f16903 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeAdder.java @@ -23,7 +23,7 @@ import java.util.List; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class StandByAutomatonDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeProvider.java index 176de812e9..dac04fb625 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeProvider.java @@ -22,7 +22,7 @@ import java.util.stream.Stream; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class StandByAutomatonDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/SubstationPositionDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/SubstationPositionDataframeAdder.java index c53588a665..266232f0dd 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/SubstationPositionDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/SubstationPositionDataframeAdder.java @@ -22,7 +22,7 @@ import java.util.List; /** - * @author Geoffroy Jamgotchian + * @author Geoffroy Jamgotchian {@literal } */ public class SubstationPositionDataframeAdder extends AbstractSimpleAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/SubstationPositionDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/SubstationPositionDataframeProvider.java index b1c53226ac..556695c37f 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/SubstationPositionDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/SubstationPositionDataframeProvider.java @@ -22,7 +22,7 @@ import java.util.stream.Stream; /** - * @author Geoffroy Jamgotchian + * @author Geoffroy Jamgotchian {@literal } */ @AutoService(NetworkExtensionDataframeProvider.class) public class SubstationPositionDataframeProvider extends AbstractSingleDataframeNetworkExtension { diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/ConnectVoltageLevelOnLine.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/ConnectVoltageLevelOnLine.java index ef603983f6..c07767c680 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/ConnectVoltageLevelOnLine.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/ConnectVoltageLevelOnLine.java @@ -21,7 +21,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class ConnectVoltageLevelOnLine implements NetworkModification { diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/CouplingDeviceCreation.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/CouplingDeviceCreation.java index e7a3b867d0..47ccdf0727 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/CouplingDeviceCreation.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/CouplingDeviceCreation.java @@ -18,7 +18,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class CouplingDeviceCreation implements NetworkModification { diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateFeederBay.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateFeederBay.java index 95d0b8cbab..91ace0f5d6 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateFeederBay.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateFeederBay.java @@ -21,7 +21,7 @@ import static com.powsybl.python.commons.Util.convert; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class CreateFeederBay implements NetworkModification { diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateLineFeeder.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateLineFeeder.java index fb33675a52..5578fd7920 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateLineFeeder.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateLineFeeder.java @@ -20,7 +20,7 @@ import java.util.Optional; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class CreateLineFeeder implements NetworkModification { diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateLineOnLine.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateLineOnLine.java index eea2f2dc25..3295f0dd22 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateLineOnLine.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateLineOnLine.java @@ -20,7 +20,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class CreateLineOnLine implements NetworkModification { diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateTwoWindingsTransformer.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateTwoWindingsTransformer.java index df3ce7ff0b..893e9b493c 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateTwoWindingsTransformer.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/CreateTwoWindingsTransformer.java @@ -20,7 +20,7 @@ import java.util.Optional; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class CreateTwoWindingsTransformer implements NetworkModification { @Override diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/DataframeNetworkModificationType.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/DataframeNetworkModificationType.java index a9b7c2aaa0..4835bd1a8f 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/DataframeNetworkModificationType.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/DataframeNetworkModificationType.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe.network.modifications; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public enum DataframeNetworkModificationType { VOLTAGE_LEVEL_TOPOLOGY_CREATION, diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/NetworkModification.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/NetworkModification.java index fab42fc288..8d2715f3a2 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/NetworkModification.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/NetworkModification.java @@ -17,7 +17,7 @@ import java.util.List; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public interface NetworkModification { diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/NetworkModifications.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/NetworkModifications.java index 983ebc9ef8..11357bff20 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/NetworkModifications.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/NetworkModifications.java @@ -18,7 +18,7 @@ import static com.powsybl.dataframe.network.modifications.DataframeNetworkModificationType.*; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public final class NetworkModifications { diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/ReplaceTeePointByVoltageLevelOnLine.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/ReplaceTeePointByVoltageLevelOnLine.java index 4d31b85b03..0fefbd14e3 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/ReplaceTeePointByVoltageLevelOnLine.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/ReplaceTeePointByVoltageLevelOnLine.java @@ -18,7 +18,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class ReplaceTeePointByVoltageLevelOnLine implements NetworkModification { diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/RevertConnectVoltageLevelOnLine.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/RevertConnectVoltageLevelOnLine.java index 8a6f138c38..76153fdc04 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/RevertConnectVoltageLevelOnLine.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/RevertConnectVoltageLevelOnLine.java @@ -19,7 +19,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class RevertConnectVoltageLevelOnLine implements NetworkModification { diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/RevertCreateLineOnLine.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/RevertCreateLineOnLine.java index d1b4cc72f5..7ba58eea8a 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/RevertCreateLineOnLine.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/RevertCreateLineOnLine.java @@ -19,7 +19,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class RevertCreateLineOnLine implements NetworkModification { diff --git a/java/src/main/java/com/powsybl/dataframe/network/modifications/VoltageLevelTopologyCreation.java b/java/src/main/java/com/powsybl/dataframe/network/modifications/VoltageLevelTopologyCreation.java index af934a302a..5df2c2b272 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/modifications/VoltageLevelTopologyCreation.java +++ b/java/src/main/java/com/powsybl/dataframe/network/modifications/VoltageLevelTopologyCreation.java @@ -22,7 +22,7 @@ import static com.powsybl.dataframe.network.adders.SeriesUtils.applyIfPresent; /** - * @author Coline Piloquet + * @author Coline Piloquet {@literal } */ public class VoltageLevelTopologyCreation implements NetworkModification { diff --git a/java/src/main/java/com/powsybl/dataframe/network/package-info.java b/java/src/main/java/com/powsybl/dataframe/network/package-info.java index b18ff3b1e9..015ca487fd 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/package-info.java +++ b/java/src/main/java/com/powsybl/dataframe/network/package-info.java @@ -8,6 +8,6 @@ /** * Mapping of {@link com.powsybl.iidm.network.Network} elements to and from dataframes. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ package com.powsybl.dataframe.network; diff --git a/java/src/main/java/com/powsybl/dataframe/package-info.java b/java/src/main/java/com/powsybl/dataframe/package-info.java index 9d1ced92be..806f0e0c5d 100644 --- a/java/src/main/java/com/powsybl/dataframe/package-info.java +++ b/java/src/main/java/com/powsybl/dataframe/package-info.java @@ -15,6 +15,6 @@ * and {@link com.powsybl.dataframe.IndexedSeries} and its primitive variants allow to provide * some input series to update the data. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ package com.powsybl.dataframe; diff --git a/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/FaultDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/FaultDataframeAdder.java index 496a98a51d..dc26907a87 100644 --- a/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/FaultDataframeAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/FaultDataframeAdder.java @@ -22,7 +22,7 @@ import java.util.List; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public class FaultDataframeAdder implements ShortCircuitContextFaultAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/ShortCircuitContextFaultAdder.java b/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/ShortCircuitContextFaultAdder.java index 921b4b5d26..7aceefe07b 100644 --- a/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/ShortCircuitContextFaultAdder.java +++ b/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/ShortCircuitContextFaultAdder.java @@ -14,7 +14,7 @@ import java.util.List; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public interface ShortCircuitContextFaultAdder { diff --git a/java/src/main/java/com/powsybl/dataframe/update/DefaultUpdatingDataframe.java b/java/src/main/java/com/powsybl/dataframe/update/DefaultUpdatingDataframe.java index da2d3e0f51..16fa8bafb1 100644 --- a/java/src/main/java/com/powsybl/dataframe/update/DefaultUpdatingDataframe.java +++ b/java/src/main/java/com/powsybl/dataframe/update/DefaultUpdatingDataframe.java @@ -16,7 +16,7 @@ * Default implementation for the dataframe, the behaviour will rely on the provided series implementations. * * @author Etienne Lesot {@literal } - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class DefaultUpdatingDataframe implements UpdatingDataframe { private final int rowCount; diff --git a/java/src/main/java/com/powsybl/dataframe/update/DoubleSeries.java b/java/src/main/java/com/powsybl/dataframe/update/DoubleSeries.java index bac815974c..a906825d27 100644 --- a/java/src/main/java/com/powsybl/dataframe/update/DoubleSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/update/DoubleSeries.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe.update; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public interface DoubleSeries { double get(int index); diff --git a/java/src/main/java/com/powsybl/dataframe/update/IntSeries.java b/java/src/main/java/com/powsybl/dataframe/update/IntSeries.java index 57b3456201..b8aaf553c7 100644 --- a/java/src/main/java/com/powsybl/dataframe/update/IntSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/update/IntSeries.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe.update; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public interface IntSeries { int get(int index); diff --git a/java/src/main/java/com/powsybl/dataframe/update/StringSeries.java b/java/src/main/java/com/powsybl/dataframe/update/StringSeries.java index 520d4794d1..36437433dd 100644 --- a/java/src/main/java/com/powsybl/dataframe/update/StringSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/update/StringSeries.java @@ -8,7 +8,7 @@ package com.powsybl.dataframe.update; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public interface StringSeries { String get(int index); diff --git a/java/src/main/java/com/powsybl/python/commons/CommonObjects.java b/java/src/main/java/com/powsybl/python/commons/CommonObjects.java index 1d5a784342..91a55ba68f 100644 --- a/java/src/main/java/com/powsybl/python/commons/CommonObjects.java +++ b/java/src/main/java/com/powsybl/python/commons/CommonObjects.java @@ -13,7 +13,7 @@ /** * Manages common runtime objects, typically library-wide singletons. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public final class CommonObjects { diff --git a/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java b/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java index 5b9b0423ef..78d8d9423b 100644 --- a/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java +++ b/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java @@ -597,11 +597,13 @@ public enum ElementType { THREE_WINDINGS_TRANSFORMER, GENERATOR, LOAD, + GROUND, BATTERY, SHUNT_COMPENSATOR, NON_LINEAR_SHUNT_COMPENSATOR_SECTION, LINEAR_SHUNT_COMPENSATOR_SECTION, DANGLING_LINE, + DANGLING_LINE_GENERATION, TIE_LINE, LCC_CONVERTER_STATION, VSC_CONVERTER_STATION, @@ -617,13 +619,18 @@ public enum ElementType { PHASE_TAP_CHANGER, REACTIVE_CAPABILITY_CURVE_POINT, OPERATIONAL_LIMITS, + SELECTED_OPERATIONAL_LIMITS, MINMAX_REACTIVE_LIMITS, ALIAS, IDENTIFIABLE, INJECTION, BRANCH, TERMINAL, - SUB_NETWORK; + SUB_NETWORK, + AREA, + AREA_VOLTAGE_LEVELS, + AREA_BOUNDARIES, + INTERNAL_CONNECTION; @CEnumValue public native int getCValue(); @@ -1046,10 +1053,10 @@ public interface SldParametersPointer extends PointerBase { void setDiagonalLabel(boolean diagonalLabel); @CField("nodes_infos") - boolean isAddNodesInfos(); + boolean isBusesLegendAdded(); @CField("nodes_infos") - void setAddNodesInfos(boolean addNodeInfos); + void setBusesLegendAdded(boolean addNodeInfos); @CField("tooltip_enabled") void setTooltipEnabled(boolean tooltipEnabled); @@ -1178,14 +1185,32 @@ public interface NadParametersPointer extends PointerBase { @CEnum("DynamicMappingType") public enum DynamicMappingType { - ALPHA_BETA_LOAD, - ONE_TRANSFORMER_LOAD, - GENERATOR_SYNCHRONOUS_THREE_WINDINGS, - GENERATOR_SYNCHRONOUS_THREE_WINDINGS_PROPORTIONAL_REGULATIONS, - GENERATOR_SYNCHRONOUS_FOUR_WINDINGS, - GENERATOR_SYNCHRONOUS_FOUR_WINDINGS_PROPORTIONAL_REGULATIONS, - CURRENT_LIMIT_AUTOMATON, - GENERATOR_SYNCHRONOUS; + BASE_LOAD, + LOAD_ONE_TRANSFORMER, + LOAD_ONE_TRANSFORMER_TAP_CHANGER, + LOAD_TWO_TRANSFORMERS, + LOAD_TWO_TRANSFORMERS_TAP_CHANGERS, + BASE_GENERATOR, + SYNCHRONIZED_GENERATOR, + SYNCHRONOUS_GENERATOR, + WECC, + GRID_FORMING_CONVERTER, + SIGNAL_N_GENERATOR, + HVDC_P, + HVDC_VSC, + BASE_TRANSFORMER, + BASE_STATIC_VAR_COMPENSATOR, + BASE_LINE, + BASE_BUS, + INFINITE_BUS, + OVERLOAD_MANAGEMENT_SYSTEM, + TWO_LEVELS_OVERLOAD_MANAGEMENT_SYSTEM, + UNDER_VOLTAGE, + PHASE_SHIFTER_I, + PHASE_SHIFTER_P, + PHASE_SHIFTER_BLOCKING_I, + TAP_CHANGER, + TAP_CHANGER_BLOCKING; @CEnumValue public native int getCValue(); @@ -1194,6 +1219,19 @@ public enum DynamicMappingType { public static native DynamicMappingType fromCValue(int value); } + @CEnum("EventMappingType") + public enum EventMappingType { + DISCONNECT, + NODE_FAULT, + ACTIVE_POWER_VARIATION; + + @CEnumValue + public native int getCValue(); + + @CEnumLookup + public static native EventMappingType fromCValue(int value); + } + @CEnum("ThreeSide") public enum ThreeSideType { UNDEFINED, diff --git a/java/src/main/java/com/powsybl/python/commons/PyPowsyblConfiguration.java b/java/src/main/java/com/powsybl/python/commons/PyPowsyblConfiguration.java index d7c1b1a6ee..a3117a8973 100644 --- a/java/src/main/java/com/powsybl/python/commons/PyPowsyblConfiguration.java +++ b/java/src/main/java/com/powsybl/python/commons/PyPowsyblConfiguration.java @@ -3,7 +3,7 @@ /** * Library level configurations. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public final class PyPowsyblConfiguration { diff --git a/java/src/main/java/com/powsybl/python/commons/Util.java b/java/src/main/java/com/powsybl/python/commons/Util.java index 28e97d1b17..8eff366a41 100644 --- a/java/src/main/java/com/powsybl/python/commons/Util.java +++ b/java/src/main/java/com/powsybl/python/commons/Util.java @@ -7,7 +7,6 @@ */ package com.powsybl.python.commons; -import com.powsybl.commons.PowsyblException; import com.powsybl.commons.datasource.CompressionFormat; import com.powsybl.contingency.ContingencyContextType; import com.powsybl.dataframe.DataframeElementType; @@ -53,7 +52,7 @@ import static com.powsybl.python.commons.PyPowsyblApiHeader.allocArrayPointer; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public final class Util { @@ -191,9 +190,11 @@ public static PyPowsyblApiHeader.ElementType convert(DataframeElementType type) case THREE_WINDINGS_TRANSFORMER -> PyPowsyblApiHeader.ElementType.THREE_WINDINGS_TRANSFORMER; case GENERATOR -> PyPowsyblApiHeader.ElementType.GENERATOR; case LOAD -> PyPowsyblApiHeader.ElementType.LOAD; + case GROUND -> PyPowsyblApiHeader.ElementType.GROUND; case BATTERY -> PyPowsyblApiHeader.ElementType.BATTERY; case SHUNT_COMPENSATOR -> PyPowsyblApiHeader.ElementType.SHUNT_COMPENSATOR; case DANGLING_LINE -> PyPowsyblApiHeader.ElementType.DANGLING_LINE; + case DANGLING_LINE_GENERATION -> PyPowsyblApiHeader.ElementType.DANGLING_LINE_GENERATION; case TIE_LINE -> PyPowsyblApiHeader.ElementType.TIE_LINE; case LCC_CONVERTER_STATION -> PyPowsyblApiHeader.ElementType.LCC_CONVERTER_STATION; case VSC_CONVERTER_STATION -> PyPowsyblApiHeader.ElementType.VSC_CONVERTER_STATION; @@ -212,6 +213,7 @@ public static PyPowsyblApiHeader.ElementType convert(DataframeElementType type) PyPowsyblApiHeader.ElementType.NON_LINEAR_SHUNT_COMPENSATOR_SECTION; case LINEAR_SHUNT_COMPENSATOR_SECTION -> PyPowsyblApiHeader.ElementType.LINEAR_SHUNT_COMPENSATOR_SECTION; case OPERATIONAL_LIMITS -> PyPowsyblApiHeader.ElementType.OPERATIONAL_LIMITS; + case SELECTED_OPERATIONAL_LIMITS -> PyPowsyblApiHeader.ElementType.SELECTED_OPERATIONAL_LIMITS; case MINMAX_REACTIVE_LIMITS -> PyPowsyblApiHeader.ElementType.MINMAX_REACTIVE_LIMITS; case ALIAS -> PyPowsyblApiHeader.ElementType.ALIAS; case TERMINAL -> PyPowsyblApiHeader.ElementType.TERMINAL; @@ -219,6 +221,10 @@ public static PyPowsyblApiHeader.ElementType convert(DataframeElementType type) case BRANCH -> PyPowsyblApiHeader.ElementType.BRANCH; case IDENTIFIABLE -> PyPowsyblApiHeader.ElementType.IDENTIFIABLE; case SUB_NETWORK -> PyPowsyblApiHeader.ElementType.SUB_NETWORK; + case AREA -> PyPowsyblApiHeader.ElementType.AREA; + case AREA_VOLTAGE_LEVELS -> PyPowsyblApiHeader.ElementType.AREA_VOLTAGE_LEVELS; + case AREA_BOUNDARIES -> PyPowsyblApiHeader.ElementType.AREA_BOUNDARIES; + case INTERNAL_CONNECTION -> PyPowsyblApiHeader.ElementType.INTERNAL_CONNECTION; }; } @@ -231,9 +237,11 @@ public static DataframeElementType convert(PyPowsyblApiHeader.ElementType type) case THREE_WINDINGS_TRANSFORMER -> DataframeElementType.THREE_WINDINGS_TRANSFORMER; case GENERATOR -> DataframeElementType.GENERATOR; case LOAD -> DataframeElementType.LOAD; + case GROUND -> DataframeElementType.GROUND; case BATTERY -> DataframeElementType.BATTERY; case SHUNT_COMPENSATOR -> DataframeElementType.SHUNT_COMPENSATOR; case DANGLING_LINE -> DataframeElementType.DANGLING_LINE; + case DANGLING_LINE_GENERATION -> DataframeElementType.DANGLING_LINE_GENERATION; case TIE_LINE -> DataframeElementType.TIE_LINE; case LCC_CONVERTER_STATION -> DataframeElementType.LCC_CONVERTER_STATION; case VSC_CONVERTER_STATION -> DataframeElementType.VSC_CONVERTER_STATION; @@ -251,6 +259,7 @@ public static DataframeElementType convert(PyPowsyblApiHeader.ElementType type) case NON_LINEAR_SHUNT_COMPENSATOR_SECTION -> DataframeElementType.NON_LINEAR_SHUNT_COMPENSATOR_SECTION; case LINEAR_SHUNT_COMPENSATOR_SECTION -> DataframeElementType.LINEAR_SHUNT_COMPENSATOR_SECTION; case OPERATIONAL_LIMITS -> DataframeElementType.OPERATIONAL_LIMITS; + case SELECTED_OPERATIONAL_LIMITS -> DataframeElementType.SELECTED_OPERATIONAL_LIMITS; case MINMAX_REACTIVE_LIMITS -> DataframeElementType.MINMAX_REACTIVE_LIMITS; case ALIAS -> DataframeElementType.ALIAS; case TERMINAL -> DataframeElementType.TERMINAL; @@ -258,6 +267,10 @@ public static DataframeElementType convert(PyPowsyblApiHeader.ElementType type) case BRANCH -> DataframeElementType.BRANCH; case IDENTIFIABLE -> DataframeElementType.IDENTIFIABLE; case SUB_NETWORK -> DataframeElementType.SUB_NETWORK; + case AREA -> DataframeElementType.AREA; + case AREA_VOLTAGE_LEVELS -> DataframeElementType.AREA_VOLTAGE_LEVELS; + case AREA_BOUNDARIES -> DataframeElementType.AREA_BOUNDARIES; + case INTERNAL_CONNECTION -> DataframeElementType.INTERNAL_CONNECTION; }; } @@ -419,7 +432,6 @@ public static LimitViolationType convert(PyPowsyblApiHeader.LimitViolationType v case LOW_SHORT_CIRCUIT_CURRENT -> LimitViolationType.LOW_SHORT_CIRCUIT_CURRENT; case HIGH_SHORT_CIRCUIT_CURRENT -> LimitViolationType.HIGH_SHORT_CIRCUIT_CURRENT; case OTHER -> LimitViolationType.OTHER; - default -> throw new PowsyblException("Unknown limit violation type: " + violationType); }; } diff --git a/java/src/main/java/com/powsybl/python/dataframe/CDataframeHandler.java b/java/src/main/java/com/powsybl/python/dataframe/CDataframeHandler.java index 2a767cec9f..04bd6bc35c 100644 --- a/java/src/main/java/com/powsybl/python/dataframe/CDataframeHandler.java +++ b/java/src/main/java/com/powsybl/python/dataframe/CDataframeHandler.java @@ -24,7 +24,7 @@ /** * Writes dataframe to C structures. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class CDataframeHandler implements DataframeHandler { diff --git a/java/src/main/java/com/powsybl/python/datasource/InMemoryZipFileDataSource.java b/java/src/main/java/com/powsybl/python/datasource/InMemoryZipFileDataSource.java index 4f582b1dd5..fb5aa9d3fd 100644 --- a/java/src/main/java/com/powsybl/python/datasource/InMemoryZipFileDataSource.java +++ b/java/src/main/java/com/powsybl/python/datasource/InMemoryZipFileDataSource.java @@ -8,7 +8,7 @@ package com.powsybl.python.datasource; import com.powsybl.commons.datasource.DataSourceUtil; -import com.powsybl.commons.datasource.ReadOnlyDataSource; +import com.powsybl.commons.datasource.ReadOnlyMemDataSource; import com.powsybl.commons.io.ForwardingInputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; @@ -25,7 +25,7 @@ /** * @author Bertrand Rix {@literal } */ -public class InMemoryZipFileDataSource implements ReadOnlyDataSource { +public class InMemoryZipFileDataSource extends ReadOnlyMemDataSource { private final byte[] zipFileBytes; diff --git a/java/src/main/java/com/powsybl/python/dynamic/CurveMappingSupplier.java b/java/src/main/java/com/powsybl/python/dynamic/CurveMappingSupplier.java deleted file mode 100644 index bea73a052c..0000000000 --- a/java/src/main/java/com/powsybl/python/dynamic/CurveMappingSupplier.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.python.dynamic; - -import com.powsybl.commons.report.ReportNode; -import com.powsybl.dynamicsimulation.OutputVariable; -import com.powsybl.dynamicsimulation.OutputVariablesSupplier; -import com.powsybl.dynawo.outputvariables.DynawoOutputVariablesBuilder; -import com.powsybl.iidm.network.Network; - -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; - -/** - * @author Nicolas Pierre - */ -public class CurveMappingSupplier implements OutputVariablesSupplier { - - private final List outputvariables; - - public CurveMappingSupplier() { - outputvariables = new LinkedList<>(); - } - - public void addCurve(String dynamicId, String variable) { - outputvariables.addAll(new DynawoOutputVariablesBuilder().dynamicModelId(dynamicId).variable(variable).build()); - } - - public void addCurves(String dynamicId, Collection variablesCol) { - for (String variable : variablesCol) { - addCurve(dynamicId, variable); - } - } - - @Override - public List get(Network network, ReportNode reportNode) { - return get(network); - } - - @Override - public List get(Network network) { - return outputvariables; - } - -} diff --git a/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationCFunctions.java b/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationCFunctions.java index 53a4993b7c..aa12e8668d 100644 --- a/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationCFunctions.java +++ b/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationCFunctions.java @@ -8,43 +8,52 @@ package com.powsybl.python.dynamic; import static com.powsybl.python.commons.Util.doCatch; +import static com.powsybl.python.network.NetworkCFunctions.createDataframe; import java.util.ArrayList; +import java.util.List; -import com.powsybl.dynamicsimulation.OutputVariablesSupplier; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.python.report.ReportCUtils; import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.ObjectHandle; import org.graalvm.nativeimage.ObjectHandles; +import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.CContext; import org.graalvm.nativeimage.c.function.CEntryPoint; +import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CCharPointerPointer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.powsybl.dataframe.dynamic.CurvesSeries; -import com.powsybl.dataframe.dynamic.adders.DynamicMappingAdderFactory; +import com.powsybl.dataframe.dynamic.adders.DynamicMappingHandler; +import com.powsybl.dataframe.dynamic.adders.EventMappingHandler; import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.dynamicsimulation.OutputVariablesSupplier; import com.powsybl.dynamicsimulation.DynamicSimulationParameters; import com.powsybl.dynamicsimulation.DynamicSimulationResult; import com.powsybl.dynamicsimulation.EventModelsSupplier; import com.powsybl.iidm.network.Network; import com.powsybl.python.commons.CTypeUtil; import com.powsybl.python.commons.Directives; -import com.powsybl.python.commons.PyPowsyblApiHeader; import com.powsybl.python.commons.PyPowsyblApiHeader.ArrayPointer; import com.powsybl.python.commons.PyPowsyblApiHeader.DataframeMetadataPointer; import com.powsybl.python.commons.PyPowsyblApiHeader.DataframePointer; import com.powsybl.python.commons.PyPowsyblApiHeader.DynamicMappingType; +import com.powsybl.python.commons.PyPowsyblApiHeader.EventMappingType; import com.powsybl.python.commons.PyPowsyblApiHeader.SeriesPointer; import com.powsybl.python.commons.Util; import com.powsybl.python.network.Dataframes; -import com.powsybl.python.network.NetworkCFunctions; import com.powsybl.timeseries.DoublePoint; import com.powsybl.timeseries.TimeSeries; +import static com.powsybl.python.commons.PyPowsyblApiHeader.*; + /** - * @author Nicolas Pierre + * @author Nicolas Pierre {@literal } */ @CContext(Directives.class) public final class DynamicSimulationCFunctions { @@ -58,72 +67,117 @@ private static Logger logger() { @CEntryPoint(name = "createDynamicSimulationContext") public static ObjectHandle createDynamicSimulationContext(IsolateThread thread, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> ObjectHandles.getGlobal().create(new DynamicSimulationContext())); } @CEntryPoint(name = "createDynamicModelMapping") public static ObjectHandle createDynamicModelMapping(IsolateThread thread, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> ObjectHandles.getGlobal().create(new PythonDynamicModelsSupplier())); } @CEntryPoint(name = "createTimeseriesMapping") public static ObjectHandle createTimeseriesMapping(IsolateThread thread, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { - return doCatch(exceptionHandlerPtr, () -> ObjectHandles.getGlobal().create(new CurveMappingSupplier())); + ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> ObjectHandles.getGlobal().create(new PythonCurveSupplier())); } @CEntryPoint(name = "createEventMapping") public static ObjectHandle createEventMapping(IsolateThread thread, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { - return doCatch(exceptionHandlerPtr, () -> ObjectHandles.getGlobal().create(new EventSupplier())); + ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> ObjectHandles.getGlobal().create(new PythonEventModelsSupplier())); } @CEntryPoint(name = "runDynamicModel") public static ObjectHandle runDynamicModel(IsolateThread thread, - ObjectHandle dynamicContextHandle, - ObjectHandle networkHandle, - ObjectHandle dynamicMappingHandle, - ObjectHandle eventModelsSupplierHandle, - ObjectHandle curvesSupplierHandle, - int startTime, int stopTime, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + ObjectHandle dynamicContextHandle, + ObjectHandle networkHandle, + ObjectHandle dynamicMappingHandle, + ObjectHandle eventModelsSupplierHandle, + ObjectHandle curvesSupplierHandle, + int startTime, + int stopTime, + ObjectHandle reportNodeHandle, + ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> { DynamicSimulationContext dynamicContext = ObjectHandles.getGlobal().get(dynamicContextHandle); Network network = ObjectHandles.getGlobal().get(networkHandle); PythonDynamicModelsSupplier dynamicMapping = ObjectHandles.getGlobal().get(dynamicMappingHandle); EventModelsSupplier eventModelsSupplier = ObjectHandles.getGlobal().get(eventModelsSupplierHandle); OutputVariablesSupplier curvesSupplier = ObjectHandles.getGlobal().get(curvesSupplierHandle); + ReportNode reportNode = ReportCUtils.getReportNode(reportNodeHandle); DynamicSimulationParameters dynamicSimulationParameters = new DynamicSimulationParameters(startTime, stopTime); DynamicSimulationResult result = dynamicContext.run(network, dynamicMapping, eventModelsSupplier, curvesSupplier, - dynamicSimulationParameters); + dynamicSimulationParameters, + reportNode); logger().info("Dynamic simulation ran successfully in java"); return ObjectHandles.getGlobal().create(result); }); } @CEntryPoint(name = "addDynamicMappings") - public static void addDynamicMapping(IsolateThread thread, ObjectHandle dynamicMappingHandle, - DynamicMappingType mappingType, - DataframePointer mappingDataframePtr, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + public static void addDynamicMappings(IsolateThread thread, ObjectHandle dynamicMappingHandle, + DynamicMappingType mappingType, + DataframeArrayPointer mappingDataframePtr, + ExceptionHandlerPointer exceptionHandlerPtr) { doCatch(exceptionHandlerPtr, () -> { PythonDynamicModelsSupplier dynamicMapping = ObjectHandles.getGlobal().get(dynamicMappingHandle); - UpdatingDataframe mappingDataframe = NetworkCFunctions.createDataframe(mappingDataframePtr); - DynamicMappingAdderFactory.getAdder(mappingType).addElements(dynamicMapping, mappingDataframe); + List mappingDataframes = new ArrayList<>(); + for (int i = 0; i < mappingDataframePtr.getDataframesCount(); i++) { + mappingDataframes.add(createDataframe(mappingDataframePtr.getDataframes().addressOf(i))); + } + DynamicMappingHandler.addElements(mappingType, dynamicMapping, mappingDataframes); }); } @CEntryPoint(name = "getDynamicMappingsMetaData") - public static DataframeMetadataPointer getDynamicMappingsMetaData(IsolateThread thread, - DynamicMappingType mappingType, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { - return doCatch(exceptionHandlerPtr, () -> CTypeUtil.createSeriesMetadata(DynamicMappingAdderFactory.getAdder(mappingType).getMetadata())); + public static DataframesMetadataPointer getDynamicMappingsMetaData(IsolateThread thread, + DynamicMappingType mappingType, + ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + List> metadata = DynamicMappingHandler.getMetadata(mappingType); + DataframeMetadataPointer dataframeMetadataArray = UnmanagedMemory.calloc(metadata.size() * SizeOf.get(DataframeMetadataPointer.class)); + int i = 0; + for (List dataframeMetadata : metadata) { + CTypeUtil.createSeriesMetadata(dataframeMetadata, dataframeMetadataArray.addressOf(i)); + i++; + } + DataframesMetadataPointer res = UnmanagedMemory.calloc(SizeOf.get(DataframesMetadataPointer.class)); + res.setDataframesMetadata(dataframeMetadataArray); + res.setDataframesCount(metadata.size()); + return res; + }); + } + + @CEntryPoint(name = "getSupportedModels") + public static ArrayPointer getSupportedModels(IsolateThread thread, + DynamicMappingType mappingType, + ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> Util.createCharPtrArray(List.copyOf(DynamicMappingHandler.getSupportedModels(mappingType)))); + } + + @CEntryPoint(name = "addEventMappings") + public static void addEventMappings(IsolateThread thread, ObjectHandle eventMappingHandle, + EventMappingType mappingType, + DataframePointer mappingDataframePtr, + ExceptionHandlerPointer exceptionHandlerPtr) { + doCatch(exceptionHandlerPtr, () -> { + PythonEventModelsSupplier eventMapping = ObjectHandles.getGlobal().get(eventMappingHandle); + UpdatingDataframe mappingDataframe = createDataframe(mappingDataframePtr); + EventMappingHandler.addElements(mappingType, eventMapping, mappingDataframe); + }); + } + + @CEntryPoint(name = "getEventMappingsMetaData") + public static DataframeMetadataPointer getEventMappingsMetaData(IsolateThread thread, + EventMappingType mappingType, + ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> CTypeUtil.createSeriesMetadata(EventMappingHandler.getMetadata(mappingType))); } @CEntryPoint(name = "addCurve") @@ -131,33 +185,19 @@ public static void addCurve(IsolateThread thread, ObjectHandle timeseriesSupplier, CCharPointer dynamicIdPtr, CCharPointer variablePtr, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + ExceptionHandlerPointer exceptionHandlerPtr) { doCatch(exceptionHandlerPtr, () -> { String dynamicId = CTypeUtil.toString(dynamicIdPtr); String variable = CTypeUtil.toString(variablePtr); - CurveMappingSupplier timeSeriesSupplier = ObjectHandles.getGlobal().get(timeseriesSupplier); + PythonCurveSupplier timeSeriesSupplier = ObjectHandles.getGlobal().get(timeseriesSupplier); timeSeriesSupplier.addCurve(dynamicId, variable); }); } - @CEntryPoint(name = "addEventDisconnection") - public static void addEventDisconnection(IsolateThread thread, - ObjectHandle eventSupplierHandle, - CCharPointer staticIdPtr, - double eventTime, - int disconnectOnly, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { - doCatch(exceptionHandlerPtr, () -> { - String staticId = CTypeUtil.toString(staticIdPtr); - EventSupplier eventSupplier = ObjectHandles.getGlobal().get(eventSupplierHandle); - eventSupplier.addEventDisconnection(staticId, eventTime, Util.convert(PyPowsyblApiHeader.ThreeSideType.fromCValue(disconnectOnly)).toTwoSides()); - }); - } - @CEntryPoint(name = "getDynamicSimulationResultsStatus") public static CCharPointer getDynamicSimulationResultsStatus(IsolateThread thread, ObjectHandle dynamicSimulationResultsHandle, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> { DynamicSimulationResult simulationResult = ObjectHandles.getGlobal().get(dynamicSimulationResultsHandle); return CTypeUtil.toCharPtr(simulationResult.isOk() ? "Ok" : "Not OK"); @@ -168,7 +208,7 @@ public static CCharPointer getDynamicSimulationResultsStatus(IsolateThread threa public static ArrayPointer getDynamicCurve(IsolateThread thread, ObjectHandle resultHandle, CCharPointer curveNamePtr, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> { DynamicSimulationResult result = ObjectHandles.getGlobal().get(resultHandle); String curveName = CTypeUtil.toString(curveNamePtr); @@ -180,7 +220,7 @@ public static ArrayPointer getDynamicCurve(IsolateThread thread, @CEntryPoint(name = "getAllDynamicCurvesIds") public static ArrayPointer getAllDynamicCurvesIds(IsolateThread thread, ObjectHandle resultHandle, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> { DynamicSimulationResult result = ObjectHandles.getGlobal().get(resultHandle); return Util.createCharPtrArray(new ArrayList<>(result.getCurves().keySet())); diff --git a/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationContext.java b/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationContext.java index 4845b0b117..17836790b9 100644 --- a/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationContext.java +++ b/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationContext.java @@ -7,49 +7,31 @@ */ package com.powsybl.python.dynamic; -import java.util.ServiceLoader; - +import com.powsybl.commons.report.ReportNode; +import com.powsybl.computation.local.LocalComputationManager; import com.powsybl.dynamicsimulation.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.Network; /** - * @author Nicolas Pierre + * @author Nicolas Pierre {@literal } */ public class DynamicSimulationContext { - private static final Logger LOGGER = LoggerFactory.getLogger(DynamicSimulationContext.class); + + private static final String DEFAULT_PROVIDER = "Dynawo"; public DynamicSimulationResult run(Network network, DynamicModelsSupplier dynamicModelsSupplier, EventModelsSupplier eventModelsSupplier, OutputVariablesSupplier curvesSupplier, - DynamicSimulationParameters parameters) { - DynamicSimulationProvider provider = getDynamicProvider(""); - LOGGER.info(String.format("Running dynamic simulation with %s", provider.getName())); - DynamicSimulation.Runner runner = new DynamicSimulation.Runner(provider); - return runner.run(network, + DynamicSimulationParameters parameters, + ReportNode reportNode) { + return DynamicSimulation.find(DEFAULT_PROVIDER).run(network, dynamicModelsSupplier, eventModelsSupplier, curvesSupplier, - parameters); - } - - /** - * TODO do we need to keep interface with providers here or if it should be done - * by the class {@link DynamicSimulation} - * - * @param name - * @return DynamicSimulationProvider - */ - public static DynamicSimulationProvider getDynamicProvider(String name) { - String actualName = (name == null || name.isEmpty()) ? "DynaWaltz" : name; - return ServiceLoader.load(DynamicSimulationProvider.class).stream() - .map(ServiceLoader.Provider::get) - .filter(provider -> provider.getName().equals(actualName)) - .findFirst() - .orElseThrow(() -> new PowsyblException("No dynamicSimulation provider for name '" + actualName + "'")); + network.getVariantManager().getWorkingVariantId(), + LocalComputationManager.getDefault(), + parameters, + reportNode); } } diff --git a/java/src/main/java/com/powsybl/python/dynamic/EventSupplier.java b/java/src/main/java/com/powsybl/python/dynamic/EventSupplier.java deleted file mode 100644 index 01853be630..0000000000 --- a/java/src/main/java/com/powsybl/python/dynamic/EventSupplier.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.python.dynamic; - -import com.powsybl.commons.report.ReportNode; -import com.powsybl.dynamicsimulation.EventModel; -import com.powsybl.dynamicsimulation.EventModelsSupplier; -import com.powsybl.dynawo.models.events.EventDisconnectionBuilder; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.TwoSides; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.function.Function; - -/** - * @author Nicolas Pierre - * @author Laurent Issertial {@literal } - */ -public class EventSupplier implements EventModelsSupplier { - - private final List> eventSupplierList = new ArrayList<>(); - - /** - * According to Dynawaltz staticId must refer to an injection, branch or hvdc line - *

- * The event represent the disconnection the given equipment - */ - public void addEventDisconnection(String staticId, double eventTime, TwoSides disconnectOnly) { - eventSupplierList.add(network -> { - EventDisconnectionBuilder builder = EventDisconnectionBuilder.of(network) - .staticId(staticId) - .startTime(eventTime); - if (disconnectOnly != null) { - builder.disconnectOnly(disconnectOnly); - } - return builder.build(); - }); - } - - @Override - public List get(Network network, ReportNode reportNode) { - return get(network); - } - - @Override - public List get(Network network) { - return eventSupplierList.stream().map(f -> f.apply(network)).filter(Objects::nonNull).toList(); - } -} diff --git a/java/src/main/java/com/powsybl/python/dynamic/PythonCurveSupplier.java b/java/src/main/java/com/powsybl/python/dynamic/PythonCurveSupplier.java new file mode 100644 index 0000000000..9defae51bd --- /dev/null +++ b/java/src/main/java/com/powsybl/python/dynamic/PythonCurveSupplier.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.python.dynamic; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dynamicsimulation.OutputVariable; +import com.powsybl.dynamicsimulation.OutputVariablesSupplier; +import com.powsybl.dynawo.outputvariables.DynawoOutputVariablesBuilder; +import com.powsybl.iidm.network.Network; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** + * @author Nicolas Pierre + * @author Laurent Issertial {@literal } + */ +public class PythonCurveSupplier implements OutputVariablesSupplier { + + private final List, ReportNode>> curvesSupplierList = new ArrayList<>(); + + public void addCurve(String dynamicId, String variable) { + curvesSupplierList.add((c, r) -> new DynawoOutputVariablesBuilder(r) + .dynamicModelId(dynamicId) + .variable(variable) + .add(c)); + } + + @Override + public List get(Network network, ReportNode reportNode) { + List curves = new ArrayList<>(); + ReportNode supplierReportNode = SupplierReport.createSupplierReportNode(reportNode, + "pypowsyblOutputVariables", + "PyPowsybl Output Variables Supplier"); + curvesSupplierList.forEach(c -> c.accept(curves::add, supplierReportNode)); + return curves; + } +} diff --git a/java/src/main/java/com/powsybl/python/dynamic/PythonDynamicModelsSupplier.java b/java/src/main/java/com/powsybl/python/dynamic/PythonDynamicModelsSupplier.java index f50f6a978a..be2a0f8d4e 100644 --- a/java/src/main/java/com/powsybl/python/dynamic/PythonDynamicModelsSupplier.java +++ b/java/src/main/java/com/powsybl/python/dynamic/PythonDynamicModelsSupplier.java @@ -10,25 +10,20 @@ import com.powsybl.commons.report.ReportNode; import com.powsybl.dynamicsimulation.DynamicModel; import com.powsybl.dynamicsimulation.DynamicModelsSupplier; -import com.powsybl.dynawo.models.automationsystems.overloadmanagments.DynamicOverloadManagementSystemBuilder; -import com.powsybl.dynawo.models.generators.SynchronizedGeneratorBuilder; -import com.powsybl.dynawo.models.loads.BaseLoadBuilder; -import com.powsybl.dynawo.models.loads.LoadOneTransformerBuilder; import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.TwoSides; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.function.Function; +import java.util.function.BiFunction; /** - * @author Nicolas Pierre + * @author Nicolas Pierre {@literal } * @author Laurent Issertial {@literal } */ public class PythonDynamicModelsSupplier implements DynamicModelsSupplier { - private final List> dynamicModelList = new ArrayList<>(); + private final List> dynamicModelList = new ArrayList<>(); @Override public String getName() { @@ -37,70 +32,13 @@ public String getName() { @Override public List get(Network network, ReportNode reportNode) { - return get(network); + ReportNode supplierReportNode = SupplierReport.createSupplierReportNode(reportNode, + "pypowsyblDynamicModels", + "PyPowsybl Dynamic Models Supplier"); + return dynamicModelList.stream().map(f -> f.apply(network, supplierReportNode)).filter(Objects::nonNull).toList(); } - @Override - public List get(Network network) { - return dynamicModelList.stream().map(f -> f.apply(network)).filter(Objects::nonNull).toList(); - } - - /** - * maps static element to a dynamic alpha_beta load - * - * @param staticId also determines the dynamic id of the element - */ - public void addAlphaBetaLoad(String staticId, String parameterSetId) { - dynamicModelList.add(network -> BaseLoadBuilder.of(network, "LoadAlphaBeta") - .staticId(staticId) - .parameterSetId(parameterSetId) - .build()); - } - - /** - * maps static element to a dynamic one transformer - * - * @param staticId also determines the dynamic id of the element - */ - public void addOneTransformerLoad(String staticId, String parameterSetId) { - dynamicModelList.add(network -> LoadOneTransformerBuilder.of(network, "LoadOneTransformer") - .staticId(staticId) - .parameterSetId(parameterSetId) - .build()); - } - - public void addModel(Function modelFunction) { + public void addModel(BiFunction modelFunction) { dynamicModelList.add(modelFunction); } - - public void addGeneratorSynchronous(String staticId, String parameterSetId, String generatorLib) { - dynamicModelList.add(network -> SynchronizedGeneratorBuilder.of(network, generatorLib) - .staticId(staticId) - .parameterSetId(parameterSetId) - .build()); - } - - public void addGeneratorSynchronousThreeWindings(String staticId, String parameterSetId) { - addGeneratorSynchronous(staticId, parameterSetId, "GeneratorSynchronousThreeWindings"); - } - - public void addGeneratorSynchronousThreeWindingsProportionalRegulations(String staticId, String parameterSetId) { - addGeneratorSynchronous(staticId, parameterSetId, "GeneratorSynchronousThreeWindingsProportionalRegulations"); - } - - public void addGeneratorSynchronousFourWindings(String staticId, String parameterSetId) { - addGeneratorSynchronous(staticId, parameterSetId, "GeneratorSynchronousFourWindings"); - } - - public void addGeneratorSynchronousFourWindingsProportionalRegulations(String staticId, String parameterSetId) { - addGeneratorSynchronous(staticId, parameterSetId, "GeneratorSynchronousFourWindingsProportionalRegulations"); - } - - public void addCurrentLimitAutomaton(String staticId, String parameterSetId, TwoSides side) { - dynamicModelList.add(network -> DynamicOverloadManagementSystemBuilder.of(network, "CurrentLimitAutomaton") - .parameterSetId(parameterSetId) - .iMeasurement(staticId) - .iMeasurementSide(side) - .build()); - } } diff --git a/java/src/main/java/com/powsybl/python/dynamic/PythonEventModelsSupplier.java b/java/src/main/java/com/powsybl/python/dynamic/PythonEventModelsSupplier.java new file mode 100644 index 0000000000..907f5b4961 --- /dev/null +++ b/java/src/main/java/com/powsybl/python/dynamic/PythonEventModelsSupplier.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2020-2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.python.dynamic; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dynamicsimulation.EventModel; +import com.powsybl.dynamicsimulation.EventModelsSupplier; +import com.powsybl.iidm.network.Network; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.BiFunction; + +/** + * @author Nicolas Pierre {@literal } + * @author Laurent Issertial {@literal } + */ +public class PythonEventModelsSupplier implements EventModelsSupplier { + + private final List> eventSupplierList = new ArrayList<>(); + + @Override + public List get(Network network, ReportNode reportNode) { + ReportNode supplierReportNode = SupplierReport.createSupplierReportNode(reportNode, + "pypowsyblEventModels", + "PyPowsybl Event Models Supplier"); + return eventSupplierList.stream().map(f -> f.apply(network, supplierReportNode)).filter(Objects::nonNull).toList(); + } + + public void addModel(BiFunction modelFunction) { + eventSupplierList.add(modelFunction); + } +} diff --git a/java/src/main/java/com/powsybl/python/dynamic/SupplierReport.java b/java/src/main/java/com/powsybl/python/dynamic/SupplierReport.java new file mode 100644 index 0000000000..5131cf0149 --- /dev/null +++ b/java/src/main/java/com/powsybl/python/dynamic/SupplierReport.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.python.dynamic; + +import com.powsybl.commons.report.ReportNode; + +/** + * @author Laurent Issertial {@literal } + */ +public final class SupplierReport { + + private SupplierReport() { + } + + public static ReportNode createSupplierReportNode(ReportNode reportNode, String key, String message) { + return reportNode.newReportNode() + .withMessageTemplate(key, message) + .add(); + } +} diff --git a/java/src/main/java/com/powsybl/python/glsk/GlskCFunctions.java b/java/src/main/java/com/powsybl/python/glsk/GlskCFunctions.java index a6acfc68e5..544b4164bc 100644 --- a/java/src/main/java/com/powsybl/python/glsk/GlskCFunctions.java +++ b/java/src/main/java/com/powsybl/python/glsk/GlskCFunctions.java @@ -29,7 +29,7 @@ /** * C functions for GLSK processing * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ @CContext(Directives.class) public final class GlskCFunctions { diff --git a/java/src/main/java/com/powsybl/python/loadflow/LoadFlowCFunctions.java b/java/src/main/java/com/powsybl/python/loadflow/LoadFlowCFunctions.java index 6e72b626ab..da2b5f5c4a 100644 --- a/java/src/main/java/com/powsybl/python/loadflow/LoadFlowCFunctions.java +++ b/java/src/main/java/com/powsybl/python/loadflow/LoadFlowCFunctions.java @@ -43,7 +43,7 @@ /** * C functions related to loadflow. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ @CContext(Directives.class) public final class LoadFlowCFunctions { diff --git a/java/src/main/java/com/powsybl/python/loadflow/LoadFlowCUtils.java b/java/src/main/java/com/powsybl/python/loadflow/LoadFlowCUtils.java index 60506b14f5..c323b2e3f5 100644 --- a/java/src/main/java/com/powsybl/python/loadflow/LoadFlowCUtils.java +++ b/java/src/main/java/com/powsybl/python/loadflow/LoadFlowCUtils.java @@ -21,18 +21,20 @@ import com.powsybl.python.commons.PyPowsyblConfiguration; import org.graalvm.nativeimage.UnmanagedMemory; +import java.util.Comparator; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public final class LoadFlowCUtils { static final DataframeMapper SPECIFIC_PARAMETERS_MAPPER = new DataframeMapperBuilder() - .itemsProvider(provider -> provider.getSpecificParameters()) + .itemsProvider(provider -> provider.getSpecificParameters().stream().sorted(Comparator.comparing(Parameter::getCategoryKey).thenComparing(Parameter::getName)).toList()) .stringsIndex("name", Parameter::getName) + .strings("category_key", p -> Objects.toString(p.getCategoryKey(), "")) .strings("description", Parameter::getDescription) .enums("type", ParameterType.class, Parameter::getType) .strings("default", p -> Objects.toString(p.getDefaultValue(), "")) diff --git a/java/src/main/java/com/powsybl/python/loadflow/validation/LoadFlowValidationCFunctions.java b/java/src/main/java/com/powsybl/python/loadflow/validation/LoadFlowValidationCFunctions.java index 945ef005f6..2efb3a2959 100644 --- a/java/src/main/java/com/powsybl/python/loadflow/validation/LoadFlowValidationCFunctions.java +++ b/java/src/main/java/com/powsybl/python/loadflow/validation/LoadFlowValidationCFunctions.java @@ -29,13 +29,15 @@ import com.powsybl.python.loadflow.LoadFlowCUtils; import com.powsybl.python.loadflow.LoadFlowCFunctions; +import java.util.Optional; + import static com.powsybl.python.commons.PyPowsyblApiHeader.*; import static com.powsybl.python.commons.Util.doCatch; /** * Defines C interface for loadflow validation. * - * @author Yichen TANG + * @author Yichen TANG {@literal } */ @CContext(Directives.class) public final class LoadFlowValidationCFunctions { @@ -116,8 +118,9 @@ public static LoadFlowValidationParametersPointer createValidationConfig(Isolate public static void copyToCLoadFlowValidationParameters(ValidationConfig parameters, LoadFlowValidationParametersPointer cParameters) { cParameters.setThreshold(parameters.getThreshold()); cParameters.setVerbose(parameters.isVerbose()); - if (parameters.getLoadFlowName().isPresent()) { - cParameters.setLoadFlowName(CTypeUtil.toCharPtr(parameters.getLoadFlowName().get())); + Optional optionalName = parameters.getLoadFlowName(); + if (optionalName.isPresent()) { + cParameters.setLoadFlowName(CTypeUtil.toCharPtr(optionalName.get())); } else { cParameters.setLoadFlowName(CTypeUtil.toCharPtr("")); } diff --git a/java/src/main/java/com/powsybl/python/logging/LoggingCFunctions.java b/java/src/main/java/com/powsybl/python/logging/LoggingCFunctions.java index 16f4762a53..50f5bff346 100644 --- a/java/src/main/java/com/powsybl/python/logging/LoggingCFunctions.java +++ b/java/src/main/java/com/powsybl/python/logging/LoggingCFunctions.java @@ -23,7 +23,7 @@ /** * C functions related to logging. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ @CContext(Directives.class) public final class LoggingCFunctions { diff --git a/java/src/main/java/com/powsybl/python/network/Dataframes.java b/java/src/main/java/com/powsybl/python/network/Dataframes.java index 76627f7c4d..1430a3124d 100644 --- a/java/src/main/java/com/powsybl/python/network/Dataframes.java +++ b/java/src/main/java/com/powsybl/python/network/Dataframes.java @@ -42,7 +42,7 @@ /** * Mappers to dataframes. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public final class Dataframes { @@ -363,15 +363,9 @@ private static DataframeMapper createBusBreak } private static List getBusBreakerViewBuses(VoltageLevel voltageLevel) { - return voltageLevel.getBusBreakerView().getBusStream().map(bus -> { - Bus busViewBus = bus.getConnectedTerminalStream() - .map(t -> t.getBusView().getBus()) - .filter(Objects::nonNull) - .findFirst() - .orElse(null); - return new BusBreakerViewBusData(bus, busViewBus); - }).collect(Collectors.toList()); - + return voltageLevel.getBusBreakerView().getBusStream() + .map(bus -> new BusBreakerViewBusData(bus, NetworkUtil.getBusViewBus(bus).orElse(null))) + .toList(); } private static DataframeMapper createBusBreakerViewBuses() { @@ -589,6 +583,7 @@ private static DataframeMapper createMagnitude .stringsIndex("id", MagnitudeFeederResultContext::getFaultId) .stringsIndex("connectable_id", MagnitudeFeederResultContext::getConnectableId) .doubles("current", MagnitudeFeederResultContext::getCurrent) + .enums("side", ThreeSides.class, MagnitudeFeederResultContext::getSide) .build(); } @@ -609,6 +604,7 @@ private static DataframeMapper createFortescue fortescueFeederResultContext.getCurrent().getZeroMagnitude(), false) .doubles("current_zero_angle", fortescueFeederResultContext -> fortescueFeederResultContext.getCurrent().getZeroAngle(), false) + .enums("side", ThreeSides.class, FortescueFeederResultContext::getSide) .build(); } diff --git a/java/src/main/java/com/powsybl/python/network/InternalConnectionContext.java b/java/src/main/java/com/powsybl/python/network/InternalConnectionContext.java index 913b7085b0..4a0b92ee7c 100644 --- a/java/src/main/java/com/powsybl/python/network/InternalConnectionContext.java +++ b/java/src/main/java/com/powsybl/python/network/InternalConnectionContext.java @@ -12,7 +12,7 @@ import java.util.Objects; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public record InternalConnectionContext(VoltageLevel.NodeBreakerView.InternalConnection internalConnection, int index) { diff --git a/java/src/main/java/com/powsybl/python/network/NetworkAreaDiagramUtil.java b/java/src/main/java/com/powsybl/python/network/NetworkAreaDiagramUtil.java index ae77b7665b..c078d9dec6 100644 --- a/java/src/main/java/com/powsybl/python/network/NetworkAreaDiagramUtil.java +++ b/java/src/main/java/com/powsybl/python/network/NetworkAreaDiagramUtil.java @@ -31,13 +31,13 @@ public final class NetworkAreaDiagramUtil { private NetworkAreaDiagramUtil() { } - static void writeSvg(Network network, List voltageLevelIds, int depth, Writer writer, + static void writeSvg(Network network, List voltageLevelIds, int depth, Writer writer, Writer metadataWriter, double nominalVoltageUpperBound, double nominalVoltageLowerBound, NadParameters nadParameters) { Predicate filter = !voltageLevelIds.isEmpty() ? getNominalVoltageFilter(network, voltageLevelIds, nominalVoltageLowerBound, nominalVoltageUpperBound, depth) : getNominalVoltageFilter(network, nominalVoltageLowerBound, nominalVoltageUpperBound); - NetworkAreaDiagram.draw(network, writer, nadParameters, filter); + NetworkAreaDiagram.draw(network, writer, metadataWriter, nadParameters, filter); } static String getSvg(Network network, List voltageLevelIds, NadParameters nadParameters) { @@ -46,18 +46,35 @@ static String getSvg(Network network, List voltageLevelIds, NadParameter static String getSvg(Network network, List voltageLevelIds, int depth, double nominalVoltageUpperBound, double nominalVoltageLowerBound, NadParameters nadParameters) { - try (StringWriter writer = new StringWriter()) { - writeSvg(network, voltageLevelIds, depth, writer, nominalVoltageUpperBound, nominalVoltageLowerBound, nadParameters); + try (StringWriter writer = new StringWriter(); StringWriter metadataWriter = new StringWriter()) { + writeSvg(network, voltageLevelIds, depth, writer, metadataWriter, nominalVoltageUpperBound, + nominalVoltageLowerBound, nadParameters); return writer.toString(); } catch (IOException e) { throw new UncheckedIOException(e); } } - static void writeSvg(Network network, List voltageLevelIds, int depth, String svgFile, + static List getSvgAndMetadata(Network network, List voltageLevelIds, NadParameters nadParameters) { + return getSvgAndMetadata(network, voltageLevelIds, 0, -1, -1, nadParameters); + } + + static List getSvgAndMetadata(Network network, List voltageLevelIds, int depth, + double nominalVoltageUpperBound, double nominalVoltageLowerBound, NadParameters nadParameters) { + try (StringWriter writer = new StringWriter(); StringWriter metadataWriter = new StringWriter()) { + writeSvg(network, voltageLevelIds, depth, writer, metadataWriter, nominalVoltageUpperBound, + nominalVoltageLowerBound, nadParameters); + return List.of(writer.toString(), metadataWriter.toString()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + static void writeSvg(Network network, List voltageLevelIds, int depth, String svgFile, String metadataFile, Double nominalVoltageUpperBound, Double nominalVoltageLowerBound, NadParameters nadParameters) { - try (Writer writer = Files.newBufferedWriter(Paths.get(svgFile), StandardCharsets.UTF_8)) { - writeSvg(network, voltageLevelIds, depth, writer, nominalVoltageUpperBound, nominalVoltageLowerBound, nadParameters); + try (Writer writer = Files.newBufferedWriter(Paths.get(svgFile), StandardCharsets.UTF_8); + Writer metadataWriter = metadataFile == null || metadataFile.isEmpty() ? new StringWriter() : Files.newBufferedWriter(Paths.get(metadataFile))) { + writeSvg(network, voltageLevelIds, depth, writer, metadataWriter, nominalVoltageUpperBound, nominalVoltageLowerBound, nadParameters); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java b/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java index aee7f43fb8..0bd9547035 100644 --- a/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java +++ b/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java @@ -21,6 +21,7 @@ import com.powsybl.dataframe.network.NetworkDataframeMapper; import com.powsybl.dataframe.network.NetworkDataframes; import com.powsybl.dataframe.network.adders.AliasDataframeAdder; +import com.powsybl.dataframe.network.adders.InternalConnectionDataframeAdder; import com.powsybl.dataframe.network.adders.NetworkElementAdders; import com.powsybl.dataframe.network.adders.NetworkUtils; import com.powsybl.dataframe.network.extensions.NetworkExtensions; @@ -78,7 +79,7 @@ /** * Defines the basic C functions for a network. * - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ @CContext(Directives.class) public final class NetworkCFunctions { @@ -94,11 +95,21 @@ public static ArrayPointer getNetworkImportFormats(IsolateT return doCatch(exceptionHandlerPtr, () -> createCharPtrArray(Importer.getFormats().stream().sorted().toList())); } + @CEntryPoint(name = "getNetworkImportSupportedExtensions") + public static ArrayPointer getNetworkImportSupportedExtensions(IsolateThread thread, ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> createCharPtrArray(Importer.list().stream().flatMap(l -> l.getSupportedExtensions().stream()).distinct().sorted().toList())); + } + @CEntryPoint(name = "getNetworkExportFormats") public static ArrayPointer getNetworkExportFormats(IsolateThread thread, ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> createCharPtrArray(Exporter.getFormats().stream().sorted().toList())); } + @CEntryPoint(name = "getNetworkImportPostProcessors") + public static ArrayPointer getNetworkImportPostProcessors(IsolateThread thread, ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> createCharPtrArray(Importer.getPostProcessorNames().stream().sorted().toList())); + } + @CEntryPoint(name = "createNetwork") public static ObjectHandle createNetwork(IsolateThread thread, CCharPointer name, CCharPointer id, ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> { @@ -141,9 +152,19 @@ private static void freeNetworkMetadata(NetworkMetadataPointer networkMetadataPo UnmanagedMemory.free(networkMetadataPointer); } + private static ImportConfig createImportConfig(CCharPointerPointer postProcessorsPtrPtr, int postProcessorsCount) { + // FIXME to clean when a addPostProcessors will be added to core + List postProcessors = new ArrayList<>(); + postProcessors.addAll(ImportConfig.load().getPostProcessors()); + postProcessors.addAll(toStringList(postProcessorsPtrPtr, postProcessorsCount)); + return new ImportConfig(postProcessors.stream().distinct().toList()); + } + @CEntryPoint(name = "loadNetwork") public static ObjectHandle loadNetwork(IsolateThread thread, CCharPointer file, CCharPointerPointer parameterNamesPtrPtr, int parameterNamesCount, - CCharPointerPointer parameterValuesPtrPtr, int parameterValuesCount, ObjectHandle reportNodeHandle, + CCharPointerPointer parameterValuesPtrPtr, int parameterValuesCount, + CCharPointerPointer postProcessorsPtrPtr, int postProcessorsCount, + ObjectHandle reportNodeHandle, ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> { String fileStr = CTypeUtil.toString(file); @@ -152,7 +173,8 @@ public static ObjectHandle loadNetwork(IsolateThread thread, CCharPointer file, if (reportNode == null) { reportNode = ReportNode.NO_OP; } - Network network = Network.read(Paths.get(fileStr), LocalComputationManager.getDefault(), ImportConfig.load(), parameters, IMPORTERS_LOADER_SUPPLIER, reportNode); + var importConfig = createImportConfig(postProcessorsPtrPtr, postProcessorsCount); + Network network = Network.read(Paths.get(fileStr), LocalComputationManager.getDefault(), importConfig, parameters, IMPORTERS_LOADER_SUPPLIER, reportNode); return ObjectHandles.getGlobal().create(network); }); } @@ -161,7 +183,9 @@ public static ObjectHandle loadNetwork(IsolateThread thread, CCharPointer file, public static ObjectHandle loadNetworkFromString(IsolateThread thread, CCharPointer fileName, CCharPointer fileContent, CCharPointerPointer parameterNamesPtrPtr, int parameterNamesCount, CCharPointerPointer parameterValuesPtrPtr, int parameterValuesCount, - ObjectHandle reportNodeHandle, ExceptionHandlerPointer exceptionHandlerPtr) { + CCharPointerPointer postProcessorsPtrPtr, int postProcessorsCount, + ObjectHandle reportNodeHandle, + ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> { String fileNameStr = CTypeUtil.toString(fileName); String fileContentStr = CTypeUtil.toString(fileContent); @@ -171,7 +195,8 @@ public static ObjectHandle loadNetworkFromString(IsolateThread thread, CCharPoin if (reportNode == null) { reportNode = ReportNode.NO_OP; } - Network network = Network.read(fileNameStr, is, LocalComputationManager.getDefault(), ImportConfig.load(), parameters, IMPORTERS_LOADER_SUPPLIER, reportNode); + var importConfig = createImportConfig(postProcessorsPtrPtr, postProcessorsCount); + Network network = Network.read(fileNameStr, is, LocalComputationManager.getDefault(), importConfig, parameters, IMPORTERS_LOADER_SUPPLIER, reportNode); return ObjectHandles.getGlobal().create(network); } catch (IOException e) { throw new UncheckedIOException(e); @@ -180,8 +205,11 @@ public static ObjectHandle loadNetworkFromString(IsolateThread thread, CCharPoin } @CEntryPoint(name = "loadNetworkFromBinaryBuffers") - public static ObjectHandle loadNetworkFromBinaryBuffers(IsolateThread thread, CCharPointerPointer data, CIntPointer dataSizes, int bufferCount, CCharPointerPointer parameterNamesPtrPtr, - int parameterNamesCount, CCharPointerPointer parameterValuesPtrPtr, int parameterValuesCount, ObjectHandle reportNodeHandle, + public static ObjectHandle loadNetworkFromBinaryBuffers(IsolateThread thread, CCharPointerPointer data, CIntPointer dataSizes, int bufferCount, + CCharPointerPointer parameterNamesPtrPtr, int parameterNamesCount, + CCharPointerPointer parameterValuesPtrPtr, int parameterValuesCount, + CCharPointerPointer postProcessorsPtrPtr, int postProcessorsCount, + ObjectHandle reportNodeHandle, ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> { Properties parameters = createParameters(parameterNamesPtrPtr, parameterNamesCount, parameterValuesPtrPtr, parameterValuesCount); @@ -214,6 +242,8 @@ public static ObjectHandle loadNetworkFromBinaryBuffers(IsolateThread thread, CC reportNode = ReportNode.NO_OP; } MultipleReadOnlyDataSource dataSource = new MultipleReadOnlyDataSource(dataSourceList); + var importConfig = createImportConfig(postProcessorsPtrPtr, postProcessorsCount); + // FIXME there is no way to pass the import config with powsybl 2024.2.0. To FIX when upgrading to next release. Network network = Network.read(dataSource, parameters, reportNode); return ObjectHandles.getGlobal().create(network); }); @@ -495,6 +525,17 @@ public static void removeAliases(IsolateThread thread, ObjectHandle networkHandl }); } + @CEntryPoint(name = "removeInternalConnections") + public static void removeInternalConnections(IsolateThread thread, ObjectHandle networkHandle, + DataframePointer cDataframe, + ExceptionHandlerPointer exceptionHandlerPtr) { + doCatch(exceptionHandlerPtr, () -> { + Network network = ObjectHandles.getGlobal().get(networkHandle); + UpdatingDataframe dataframe = createDataframe(cDataframe); + InternalConnectionDataframeAdder.deleteElements(network, dataframe); + }); + } + @CEntryPoint(name = "removeNetworkElements") public static void removeNetworkElements(IsolateThread thread, ObjectHandle networkHandle, CCharPointerPointer cElementIds, int elementCount, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { @@ -524,6 +565,8 @@ public static void removeNetworkElements(IsolateThread thread, ObjectHandle netw } } else if (identifiable instanceof TieLine tieLine) { tieLine.remove(); + } else if (identifiable instanceof Area area) { + area.remove(); } else { throw new PowsyblException(String.format("identifiable with id : %s can't be removed", identifiable.getId())); } @@ -879,7 +922,7 @@ public static void copyToCSldParameters(SldParameters parameters, SldParametersP cParameters.setCenterName(parameters.getSvgParameters().isLabelCentered()); cParameters.setDiagonalLabel(parameters.getSvgParameters().isLabelDiagonal()); cParameters.setTopologicalColoring(parameters.getStyleProviderFactory() instanceof DefaultStyleProviderFactory); - cParameters.setAddNodesInfos(parameters.getSvgParameters().isAddNodesInfos()); + cParameters.setBusesLegendAdded(parameters.getSvgParameters().isBusesLegendAdded()); cParameters.setTooltipEnabled(parameters.getSvgParameters().isTooltipEnabled()); cParameters.setComponentLibrary(CTypeUtil.toCharPtr(parameters.getComponentLibrary().getName())); cParameters.setDisplayCurrentFeederInfo(parameters.getSvgParameters().isDisplayCurrentFeederInfo()); @@ -959,7 +1002,7 @@ public static SldParameters convertSldParameters(SldParametersPointer sldParamet .setUseName(sldParametersPtr.isUseName()) .setLabelCentered(sldParametersPtr.isCenterName()) .setLabelDiagonal(sldParametersPtr.isDiagonalLabel()) - .setAddNodesInfos(sldParametersPtr.isAddNodesInfos()) + .setBusesLegendAdded(sldParametersPtr.isBusesLegendAdded()) .setTooltipEnabled(sldParametersPtr.getTooltipEnabled()) .setDisplayCurrentFeederInfo(sldParametersPtr.isDisplayCurrentFeederInfo()) .setTooltipEnabled(sldParametersPtr.getTooltipEnabled()) @@ -1068,16 +1111,17 @@ public static PyPowsyblApiHeader.ArrayPointer getSingleLine } @CEntryPoint(name = "writeNetworkAreaDiagramSvg") - public static void writeNetworkAreaDiagramSvg(IsolateThread thread, ObjectHandle networkHandle, CCharPointer svgFile, + public static void writeNetworkAreaDiagramSvg(IsolateThread thread, ObjectHandle networkHandle, CCharPointer svgFile, CCharPointer metadataFile, CCharPointerPointer voltageLevelIdsPointer, int voltageLevelIdCount, int depth, double highNominalVoltageBound, double lowNominalVoltageBound, NadParametersPointer nadParametersPointer, ExceptionHandlerPointer exceptionHandlerPtr) { doCatch(exceptionHandlerPtr, () -> { Network network = ObjectHandles.getGlobal().get(networkHandle); String svgFileStr = CTypeUtil.toString(svgFile); + String metadataFileStr = metadataFile.isNonNull() ? CTypeUtil.toString(metadataFile) : null; List voltageLevelIds = toStringList(voltageLevelIdsPointer, voltageLevelIdCount); NadParameters nadParameters = convertNadParameters(nadParametersPointer, network); - NetworkAreaDiagramUtil.writeSvg(network, voltageLevelIds, depth, svgFileStr, highNominalVoltageBound, lowNominalVoltageBound, nadParameters); + NetworkAreaDiagramUtil.writeSvg(network, voltageLevelIds, depth, svgFileStr, metadataFileStr, highNominalVoltageBound, lowNominalVoltageBound, nadParameters); }); } @@ -1094,6 +1138,19 @@ public static CCharPointer getNetworkAreaDiagramSvg(IsolateThread thread, Object }); } + @CEntryPoint(name = "getNetworkAreaDiagramSvgAndMetadata") + public static ArrayPointer getNetworkAreaDiagramSvgAndMetadata(IsolateThread thread, ObjectHandle networkHandle, CCharPointerPointer voltageLevelIdsPointer, + int voltageLevelIdCount, int depth, double highNominalVoltageBound, + double lowNominalVoltageBound, NadParametersPointer nadParametersPointer, ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + Network network = ObjectHandles.getGlobal().get(networkHandle); + List voltageLevelIds = toStringList(voltageLevelIdsPointer, voltageLevelIdCount); + NadParameters nadParameters = convertNadParameters(nadParametersPointer, network); + List svgAndMeta = NetworkAreaDiagramUtil.getSvgAndMetadata(network, voltageLevelIds, depth, highNominalVoltageBound, lowNominalVoltageBound, nadParameters); + return createCharPtrArray(svgAndMeta); + }); + } + @CEntryPoint(name = "getNetworkAreaDiagramDisplayedVoltageLevels") public static PyPowsyblApiHeader.ArrayPointer getNetworkAreaDiagramDisplayedVoltageLevels(IsolateThread thread, ObjectHandle networkHandle, CCharPointerPointer voltageLevelIdsPointer, int voltageLevelIdCount, int depth, ExceptionHandlerPointer exceptionHandlerPtr) { diff --git a/java/src/main/java/com/powsybl/python/network/NetworkModificationsCFunctions.java b/java/src/main/java/com/powsybl/python/network/NetworkModificationsCFunctions.java index 953ae1284f..a6b718762d 100644 --- a/java/src/main/java/com/powsybl/python/network/NetworkModificationsCFunctions.java +++ b/java/src/main/java/com/powsybl/python/network/NetworkModificationsCFunctions.java @@ -42,7 +42,7 @@ /** * Defines the C functions for network modifications. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ @CContext(Directives.class) public final class NetworkModificationsCFunctions { diff --git a/java/src/main/java/com/powsybl/python/network/NetworkUtil.java b/java/src/main/java/com/powsybl/python/network/NetworkUtil.java index de7320c851..150413d616 100644 --- a/java/src/main/java/com/powsybl/python/network/NetworkUtil.java +++ b/java/src/main/java/com/powsybl/python/network/NetworkUtil.java @@ -13,8 +13,7 @@ import com.powsybl.iidm.network.extensions.ConnectablePosition; import com.powsybl.python.commons.PyPowsyblApiHeader; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -157,39 +156,50 @@ static List getElementsIds(Network network, PyPowsyblApiHeader.ElementTy public static Stream getLimits(Network network) { Stream.Builder limits = Stream.builder(); network.getBranchStream().forEach(branch -> { - addLimit(limits, branch, (LoadingLimits) branch.getCurrentLimits1().orElse(null), ONE); - addLimit(limits, branch, (LoadingLimits) branch.getCurrentLimits2().orElse(null), TWO); - addLimit(limits, branch, (LoadingLimits) branch.getActivePowerLimits1().orElse(null), ONE); - addLimit(limits, branch, (LoadingLimits) branch.getActivePowerLimits2().orElse(null), TWO); - addLimit(limits, branch, (LoadingLimits) branch.getApparentPowerLimits1().orElse(null), ONE); - addLimit(limits, branch, (LoadingLimits) branch.getApparentPowerLimits2().orElse(null), TWO); + addOperationalLimitGroupsLimits(limits, branch.getOperationalLimitsGroups1(), branch, ONE, + (String) branch.getSelectedOperationalLimitsGroupId1().orElse(null)); + addOperationalLimitGroupsLimits(limits, branch.getOperationalLimitsGroups2(), branch, TWO, + (String) branch.getSelectedOperationalLimitsGroupId2().orElse(null)); }); - network.getDanglingLineStream().forEach(danglingLine -> { - addLimit(limits, danglingLine, danglingLine.getCurrentLimits().orElse(null), NONE); - addLimit(limits, danglingLine, danglingLine.getActivePowerLimits().orElse(null), NONE); - addLimit(limits, danglingLine, danglingLine.getApparentPowerLimits().orElse(null), NONE); - }); - network.getThreeWindingsTransformerStream().forEach(threeWindingsTransformer -> { - addLimit(limits, threeWindingsTransformer, threeWindingsTransformer.getLeg1().getCurrentLimits().orElse(null), ONE); - addLimit(limits, threeWindingsTransformer, threeWindingsTransformer.getLeg1().getActivePowerLimits().orElse(null), ONE); - addLimit(limits, threeWindingsTransformer, threeWindingsTransformer.getLeg1().getApparentPowerLimits().orElse(null), ONE); - addLimit(limits, threeWindingsTransformer, threeWindingsTransformer.getLeg2().getCurrentLimits().orElse(null), TWO); - addLimit(limits, threeWindingsTransformer, threeWindingsTransformer.getLeg2().getActivePowerLimits().orElse(null), TWO); - addLimit(limits, threeWindingsTransformer, threeWindingsTransformer.getLeg2().getApparentPowerLimits().orElse(null), TWO); - addLimit(limits, threeWindingsTransformer, threeWindingsTransformer.getLeg3().getCurrentLimits().orElse(null), THREE); - addLimit(limits, threeWindingsTransformer, threeWindingsTransformer.getLeg3().getActivePowerLimits().orElse(null), THREE); - addLimit(limits, threeWindingsTransformer, threeWindingsTransformer.getLeg3().getApparentPowerLimits().orElse(null), THREE); + network.getDanglingLineStream().forEach(danglingLine -> + addOperationalLimitGroupsLimits(limits, danglingLine.getOperationalLimitsGroups(), danglingLine, NONE, + danglingLine.getSelectedOperationalLimitsGroupId().orElse(null)) + ); + network.getThreeWindingsTransformerStream().forEach(twt -> { + addOperationalLimitGroupsLimits(limits, twt.getLeg1().getOperationalLimitsGroups(), twt, ONE, + twt.getLeg1().getSelectedOperationalLimitsGroupId().orElse(null)); + addOperationalLimitGroupsLimits(limits, twt.getLeg2().getOperationalLimitsGroups(), twt, TWO, + twt.getLeg2().getSelectedOperationalLimitsGroupId().orElse(null)); + addOperationalLimitGroupsLimits(limits, twt.getLeg3().getOperationalLimitsGroups(), twt, THREE, + twt.getLeg3().getSelectedOperationalLimitsGroupId().orElse(null)); }); return limits.build(); } + public static Stream getSelectedLimits(Network network) { + return getLimits(network).filter(TemporaryLimitData::isSelected); + } + + private static void addOperationalLimitGroupsLimits(Stream.Builder limits, Collection groups, + Identifiable element, TemporaryLimitData.Side side, String selectedGroupId) { + groups.forEach(group -> { + String groupId1 = group.getId(); + boolean isSelected1 = groupId1.equals(selectedGroupId); + addLimit(limits, element, group.getCurrentLimits().orElse(null), side, groupId1, isSelected1); + addLimit(limits, element, group.getActivePowerLimits().orElse(null), side, groupId1, isSelected1); + addLimit(limits, element, group.getApparentPowerLimits().orElse(null), side, groupId1, isSelected1); + }); + } + private static void addLimit(Stream.Builder temporaryLimitContexts, Identifiable identifiable, - LoadingLimits limits, TemporaryLimitData.Side side) { + LoadingLimits limits, TemporaryLimitData.Side side, String groupId, boolean isSelected) { if (limits != null) { - temporaryLimitContexts.add(new TemporaryLimitData(identifiable.getId(), "permanent_limit", side, limits.getPermanentLimit(), limits.getLimitType(), identifiable.getType())); + temporaryLimitContexts.add(new TemporaryLimitData(identifiable.getId(), "permanent_limit", side, limits.getPermanentLimit(), + limits.getLimitType(), identifiable.getType(), groupId, isSelected)); limits.getTemporaryLimits().stream() .map(temporaryLimit -> new TemporaryLimitData(identifiable.getId(), temporaryLimit.getName(), side, temporaryLimit.getValue(), - limits.getLimitType(), identifiable.getType(), temporaryLimit.getAcceptableDuration(), temporaryLimit.isFictitious())) + limits.getLimitType(), identifiable.getType(), temporaryLimit.getAcceptableDuration(), temporaryLimit.isFictitious(), + groupId, isSelected)) .forEach(temporaryLimitContexts::add); } } @@ -234,4 +244,33 @@ public static String getRegulatedElementId(Supplier regulatingTerminal Terminal terminal = regulatingTerminalGetter.get(); return terminal.getConnectable() != null ? terminal.getConnectable().getId() : null; } + + /** + * @param b bus in Bus/Breaker view + * @return bus in bus view containing b if there is one. + */ + public static Optional getBusViewBus(Bus b) { + VoltageLevel voltageLevel = b.getVoltageLevel(); + if (voltageLevel.getTopologyKind() == TopologyKind.BUS_BREAKER) { + // Bus/Breaker. There is an easy method directly available. + return Optional.ofNullable(voltageLevel.getBusView().getMergedBus(b.getId())); + } else { + // Node/Breaker. + // First we try the fast and easy way using connected terminals. Works for the vast majority of buses. + Optional busInBusView = b.getConnectedTerminalStream().map(t -> t.getBusView().getBus()) + .filter(Objects::nonNull) + .findFirst(); + if (busInBusView.isPresent()) { + return busInBusView; + } + // Didn't find using connected terminals. There is the possibility that the bus has zero connected terminal + // on its own but is still part of a Merged Bus via a closed retained switch. We examine this case below. + // We should probably build something more efficient on powsybl-core side to avoid having + // to loop over all buses in the voltage level. + return voltageLevel.getBusView().getBusStream() + .filter(busViewBus -> voltageLevel.getBusBreakerView().getBusStreamFromBusViewBusId(busViewBus.getId()) + .anyMatch(b2 -> b.getId().equals(b2.getId()))) + .findFirst(); + } + } } diff --git a/java/src/main/java/com/powsybl/python/network/Networks.java b/java/src/main/java/com/powsybl/python/network/Networks.java index 6108512fea..307f80470e 100644 --- a/java/src/main/java/com/powsybl/python/network/Networks.java +++ b/java/src/main/java/com/powsybl/python/network/Networks.java @@ -24,7 +24,7 @@ /** * Networks factories, for "classic" networks or test network. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public final class Networks { @@ -98,6 +98,11 @@ public static Network createEurostagTutorialExample1() { return fix(network); } + public static Network createEurostagTutorialExampleWithMoreGenerators() { + Network network = EurostagTutorialExample1Factory.createWithMoreGenerators(); + return fix(network); + } + public static Network createEurostagTutorialExample1WithFixedCurrentLimits() { Network network = EurostagTutorialExample1Factory.createWithFixedCurrentLimits(); return fix(network); @@ -125,7 +130,7 @@ public static Network createEurostagTutorialExample1WithApcExtension() { network.getGenerator("GEN") .newExtension(ActivePowerControlAdder.class) .withParticipate(true) - .withDroop(1.1f) + .withDroop(1.1) .add(); return network; } @@ -142,6 +147,10 @@ public static Network eurostagWithEntsoeCategory() { public static Network eurostagWithTieLine() { Network network = EurostagTutorialExample1Factory.createWithTieLine(); return fix(network); + } + public static Network eurostagWithTieLinesAndAreas() { + Network network = EurostagTutorialExample1Factory.createWithTieLinesAndAreas(); + return fix(network); } } diff --git a/java/src/main/java/com/powsybl/python/network/NodeBreakerViewSwitchContext.java b/java/src/main/java/com/powsybl/python/network/NodeBreakerViewSwitchContext.java index 2d5b13623c..c54e075e21 100644 --- a/java/src/main/java/com/powsybl/python/network/NodeBreakerViewSwitchContext.java +++ b/java/src/main/java/com/powsybl/python/network/NodeBreakerViewSwitchContext.java @@ -12,7 +12,7 @@ import java.util.Objects; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class NodeBreakerViewSwitchContext { private final Switch switchContext; diff --git a/java/src/main/java/com/powsybl/python/network/NodeContext.java b/java/src/main/java/com/powsybl/python/network/NodeContext.java index 42eeae1b4f..fae0bea185 100644 --- a/java/src/main/java/com/powsybl/python/network/NodeContext.java +++ b/java/src/main/java/com/powsybl/python/network/NodeContext.java @@ -11,7 +11,7 @@ import java.util.Objects; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class NodeContext { private final int node; diff --git a/java/src/main/java/com/powsybl/python/network/OrNetworkPredicate.java b/java/src/main/java/com/powsybl/python/network/OrNetworkPredicate.java index f5b5c41c99..8f820640a8 100644 --- a/java/src/main/java/com/powsybl/python/network/OrNetworkPredicate.java +++ b/java/src/main/java/com/powsybl/python/network/OrNetworkPredicate.java @@ -16,7 +16,7 @@ import java.util.Objects; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ public class OrNetworkPredicate implements NetworkPredicate { diff --git a/java/src/main/java/com/powsybl/python/network/PyPowsyblNetworksProvider.java b/java/src/main/java/com/powsybl/python/network/PyPowsyblNetworksProvider.java index 47c48b809a..35f0330922 100644 --- a/java/src/main/java/com/powsybl/python/network/PyPowsyblNetworksProvider.java +++ b/java/src/main/java/com/powsybl/python/network/PyPowsyblNetworksProvider.java @@ -19,7 +19,7 @@ /** * Provides pypowsybl named networks. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ @AutoService(Networks.NetworksProvider.class) public class PyPowsyblNetworksProvider implements Networks.NetworksProvider { @@ -37,10 +37,12 @@ public class PyPowsyblNetworksProvider implements Networks.NetworksProvider { factory("four_substations_node_breaker", (Supplier) FourSubstationsNodeBreakerFactory::create), factory("four_substations_node_breaker_with_extensions", (Supplier) FourSubstationsNodeBreakerWithExtensionsFactory::create), factory("eurostag_tutorial_example1", Networks::createEurostagTutorialExample1WithFixedCurrentLimits), + factory("eurostag_tutorial_example1_with_more_generators", Networks::createEurostagTutorialExampleWithMoreGenerators), factory("eurostag_tutorial_example1_with_power_limits", Networks::createEurostagTutorialExample1WithFixedPowerLimits), factory("eurostag_tutorial_example1_with_apc_extension", Networks::createEurostagTutorialExample1WithApcExtension), factory("eurostag_tutorial_example1_with_entsoe_category", Networks::eurostagWithEntsoeCategory), factory("eurostag_tutorial_example1_with_tie_line", Networks::eurostagWithTieLine), + factory("eurostag_tutorial_example1_with_tie_lines_and_areas", Networks::eurostagWithTieLinesAndAreas), factory("batteries", (Supplier) BatteryNetworkFactory::create), factory("dangling_lines", (Supplier) DanglingLineNetworkFactory::create), factory("three_windings_transformer", (Supplier) ThreeWindingsTransformerNetworkFactory::create), diff --git a/java/src/main/java/com/powsybl/python/network/TemporaryLimitData.java b/java/src/main/java/com/powsybl/python/network/TemporaryLimitData.java index 78f88847cc..c25f6ed111 100644 --- a/java/src/main/java/com/powsybl/python/network/TemporaryLimitData.java +++ b/java/src/main/java/com/powsybl/python/network/TemporaryLimitData.java @@ -13,7 +13,7 @@ import java.util.Objects; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class TemporaryLimitData { @@ -25,6 +25,8 @@ public class TemporaryLimitData { private final double value; private final int acceptableDuration; private final boolean isFictitious; + private final String groupId; + private final boolean selected; public enum Side { NONE, @@ -34,7 +36,7 @@ public enum Side { } public TemporaryLimitData(String id, String name, Side side, double value, LimitType type, IdentifiableType elementType, - int acceptableDuration, boolean isFictitious) { + int acceptableDuration, boolean isFictitious, String groupId, boolean selected) { this.id = Objects.requireNonNull(id); this.name = Objects.requireNonNull(name); this.side = side; @@ -43,10 +45,13 @@ public TemporaryLimitData(String id, String name, Side side, double value, Limit this.value = value; this.acceptableDuration = acceptableDuration; this.isFictitious = isFictitious; + this.groupId = Objects.requireNonNull(groupId); + this.selected = selected; } - public TemporaryLimitData(String id, String name, Side side, double value, LimitType type, IdentifiableType elementType) { - this(id, name, side, value, type, elementType, -1, false); + public TemporaryLimitData(String id, String name, Side side, double value, LimitType type, IdentifiableType elementType, + String groupId, boolean isSelected) { + this(id, name, side, value, type, elementType, -1, false, groupId, isSelected); } public String getId() { @@ -80,4 +85,12 @@ public LimitType getType() { public boolean isFictitious() { return isFictitious; } + + public String getGroupId() { + return groupId; + } + + public boolean isSelected() { + return selected; + } } diff --git a/java/src/main/java/com/powsybl/python/network/ZipMemDataSource.java b/java/src/main/java/com/powsybl/python/network/ZipMemDataSource.java index 4b0658281a..733b837a26 100644 --- a/java/src/main/java/com/powsybl/python/network/ZipMemDataSource.java +++ b/java/src/main/java/com/powsybl/python/network/ZipMemDataSource.java @@ -7,8 +7,8 @@ */ package com.powsybl.python.network; -import com.powsybl.commons.datasource.DataSource; import com.powsybl.commons.datasource.DataSourceUtil; +import com.powsybl.commons.datasource.MemDataSource; import com.powsybl.commons.io.ForwardingOutputStream; import java.io.IOException; @@ -22,7 +22,7 @@ /** * @author Geoffroy Jamgotchian {@literal } */ -public class ZipMemDataSource implements DataSource { +public class ZipMemDataSource extends MemDataSource { private final String baseName; diff --git a/java/src/main/java/com/powsybl/python/report/ReportCFunctions.java b/java/src/main/java/com/powsybl/python/report/ReportCFunctions.java index 2860e41861..b6665070bc 100644 --- a/java/src/main/java/com/powsybl/python/report/ReportCFunctions.java +++ b/java/src/main/java/com/powsybl/python/report/ReportCFunctions.java @@ -27,7 +27,7 @@ import static com.powsybl.python.commons.Util.doCatch; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ @CContext(Directives.class) public final class ReportCFunctions { diff --git a/java/src/main/java/com/powsybl/python/report/ReportCUtils.java b/java/src/main/java/com/powsybl/python/report/ReportCUtils.java index 450ed38cd1..cefd0ee490 100644 --- a/java/src/main/java/com/powsybl/python/report/ReportCUtils.java +++ b/java/src/main/java/com/powsybl/python/report/ReportCUtils.java @@ -12,7 +12,7 @@ import org.graalvm.nativeimage.ObjectHandles; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public final class ReportCUtils { @@ -21,6 +21,7 @@ private ReportCUtils() { public static ReportNode getReportNode(ObjectHandle reportNodeHandle) { ReportNode reportNode = ObjectHandles.getGlobal().get(reportNodeHandle); + return reportNode != null ? reportNode : ReportNode.NO_OP; } } diff --git a/java/src/main/java/com/powsybl/python/security/BranchResultContext.java b/java/src/main/java/com/powsybl/python/security/BranchResultContext.java index 4fb85ec589..72a19a6329 100644 --- a/java/src/main/java/com/powsybl/python/security/BranchResultContext.java +++ b/java/src/main/java/com/powsybl/python/security/BranchResultContext.java @@ -10,7 +10,7 @@ import com.powsybl.security.results.BranchResult; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class BranchResultContext extends BranchResult { diff --git a/java/src/main/java/com/powsybl/python/security/BusResultContext.java b/java/src/main/java/com/powsybl/python/security/BusResultContext.java index aca7d7c52a..06163a2578 100644 --- a/java/src/main/java/com/powsybl/python/security/BusResultContext.java +++ b/java/src/main/java/com/powsybl/python/security/BusResultContext.java @@ -10,7 +10,7 @@ import com.powsybl.security.results.BusResult; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class BusResultContext extends BusResult { diff --git a/java/src/main/java/com/powsybl/python/security/LimitViolationContext.java b/java/src/main/java/com/powsybl/python/security/LimitViolationContext.java index 66df583389..941577ad65 100644 --- a/java/src/main/java/com/powsybl/python/security/LimitViolationContext.java +++ b/java/src/main/java/com/powsybl/python/security/LimitViolationContext.java @@ -5,7 +5,7 @@ import java.util.Objects; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class LimitViolationContext extends LimitViolation { diff --git a/java/src/main/java/com/powsybl/python/security/SecurityAnalysisCFunctions.java b/java/src/main/java/com/powsybl/python/security/SecurityAnalysisCFunctions.java index 87f150ded6..4e8fbb051d 100644 --- a/java/src/main/java/com/powsybl/python/security/SecurityAnalysisCFunctions.java +++ b/java/src/main/java/com/powsybl/python/security/SecurityAnalysisCFunctions.java @@ -51,7 +51,7 @@ /** * C functions related to security analysis. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ @CContext(Directives.class) public final class SecurityAnalysisCFunctions { diff --git a/java/src/main/java/com/powsybl/python/security/SecurityAnalysisCUtils.java b/java/src/main/java/com/powsybl/python/security/SecurityAnalysisCUtils.java index 41d92ccc05..a9cd677a61 100644 --- a/java/src/main/java/com/powsybl/python/security/SecurityAnalysisCUtils.java +++ b/java/src/main/java/com/powsybl/python/security/SecurityAnalysisCUtils.java @@ -19,7 +19,7 @@ import java.util.Map; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public final class SecurityAnalysisCUtils { diff --git a/java/src/main/java/com/powsybl/python/security/ThreeWindingsTransformerResultContext.java b/java/src/main/java/com/powsybl/python/security/ThreeWindingsTransformerResultContext.java index c4b1f8dbe9..4c0e29f0a9 100644 --- a/java/src/main/java/com/powsybl/python/security/ThreeWindingsTransformerResultContext.java +++ b/java/src/main/java/com/powsybl/python/security/ThreeWindingsTransformerResultContext.java @@ -10,7 +10,7 @@ import com.powsybl.security.results.ThreeWindingsTransformerResult; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public class ThreeWindingsTransformerResultContext extends ThreeWindingsTransformerResult { diff --git a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCFunctions.java b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCFunctions.java index 9b436c8511..3ca2a9143e 100644 --- a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCFunctions.java +++ b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCFunctions.java @@ -43,7 +43,7 @@ /** * C functions related to sensitivity analysis. * - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ @CContext(Directives.class) public final class SensitivityAnalysisCFunctions { diff --git a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCUtils.java b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCUtils.java index 2f41d02a19..92676b4d57 100644 --- a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCUtils.java +++ b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCUtils.java @@ -19,7 +19,7 @@ import java.util.Map; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ public final class SensitivityAnalysisCUtils { diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/FortescueFeederResultContext.java b/java/src/main/java/com/powsybl/python/shortcircuit/FortescueFeederResultContext.java index 8613cdea63..413c9a764a 100644 --- a/java/src/main/java/com/powsybl/python/shortcircuit/FortescueFeederResultContext.java +++ b/java/src/main/java/com/powsybl/python/shortcircuit/FortescueFeederResultContext.java @@ -19,7 +19,7 @@ public class FortescueFeederResultContext extends FortescueFeederResult { private final String faultId; public FortescueFeederResultContext(String faultId, FortescueFeederResult fortescueFeederResult) { - super(fortescueFeederResult.getConnectableId(), fortescueFeederResult.getCurrent()); + super(fortescueFeederResult.getConnectableId(), fortescueFeederResult.getCurrent(), fortescueFeederResult.getSide()); this.faultId = Objects.requireNonNull(faultId); } diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/LimitViolationFaultContext.java b/java/src/main/java/com/powsybl/python/shortcircuit/LimitViolationFaultContext.java index 6521a12103..6e0f022697 100644 --- a/java/src/main/java/com/powsybl/python/shortcircuit/LimitViolationFaultContext.java +++ b/java/src/main/java/com/powsybl/python/shortcircuit/LimitViolationFaultContext.java @@ -12,7 +12,7 @@ import java.util.Objects; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public class LimitViolationFaultContext extends LimitViolation { diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/MagnitudeBusResultsContext.java b/java/src/main/java/com/powsybl/python/shortcircuit/MagnitudeBusResultsContext.java index 978b8aa26e..ca4d6a6ead 100644 --- a/java/src/main/java/com/powsybl/python/shortcircuit/MagnitudeBusResultsContext.java +++ b/java/src/main/java/com/powsybl/python/shortcircuit/MagnitudeBusResultsContext.java @@ -12,7 +12,7 @@ import java.util.Objects; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public class MagnitudeBusResultsContext extends MagnitudeShortCircuitBusResults { diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/MagnitudeFeederResultContext.java b/java/src/main/java/com/powsybl/python/shortcircuit/MagnitudeFeederResultContext.java index 6302c0990e..f453bf7b0c 100644 --- a/java/src/main/java/com/powsybl/python/shortcircuit/MagnitudeFeederResultContext.java +++ b/java/src/main/java/com/powsybl/python/shortcircuit/MagnitudeFeederResultContext.java @@ -12,13 +12,13 @@ import java.util.Objects; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public class MagnitudeFeederResultContext extends MagnitudeFeederResult { private final String faultId; public MagnitudeFeederResultContext(String faultId, MagnitudeFeederResult result) { - super(result.getConnectableId(), result.getCurrent()); + super(result.getConnectableId(), result.getCurrent(), result.getSide()); this.faultId = Objects.requireNonNull(faultId); } diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCFunctions.java b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCFunctions.java index 1d3087b7ff..b1d667beb0 100644 --- a/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCFunctions.java +++ b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCFunctions.java @@ -42,7 +42,7 @@ /** * C functions related to short-circuit analysis. * - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ @CContext(Directives.class) public final class ShortCircuitAnalysisCFunctions { diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCUtils.java b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCUtils.java index b7edd8bb4c..612e18caa4 100644 --- a/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCUtils.java +++ b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCUtils.java @@ -20,7 +20,7 @@ import java.util.Map; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public final class ShortCircuitAnalysisCUtils { diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisContext.java b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisContext.java index 423e86051a..f692a669e2 100644 --- a/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisContext.java +++ b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisContext.java @@ -16,7 +16,7 @@ import java.util.List; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ public class ShortCircuitAnalysisContext { diff --git a/java/src/main/java/com/powsybl/python/voltageinit/VoltageInitializerCFunctions.java b/java/src/main/java/com/powsybl/python/voltageinit/VoltageInitializerCFunctions.java index 889687177e..6bcba7b69b 100644 --- a/java/src/main/java/com/powsybl/python/voltageinit/VoltageInitializerCFunctions.java +++ b/java/src/main/java/com/powsybl/python/voltageinit/VoltageInitializerCFunctions.java @@ -39,7 +39,7 @@ import com.powsybl.python.commons.Util; /** - * @author Nicolas Pierre + * @author Nicolas Pierre {@literal } */ @CContext(Directives.class) public final class VoltageInitializerCFunctions { diff --git a/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-dynawo-simulation/reflect-config.json b/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-dynawo-simulation/reflect-config.json new file mode 100644 index 0000000000..62e00d74ce --- /dev/null +++ b/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-dynawo-simulation/reflect-config.json @@ -0,0 +1,26 @@ +[ + { + "name":"com.powsybl.dynawo.DynawoSimulationParameters", + "queryAllDeclaredConstructors" : true, + "queryAllPublicConstructors" : true, + "queryAllDeclaredMethods" : true, + "queryAllPublicMethods" : true, + "allDeclaredClasses" : true, + "allDeclaredFields" : true, + "allPublicClasses" : true, + "allPublicMethods" : true, + "allPublicConstructors" : true + }, + { + "name":"com.powsybl.dynawo.builders.ModelConfigsHandler", + "queryAllDeclaredConstructors" : true, + "queryAllPublicConstructors" : true, + "queryAllDeclaredMethods" : true, + "queryAllPublicMethods" : true, + "allDeclaredClasses" : true, + "allDeclaredFields" : true, + "allPublicClasses" : true, + "allPublicMethods" : true, + "allPublicConstructors" : true + } +] diff --git a/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-dynawo-simulation/resource-config.json b/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-dynawo-simulation/resource-config.json new file mode 100644 index 0000000000..1d3eb5757f --- /dev/null +++ b/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-dynawo-simulation/resource-config.json @@ -0,0 +1,8 @@ +{ + "resources":{ + "includes":[ + {"pattern":"\\QMETA-INF/services/com.powsybl.dynamicsimulation.DynamicSimulationProvider\\E"}, + {"pattern":"\\QMETA-INF/services/com.powsybl.dynawo.builders.ModelConfigLoader\\E"}, + {"pattern":"\\Qmodels.json\\E"} + ]} +} diff --git a/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-loadflow-api/resource-config.json b/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-loadflow-api/resource-config.json index d808a4fa65..5684be5d74 100644 --- a/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-loadflow-api/resource-config.json +++ b/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-loadflow-api/resource-config.json @@ -2,6 +2,7 @@ "resources":{ "includes":[ {"pattern":"\\QMETA-INF/services/com.powsybl.loadflow.LoadFlowParameters$ConfigLoader\\E"}, - {"pattern":"\\QMETA-INF/services/com.powsybl.loadflow.LoadFlowProvider\\E"} + {"pattern":"\\QMETA-INF/services/com.powsybl.loadflow.LoadFlowProvider\\E"}, + {"pattern":"\\QMETA-INF/services/com.powsybl.loadflow.LoadFlowDefaultParametersLoader\\E"} ]} } diff --git a/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-network-area-diagram/reflect-config.json b/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-network-area-diagram/reflect-config.json new file mode 100644 index 0000000000..a89a1fdb63 --- /dev/null +++ b/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-network-area-diagram/reflect-config.json @@ -0,0 +1,80 @@ +[ + { + "name":"com.powsybl.nad.svg.metadata.DiagramMetadata", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.svg.metadata.AbstractMetadataItem", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.svg.metadata.BusNodeMetadata", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.svg.metadata.EdgeMetadata", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.svg.metadata.NodeMetadata", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.svg.metadata.TextNodeMetadata", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.layout.LayoutParameters", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.model.Point", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.svg.SvgParameters", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.svg.Padding", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.svg.SvgParameters$CssLocation", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.svg.SvgParameters$SizeConstraint", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + }, + { + "name":"com.powsybl.nad.svg.SvgParameters$EdgeInfoEnum", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true + } +] \ No newline at end of file diff --git a/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-single-line-diagram-core/reflect-config.json b/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-single-line-diagram-core/reflect-config.json index fc1e29e0e6..f1ba56da50 100644 --- a/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-single-line-diagram-core/reflect-config.json +++ b/java/src/main/resources/META-INF/native-image/com.powsybl/powsybl-single-line-diagram-core/reflect-config.json @@ -99,7 +99,7 @@ "allDeclaredConstructors":true }, { - "name":"com.powsybl.sld.svg.GraphMetadata$ElectricalNodeInfoMetadata", + "name":"com.powsybl.sld.svg.GraphMetadata$BusLegendInfoMetadata", "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true diff --git a/java/src/test/java/com/powsybl/dataframe/DataframeMapperBuilderTest.java b/java/src/test/java/com/powsybl/dataframe/DataframeMapperBuilderTest.java index 2116d262bb..c880aa7801 100644 --- a/java/src/test/java/com/powsybl/dataframe/DataframeMapperBuilderTest.java +++ b/java/src/test/java/com/powsybl/dataframe/DataframeMapperBuilderTest.java @@ -25,7 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ class DataframeMapperBuilderTest { diff --git a/java/src/test/java/com/powsybl/dataframe/dynamic/adders/DynamicModelsAdderTest.java b/java/src/test/java/com/powsybl/dataframe/dynamic/adders/DynamicModelsAdderTest.java new file mode 100644 index 0000000000..305b4755ca --- /dev/null +++ b/java/src/test/java/com/powsybl/dataframe/dynamic/adders/DynamicModelsAdderTest.java @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.dataframe.update.DefaultUpdatingDataframe; +import com.powsybl.dataframe.update.TestStringSeries; +import com.powsybl.dynawo.models.AbstractPureDynamicBlackBoxModel; +import com.powsybl.dynawo.models.TransformerSide; +import com.powsybl.dynawo.models.automationsystems.TapChangerBlockingAutomationSystem; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoSides; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import com.powsybl.iidm.network.test.HvdcTestNetwork; +import com.powsybl.iidm.network.test.SvcTestCaseFactory; +import com.powsybl.python.commons.PyPowsyblApiHeader; +import com.powsybl.python.dynamic.PythonDynamicModelsSupplier; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.python.commons.PyPowsyblApiHeader.DynamicMappingType.*; +import static com.powsybl.python.commons.PyPowsyblApiHeader.DynamicMappingType.BASE_TRANSFORMER; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Laurent Issertial {@literal } + */ +class DynamicModelsAdderTest { + + private static final String DEFAULT_SUFFIX = "_DEFAULT"; + private DefaultUpdatingDataframe dataframe; + private PythonDynamicModelsSupplier dynamicModelsSupplier; + + @BeforeEach + void setup() { + dataframe = new DefaultUpdatingDataframe(2); + dynamicModelsSupplier = new PythonDynamicModelsSupplier(); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("equipmentDataProvider") + void testEquipmentsAdder(PyPowsyblApiHeader.DynamicMappingType mappingType, Network network, String staticId) { + String expectedModelName = DynamicMappingHandler.getSupportedModels(mappingType).stream().findFirst().orElse(""); + dataframe.addSeries(STATIC_ID, true, createTwoRowsSeries(staticId)); + dataframe.addSeries(DYNAMIC_MODEL_ID, false, createTwoRowsSeries("BBM" + staticId)); + dataframe.addSeries(PARAMETER_SET_ID, false, createTwoRowsSeries("eq_par")); + dataframe.addSeries(MODEL_NAME, false, new TestStringSeries(expectedModelName, "")); + DynamicMappingHandler.addElements(mappingType, dynamicModelsSupplier, List.of(dataframe)); + + assertThat(dynamicModelsSupplier.get(network)).satisfiesExactly( + model1 -> assertThat(model1).hasFieldOrPropertyWithValue("dynamicModelId", "BBM" + staticId) + .hasFieldOrPropertyWithValue("lib", expectedModelName), + model2 -> assertThat(model2).hasFieldOrPropertyWithValue("dynamicModelId", "BBM" + staticId)); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("automationSystemProvider") + void testAutomationSystemAdders(PyPowsyblApiHeader.DynamicMappingType mappingType, Consumer updateDataframe) { + String expectedModelName = DynamicMappingHandler.getSupportedModels(mappingType).stream().findFirst().orElse(""); + Network network = EurostagTutorialExample1Factory.createWithLFResults(); + String dynamicModelId = "BBM_automation_system"; + setupDataFrame(dataframe, dynamicModelId, expectedModelName); + updateDataframe.accept(dataframe); + DynamicMappingHandler.addElements(mappingType, dynamicModelsSupplier, List.of(dataframe)); + + assertThat(dynamicModelsSupplier.get(network)).satisfiesExactly( + model1 -> assertThat(model1).hasFieldOrPropertyWithValue("dynamicModelId", dynamicModelId) + .isInstanceOf(AbstractPureDynamicBlackBoxModel.class), + model2 -> assertThat(model2).hasFieldOrPropertyWithValue("dynamicModelId", dynamicModelId + DEFAULT_SUFFIX) + .isInstanceOf(AbstractPureDynamicBlackBoxModel.class)); + } + + @Test + void testTapChangerBlockingAdders() { + String expectedModelName = DynamicMappingHandler.getSupportedModels(TAP_CHANGER_BLOCKING).stream().findFirst().orElse(""); + Network network = EurostagTutorialExample1Factory.createWithLFResults(); + String dynamicModelId = "BBM_TCB"; + String defaultDynamicModelId = dynamicModelId + DEFAULT_SUFFIX; + // Setup Tcb df + setupDataFrame(dataframe, dynamicModelId, expectedModelName); + // Setup Tfo df + DefaultUpdatingDataframe tfoDataFrame = new DefaultUpdatingDataframe(3); + tfoDataFrame.addSeries(DYNAMIC_MODEL_ID, true, new TestStringSeries(dynamicModelId, dynamicModelId, defaultDynamicModelId)); + tfoDataFrame.addSeries(TRANSFORMER_ID, false, new TestStringSeries("NGEN_NHV1", "NHV2_NLOAD", "NHV2_NLOAD")); + // Setup measurement points df + DefaultUpdatingDataframe m1DataFrame = new DefaultUpdatingDataframe(3); + m1DataFrame.addSeries(DYNAMIC_MODEL_ID, true, new TestStringSeries(dynamicModelId, dynamicModelId, defaultDynamicModelId)); + m1DataFrame.addSeries(MEASUREMENT_POINT_ID, false, new TestStringSeries("BBS_NGEN", "NGEN", "NHV1")); + DefaultUpdatingDataframe m2DataFrame = new DefaultUpdatingDataframe(2); + m2DataFrame.addSeries(DYNAMIC_MODEL_ID, true, new TestStringSeries(dynamicModelId, dynamicModelId)); + m2DataFrame.addSeries(MEASUREMENT_POINT_ID, false, new TestStringSeries("OLD_NLOAD_ID", "NLOAD")); + DynamicMappingHandler.addElements(TAP_CHANGER_BLOCKING, dynamicModelsSupplier, List.of(dataframe, tfoDataFrame, m1DataFrame, m2DataFrame)); + + assertThat(dynamicModelsSupplier.get(network)).satisfiesExactly( + model1 -> assertThat(model1).hasFieldOrPropertyWithValue("dynamicModelId", dynamicModelId) + .isInstanceOf(TapChangerBlockingAutomationSystem.class) + .extracting("uMeasurements") + .asInstanceOf(InstanceOfAssertFactories.LIST) + .containsExactly(network.getBusBreakerView().getBus("NGEN"), network.getBusBreakerView().getBus("NLOAD")), + model2 -> assertThat(model2).hasFieldOrPropertyWithValue("dynamicModelId", defaultDynamicModelId) + .isInstanceOf(TapChangerBlockingAutomationSystem.class) + .extracting("uMeasurements") + .asInstanceOf(InstanceOfAssertFactories.LIST) + .containsExactly(network.getBusBreakerView().getBus("NHV1"))); + } + + @Test + void testIncompleteDataFrame() { + Network network = EurostagTutorialExample1Factory.create(); + DefaultUpdatingDataframe missingStaticDF = new DefaultUpdatingDataframe(1); + missingStaticDF.addSeries(PARAMETER_SET_ID, false, new TestStringSeries("eq_par")); + DynamicMappingHandler.addElements(BASE_LOAD, dynamicModelsSupplier, List.of(missingStaticDF)); + DefaultUpdatingDataframe missingParamDF = new DefaultUpdatingDataframe(1); + missingParamDF.addSeries(STATIC_ID, false, new TestStringSeries("LOAD")); + DynamicMappingHandler.addElements(BASE_LOAD, dynamicModelsSupplier, List.of(missingParamDF)); + assertThat(dynamicModelsSupplier.get(network)).isEmpty(); + } + + @Test + void testWrongModelName() { + Network network = EurostagTutorialExample1Factory.create(); + DefaultUpdatingDataframe wrongModelNameDF = new DefaultUpdatingDataframe(1); + wrongModelNameDF.addSeries(STATIC_ID, false, new TestStringSeries("LOAD")); + wrongModelNameDF.addSeries(PARAMETER_SET_ID, false, new TestStringSeries("eq_par")); + wrongModelNameDF.addSeries(MODEL_NAME, false, new TestStringSeries("wrongModelName")); + DynamicMappingHandler.addElements(BASE_LOAD, dynamicModelsSupplier, List.of(wrongModelNameDF)); + assertThat(dynamicModelsSupplier.get(network)).isEmpty(); + } + + static Stream equipmentDataProvider() { + return Stream.of( + Arguments.of(BASE_LOAD, EurostagTutorialExample1Factory.create(), "LOAD"), + Arguments.of(LOAD_ONE_TRANSFORMER, EurostagTutorialExample1Factory.create(), "LOAD"), + Arguments.of(LOAD_ONE_TRANSFORMER_TAP_CHANGER, EurostagTutorialExample1Factory.create(), "LOAD"), + Arguments.of(LOAD_TWO_TRANSFORMERS, EurostagTutorialExample1Factory.create(), "LOAD"), + Arguments.of(LOAD_TWO_TRANSFORMERS_TAP_CHANGERS, EurostagTutorialExample1Factory.create(), "LOAD"), + Arguments.of(BASE_GENERATOR, EurostagTutorialExample1Factory.create(), "GEN"), + Arguments.of(SYNCHRONIZED_GENERATOR, EurostagTutorialExample1Factory.create(), "GEN"), + Arguments.of(SYNCHRONOUS_GENERATOR, EurostagTutorialExample1Factory.create(), "GEN"), + Arguments.of(WECC, EurostagTutorialExample1Factory.create(), "GEN"), + Arguments.of(GRID_FORMING_CONVERTER, EurostagTutorialExample1Factory.create(), "GEN"), + Arguments.of(SIGNAL_N_GENERATOR, EurostagTutorialExample1Factory.create(), "GEN"), + Arguments.of(BASE_TRANSFORMER, EurostagTutorialExample1Factory.create(), "NGEN_NHV1"), + Arguments.of(BASE_STATIC_VAR_COMPENSATOR, SvcTestCaseFactory.create(), "SVC2"), + Arguments.of(BASE_LINE, EurostagTutorialExample1Factory.create(), "NHV1_NHV2_1"), + Arguments.of(BASE_BUS, EurostagTutorialExample1Factory.create(), "NHV1"), + Arguments.of(INFINITE_BUS, EurostagTutorialExample1Factory.create(), "NHV1"), + Arguments.of(HVDC_P, HvdcTestNetwork.createVsc(), "L"), + Arguments.of(HVDC_VSC, HvdcTestNetwork.createVsc(), "L") + ); + } + + static Stream automationSystemProvider() { + return Stream.of( + Arguments.of(OVERLOAD_MANAGEMENT_SYSTEM, + (Consumer) df -> { + String lineId = "NGEN_NHV1"; + df.addSeries(CONTROLLED_BRANCH, false, createTwoRowsSeries(lineId)); + df.addSeries(I_MEASUREMENT, false, createTwoRowsSeries(lineId)); + df.addSeries(I_MEASUREMENT_SIDE, false, createTwoRowsSeries(TwoSides.ONE.toString())); + }), + Arguments.of(TWO_LEVELS_OVERLOAD_MANAGEMENT_SYSTEM, + (Consumer) df -> { + String lineId = "NGEN_NHV1"; + df.addSeries(CONTROLLED_BRANCH, false, createTwoRowsSeries(lineId)); + df.addSeries(I_MEASUREMENT_1, false, createTwoRowsSeries("NHV1_NHV2_1")); + df.addSeries(I_MEASUREMENT_1_SIDE, false, createTwoRowsSeries(TwoSides.ONE.toString())); + df.addSeries(I_MEASUREMENT_2, false, createTwoRowsSeries("NHV1_NHV2_2")); + df.addSeries(I_MEASUREMENT_2_SIDE, false, createTwoRowsSeries(TwoSides.ONE.toString())); + }), + Arguments.of(PHASE_SHIFTER_I, + (Consumer) df -> df.addSeries(DynamicModelDataframeConstants.TRANSFORMER, false, createTwoRowsSeries("NGEN_NHV1"))), + Arguments.of(PHASE_SHIFTER_P, + (Consumer) df -> df.addSeries(DynamicModelDataframeConstants.TRANSFORMER, false, createTwoRowsSeries("NGEN_NHV1"))), + Arguments.of(PHASE_SHIFTER_BLOCKING_I, + (Consumer) df -> df.addSeries(PHASE_SHIFTER_ID, false, createTwoRowsSeries("PSI"))), + Arguments.of(TAP_CHANGER, + (Consumer) df -> { + df.addSeries(STATIC_ID, false, createTwoRowsSeries("LOAD")); + df.addSeries(SIDE, false, createTwoRowsSeries(TransformerSide.LOW_VOLTAGE.toString())); + }), + Arguments.of(TAP_CHANGER, + (Consumer) df -> df.addSeries(STATIC_ID, false, createTwoRowsSeries("LOAD"))), + Arguments.of(UNDER_VOLTAGE, + (Consumer) df -> df.addSeries(GENERATOR, false, createTwoRowsSeries("GEN"))) + ); + } + + private static void setupDataFrame(DefaultUpdatingDataframe dataframe, String dynamicModelId, String modelName) { + dataframe.addSeries(DYNAMIC_MODEL_ID, true, new TestStringSeries(dynamicModelId, dynamicModelId + DEFAULT_SUFFIX)); + dataframe.addSeries(PARAMETER_SET_ID, false, createTwoRowsSeries("as_par")); + dataframe.addSeries(MODEL_NAME, false, new TestStringSeries(modelName, "")); + } + + private static TestStringSeries createTwoRowsSeries(String value) { + return new TestStringSeries(value, value); + } +} diff --git a/java/src/test/java/com/powsybl/dataframe/dynamic/adders/EventModelsAdderTest.java b/java/src/test/java/com/powsybl/dataframe/dynamic/adders/EventModelsAdderTest.java new file mode 100644 index 0000000000..6c06634f9c --- /dev/null +++ b/java/src/test/java/com/powsybl/dataframe/dynamic/adders/EventModelsAdderTest.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic.adders; + +import com.powsybl.dataframe.update.DefaultUpdatingDataframe; +import com.powsybl.dataframe.update.TestDoubleSeries; +import com.powsybl.dataframe.update.TestStringSeries; +import com.powsybl.dynawo.models.events.AbstractEvent; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoSides; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import com.powsybl.python.dynamic.PythonEventModelsSupplier; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.function.Consumer; +import java.util.stream.Stream; + +import static com.powsybl.dataframe.dynamic.adders.DynamicModelDataframeConstants.*; +import static com.powsybl.python.commons.PyPowsyblApiHeader.EventMappingType; +import static com.powsybl.python.commons.PyPowsyblApiHeader.EventMappingType.*; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Laurent Issertial {@literal } + */ +class EventModelsAdderTest { + + private DefaultUpdatingDataframe dataframe; + private PythonEventModelsSupplier eventModelsSupplier; + + @BeforeEach + void setup() { + dataframe = new DefaultUpdatingDataframe(1); + eventModelsSupplier = new PythonEventModelsSupplier(); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("eventProvider") + void testEventAdders(EventMappingType mappingType, Consumer updateDataframe) { + Network network = EurostagTutorialExample1Factory.create(); + dataframe.addSeries(START_TIME, false, new TestDoubleSeries(10)); + updateDataframe.accept(dataframe); + EventMappingHandler.addElements(mappingType, eventModelsSupplier, dataframe); + + assertThat(eventModelsSupplier.get(network)).satisfiesExactly( + model1 -> assertThat(model1).isInstanceOf(AbstractEvent.class)); + } + + static Stream eventProvider() { + return Stream.of( + Arguments.of(DISCONNECT, + (Consumer) df -> { + df.addSeries(STATIC_ID, false, new TestStringSeries("NHV1_NHV2_1")); + df.addSeries(DISCONNECT_ONLY, false, new TestStringSeries(TwoSides.ONE.toString())); + }), + Arguments.of(DISCONNECT, + (Consumer) df -> df.addSeries(STATIC_ID, false, new TestStringSeries("GEN"))), + Arguments.of(ACTIVE_POWER_VARIATION, + (Consumer) df -> { + df.addSeries(STATIC_ID, false, new TestStringSeries("GEN")); + df.addSeries(DELTA_P, false, new TestDoubleSeries(1.3)); + }), + Arguments.of(NODE_FAULT, + (Consumer) df -> { + df.addSeries(STATIC_ID, false, new TestStringSeries("NLOAD")); + df.addSeries(FAULT_TIME, false, new TestDoubleSeries(0.1)); + df.addSeries(R_PU, false, new TestDoubleSeries(0)); + df.addSeries(X_PU, false, new TestDoubleSeries(0.01)); + }) + ); + } +} diff --git a/java/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java b/java/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java index 2c65ba5bc0..71da98e788 100644 --- a/java/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java +++ b/java/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java @@ -8,27 +8,21 @@ package com.powsybl.dataframe.network; import com.google.common.collect.ImmutableMap; +import com.powsybl.cgmes.extensions.CgmesMetadataModels; +import com.powsybl.cgmes.extensions.CgmesMetadataModelsAdder; import com.powsybl.dataframe.DataframeElementType; import com.powsybl.dataframe.DataframeFilter; import com.powsybl.dataframe.DoubleIndexedSeries; import com.powsybl.dataframe.impl.DefaultDataframeHandler; import com.powsybl.dataframe.impl.Series; import com.powsybl.dataframe.network.extensions.NetworkExtensions; -import com.powsybl.dataframe.update.DefaultUpdatingDataframe; -import com.powsybl.dataframe.update.TestDoubleSeries; -import com.powsybl.dataframe.update.TestStringSeries; -import com.powsybl.dataframe.update.UpdatingDataframe; -import com.powsybl.iidm.network.Generator; -import com.powsybl.iidm.network.HvdcLine; -import com.powsybl.iidm.network.Line; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.extensions.Coordinate; -import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControlAdder; -import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRangeAdder; -import com.powsybl.iidm.network.extensions.LinePositionAdder; -import com.powsybl.iidm.network.extensions.SecondaryVoltageControlAdder; +import com.powsybl.dataframe.update.*; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.*; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.iidm.network.test.HvdcTestNetwork; +import com.powsybl.iidm.network.test.TwoVoltageLevelNetworkFactory; +import com.powsybl.python.network.NetworkUtilTest; import com.powsybl.python.network.Networks; import org.junit.jupiter.api.Test; @@ -39,14 +33,15 @@ import java.util.function.Function; import java.util.stream.Collectors; +import static com.powsybl.cgmes.model.CgmesSubset.EQUIPMENT; +import static com.powsybl.cgmes.model.CgmesSubset.TOPOLOGY; import static com.powsybl.dataframe.DataframeElementType.*; import static com.powsybl.dataframe.DataframeFilter.AttributeFilterType.ALL_ATTRIBUTES; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ class NetworkDataframesTest { @@ -107,16 +102,64 @@ void buses() { .extracting(Series::getName) .containsExactly("id", "name", "v_mag", "v_angle", "connected_component", "synchronous_component", "voltage_level_id"); + assertThat(series.get(0).getStrings()) + .containsExactly("VLGEN_0", "VLHV1_0", "VLHV2_0", "VLLOAD_0"); assertThat(series.get(2).getDoubles()) .containsExactly(Double.NaN, Double.NaN, Double.NaN, Double.NaN); assertThat(series.get(4).getInts()) .containsExactly(0, 0, 0, 0); - assertThat(series.get(4).getInts()) + assertThat(series.get(5).getInts()) .containsExactly(0, 0, 0, 0); assertThat(series.get(6).getStrings()) .containsExactly("VLGEN", "VLHV1", "VLHV2", "VLLOAD"); } + @Test + void busBreakerViewBuses() { + // VL1 is Node/Breaker, VL2 is Bus/Breaker + Network network = TwoVoltageLevelNetworkFactory.create(); + List series = createDataFrame(BUS_FROM_BUS_BREAKER_VIEW, network); + assertThat(series) + .extracting(Series::getName) + .containsExactly("id", "name", "v_mag", "v_angle", "connected_component", "synchronous_component", + "voltage_level_id", "bus_id"); + assertThat(series.get(0).getStrings()) + .containsExactly("BUS1", "BUS2", "VL1_0", "VL1_3"); + assertThat(series.get(2).getDoubles()) + .containsExactly(Double.NaN, Double.NaN, Double.NaN, Double.NaN); + assertThat(series.get(4).getInts()) + .containsExactly(0, 0, 1, -99999); + assertThat(series.get(5).getInts()) + .containsExactly(0, 0, 1, -99999); + assertThat(series.get(6).getStrings()) + .containsExactly("VL2", "VL2", "VL1", "VL1"); + assertThat(series.get(7).getStrings()) + .containsExactly("VL2_0", "VL2_0", "VL1_0", ""); + } + + @Test + void busBreakerViewBusesNoConnectedTerminalOnBus() { + Network network = NetworkUtilTest.createTopologyTestNetwork(); + + List series = createDataFrame(BUS_FROM_BUS_BREAKER_VIEW, network); + assertThat(series) + .extracting(Series::getName) + .containsExactly("id", "name", "v_mag", "v_angle", "connected_component", "synchronous_component", + "voltage_level_id", "bus_id"); + assertThat(series.get(0).getStrings()) + .containsExactly("B1", "B2", "B3", "VL2_0", "VL2_1", "VL2_2"); + assertThat(series.get(2).getDoubles()) + .containsExactly(Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN); + assertThat(series.get(4).getInts()) + .containsExactly(0, 0, -99999, 1, 1, -99999); + assertThat(series.get(5).getInts()) + .containsExactly(0, 0, -99999, 1, 1, -99999); + assertThat(series.get(6).getStrings()) + .containsExactly("VL1", "VL1", "VL1", "VL2", "VL2", "VL2"); + assertThat(series.get(7).getStrings()) + .containsExactly("VL1_0", "VL1_0", "", "VL2_0", "VL2_0", ""); + } + @Test void generators() { Network network = EurostagTutorialExample1Factory.create(); @@ -163,24 +206,45 @@ void generatorsExtension() { List series = createExtensionDataFrame("activePowerControl", network); assertThat(series) .extracting(Series::getName) - .containsExactly("id", "droop", "participate"); + .containsExactly("id", "droop", "participate", + "participation_factor", + "max_target_p", + "min_target_p"); assertThat(series.get(1).getDoubles()) - .containsExactly(1.1f); + .containsExactly(1.1); assertThat(series.get(2).getBooleans()) .containsExactly(true); + assertThat(series.get(3).getDoubles()) + .containsExactly(Double.NaN); + assertThat(series.get(4).getDoubles()) + .containsExactly(Double.NaN); + assertThat(series.get(5).getDoubles()) + .containsExactly(Double.NaN); DefaultUpdatingDataframe dataframe = new DefaultUpdatingDataframe(1); dataframe.addSeries("id", true, new TestStringSeries("GEN")); dataframe.addSeries("droop", false, new TestDoubleSeries(1.2)); + dataframe.addSeries("participation_factor", false, new TestDoubleSeries(1.5)); + dataframe.addSeries("max_target_p", false, new TestDoubleSeries(900.)); + dataframe.addSeries("min_target_p", false, new TestDoubleSeries(200.)); updateExtension("activePowerControl", network, dataframe); series = createExtensionDataFrame("activePowerControl", network); assertThat(series) .extracting(Series::getName) - .containsExactly("id", "droop", "participate"); + .containsExactly("id", "droop", "participate", + "participation_factor", + "max_target_p", + "min_target_p"); assertThat(series.get(1).getDoubles()) - .containsExactly(1.2f); + .containsExactly(1.2); assertThat(series.get(2).getBooleans()) .containsExactly(true); + assertThat(series.get(3).getDoubles()) + .containsExactly(1.5); + assertThat(series.get(4).getDoubles()) + .containsExactly(900.); + assertThat(series.get(5).getDoubles()) + .containsExactly(200.); NetworkExtensions.removeExtensions(network, "activePowerControl", network.getGeneratorStream().map(Generator::getNameOrId).collect(Collectors.toList())); @@ -269,7 +333,19 @@ void danglingLines() { .extracting(Series::getName) .containsExactly("id", "name", "r", "x", "g", "b", "p0", "q0", "p", "q", "i", "boundary_p", "boundary_q", "boundary_v_mag", "boundary_v_angle", - "voltage_level_id", "bus_id", "bus_breaker_bus_id", "node", "connected", "pairing_key", "ucte_xnode_code", "paired", "fictitious", "tie_line_id"); + "voltage_level_id", "bus_id", "bus_breaker_bus_id", "node", "connected", "pairing_key", + "ucte_xnode_code", "paired", "fictitious", "tie_line_id", "selected_limits_group"); + } + + @Test + void danglingLinesGeneration() { + Network network = EurostagTutorialExample1Factory.create(); + List series = createDataFrame(DANGLING_LINE_GENERATION, network); + + assertThat(series) + .extracting(Series::getName) + .containsExactly("id", "min_p", "max_p", "target_p", "target_q", + "target_v", "voltage_regulator_on"); } @Test @@ -300,7 +376,8 @@ void lines() { .extracting(Series::getName) .containsExactly("id", "name", "r", "x", "g1", "b1", "g2", "b2", "p1", "q1", "i1", "p2", "q2", "i2", "voltage_level1_id", "voltage_level2_id", "bus1_id", "bus_breaker_bus1_id", "node1", - "bus2_id", "bus_breaker_bus2_id", "node2", "connected1", "connected2", "fictitious"); + "bus2_id", "bus_breaker_bus2_id", "node2", "connected1", "connected2", "fictitious", + "selected_limits_group_1", "selected_limits_group_2"); } @Test @@ -365,7 +442,7 @@ void twoWindingTransformers() { .extracting(Series::getName) .containsExactly("id", "name", "r", "x", "g", "b", "rated_u1", "rated_u2", "rated_s", "p1", "q1", "i1", "p2", "q2", "i2", "voltage_level1_id", "voltage_level2_id", "bus1_id", "bus_breaker_bus1_id", "node1", "bus2_id", "bus_breaker_bus2_id", "node2", - "connected1", "connected2", "fictitious"); + "connected1", "connected2", "fictitious", "selected_limits_group_1", "selected_limits_group_2"); } @Test @@ -383,9 +460,9 @@ void threeWindingTransformers() { assertThat(allAttributeSeries) .extracting(Series::getName) .containsExactly("id", "name", "rated_u0", - "r1", "x1", "g1", "b1", "rated_u1", "rated_s1", "ratio_tap_position1", "phase_tap_position1", "p1", "q1", "i1", "voltage_level1_id", "bus1_id", "bus_breaker_bus1_id", "node1", "connected1", - "r2", "x2", "g2", "b2", "rated_u2", "rated_s2", "ratio_tap_position2", "phase_tap_position2", "p2", "q2", "i2", "voltage_level2_id", "bus2_id", "bus_breaker_bus2_id", "node2", "connected2", - "r3", "x3", "g3", "b3", "rated_u3", "rated_s3", "ratio_tap_position3", "phase_tap_position3", "p3", "q3", "i3", "voltage_level3_id", "bus3_id", "bus_breaker_bus3_id", "node3", "connected3", + "r1", "x1", "g1", "b1", "rated_u1", "rated_s1", "ratio_tap_position1", "phase_tap_position1", "p1", "q1", "i1", "voltage_level1_id", "bus1_id", "bus_breaker_bus1_id", "node1", "connected1", "selected_limits_group_1", + "r2", "x2", "g2", "b2", "rated_u2", "rated_s2", "ratio_tap_position2", "phase_tap_position2", "p2", "q2", "i2", "voltage_level2_id", "bus2_id", "bus_breaker_bus2_id", "node2", "connected2", "selected_limits_group_2", + "r3", "x3", "g3", "b3", "rated_u3", "rated_s3", "ratio_tap_position3", "phase_tap_position3", "p3", "q3", "i3", "voltage_level3_id", "bus3_id", "bus_breaker_bus3_id", "node3", "connected3", "selected_limits_group_3", "fictitious"); } @@ -618,7 +695,8 @@ void testBranches() { assertThat(series) .extracting(Series::getName) - .containsExactly("id", "type", "voltage_level1_id", "bus1_id", "connected1", "voltage_level2_id", "bus2_id", "connected2", "p1", "q1", "i1", "p2", "q2", "i2"); + .containsExactly("id", "type", "voltage_level1_id", "bus1_id", "connected1", + "voltage_level2_id", "bus2_id", "connected2", "p1", "q1", "i1", "p2", "q2", "i2"); } @Test @@ -661,4 +739,120 @@ void linePositionExtensions() { assertThat(ext1Series.get(2).getDoubles()).containsExactly(coord1.getLatitude()); assertThat(ext1Series.get(3).getDoubles()).containsExactly(coord1.getLongitude()); } + + @Test + void cgmesMetadataModelsExtension() { + var network = EurostagTutorialExample1Factory.create(); + CgmesMetadataModels extension = network.getExtension(CgmesMetadataModels.class); + assertNull(extension); + + CgmesMetadataModelsAdder adder = network.newExtension(CgmesMetadataModelsAdder.class) + .newModel() + .setId("sshId1") + .setSubset(EQUIPMENT) + .setDescription("description") + .setVersion(1) + .setModelingAuthoritySet(" ") + .addProfile("profile") + .addDependentOn("true") + .addSupersedes(" ") + .add() + .newModel() + .setId("sshId2") + .setSubset(TOPOLOGY) + .setDescription("description") + .setVersion(1) + .setModelingAuthoritySet(" ") + .addProfile("profile") + .addDependentOn("true") + .addSupersedes(" ") + .add(); + adder.add(); + + extension = network.getExtension(CgmesMetadataModels.class); + assertNotNull(extension); + + List ids = List.of("sshId1"); + assertThrows(UnsupportedOperationException.class, () -> NetworkExtensions.removeExtensions(network, "cgmesMetadataModels", ids)); + } + + @Test + void referencePrioritiesExtensions() { + Network network = EurostagTutorialExample1Factory.create(); + Generator gen = network.getGenerator("GEN"); + Load load = network.getLoad("LOAD"); + ReferencePriority.set(gen, 1); + ReferencePriority.set(load, 2); + + List ext1Series = createExtensionDataFrame(ReferencePriorities.NAME, network); + assertThat(ext1Series) + .extracting(Series::getName) + .containsExactly("id", "priority"); + assertThat(ext1Series.get(0).getStrings()).containsExactly("GEN", "LOAD"); + assertThat(ext1Series.get(1).getInts()).containsExactly(1, 2); + + DefaultUpdatingDataframe dataframe = new DefaultUpdatingDataframe(2); + dataframe.addSeries("id", true, new TestStringSeries("GEN", "LOAD")); + dataframe.addSeries("priority", false, new TestIntSeries(3, 4)); + updateExtension(ReferencePriorities.NAME, network, dataframe); + List ext2Series = createExtensionDataFrame(ReferencePriorities.NAME, network); + assertThat(ext2Series.get(0).getStrings()).containsExactly("GEN", "LOAD"); + assertThat(ext2Series.get(1).getInts()).containsExactly(3, 4); + } + + @Test + void testOperationalLimits() { + Network network = EurostagTutorialExample1Factory.createWithFixedLimits(); + List limits = createDataFrame(OPERATIONAL_LIMITS, network); + List selectedLimits = createDataFrame(SELECTED_OPERATIONAL_LIMITS, network); + + assertThat(limits) + .extracting(Series::getName).containsExactly("element_id", "element_type", "side", + "name", "type", "value", "acceptable_duration"); + assertThat(selectedLimits) + .extracting(Series::getName).containsExactly("element_id", "element_type", "side", + "name", "type", "value", "acceptable_duration"); + assertThat(limits.get(0).getStrings()).isEqualTo(selectedLimits.get(0).getStrings()); + } + + @Test + void areas() { + Network network = EurostagTutorialExample1Factory.createWithTieLinesAndAreas(); + List series = createDataFrame(AREA, network); + + assertThat(series) + .extracting(Series::getName) + .containsExactly("id", "name", "area_type", "interchange_target", + "interchange", "ac_interchange", "dc_interchange"); + List allAttributeSeries = createDataFrame(AREA, network, new DataframeFilter(ALL_ATTRIBUTES, Collections.emptyList())); + assertThat(allAttributeSeries) + .extracting(Series::getName) + .containsExactly("id", "name", "area_type", "interchange_target", + "interchange", "ac_interchange", "dc_interchange", "fictitious"); + } + + @Test + void areasVoltageLevels() { + Network network = EurostagTutorialExample1Factory.createWithTieLinesAndAreas(); + List series = createDataFrame(AREA_VOLTAGE_LEVELS, network); + + assertThat(series) + .extracting(Series::getName) + .containsExactly("id", "voltage_level_id"); + } + + @Test + void areasBoundaries() { + Network network = EurostagTutorialExample1Factory.createWithTieLinesAndAreas(); + List series = createDataFrame(AREA_BOUNDARIES, network); + + assertThat(series) + .extracting(Series::getName) + .containsExactly("id", "element", "ac", "p", "q"); + + List allAttributeSeries = createDataFrame(AREA_BOUNDARIES, network, new DataframeFilter(ALL_ATTRIBUTES, Collections.emptyList())); + assertThat(allAttributeSeries) + .extracting(Series::getName) + .containsExactly("id", "boundary_type", "element", "side", "ac", "p", "q"); + } } diff --git a/java/src/test/java/com/powsybl/dataframe/network/adders/NetworkElementAddersTest.java b/java/src/test/java/com/powsybl/dataframe/network/adders/NetworkElementAddersTest.java index e35bb91039..fab18636a4 100644 --- a/java/src/test/java/com/powsybl/dataframe/network/adders/NetworkElementAddersTest.java +++ b/java/src/test/java/com/powsybl/dataframe/network/adders/NetworkElementAddersTest.java @@ -7,6 +7,7 @@ */ package com.powsybl.dataframe.network.adders; +import com.powsybl.cgmes.extensions.CgmesMetadataModels; import com.powsybl.dataframe.DataframeElementType; import com.powsybl.dataframe.update.*; import com.powsybl.entsoe.util.EntsoeArea; @@ -20,6 +21,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import static com.powsybl.iidm.network.ShuntCompensatorModelType.LINEAR; import static com.powsybl.iidm.network.ShuntCompensatorModelType.NON_LINEAR; @@ -27,9 +29,9 @@ import static org.junit.jupiter.api.Assertions.*; /** - * @author Yichen TANG - * @author Etienne Lesot - * @author Sylvain Leclerc + * @author Yichen TANG {@literal } + * @author Etienne Lesot {@literal } + * @author Sylvain Leclerc {@literal } */ class NetworkElementAddersTest { @@ -138,10 +140,43 @@ void danglingLine() { addDoubleColumn(dataframe, "b", Math.pow(10, -6) * 4); addDoubleColumn(dataframe, "p0", 102d); addDoubleColumn(dataframe, "q0", 151d); - NetworkElementAdders.addElements(DataframeElementType.DANGLING_LINE, network, singletonList(dataframe)); + var emptyGenerationDataframe = new DefaultUpdatingDataframe(0); + NetworkElementAdders.addElements(DataframeElementType.DANGLING_LINE, network, List.of(dataframe, emptyGenerationDataframe)); assertEquals(2, network.getDanglingLineCount()); } + @Test + void danglingLineWithGeneration() { + var network = DanglingLineNetworkFactory.create(); + var dataframe = new DefaultUpdatingDataframe(1); + addStringColumn(dataframe, "id", "dl2"); + addStringColumn(dataframe, "name", "name-dl2"); + addStringColumn(dataframe, "connectable_bus_id", "BUS"); + addStringColumn(dataframe, "bus_id", "BUS"); + addStringColumn(dataframe, "voltage_level_id", "VL"); + addDoubleColumn(dataframe, "r", 0.6d); + addDoubleColumn(dataframe, "x", 1d); + addDoubleColumn(dataframe, "g", Math.pow(10, -6)); + addDoubleColumn(dataframe, "b", Math.pow(10, -6) * 4); + addDoubleColumn(dataframe, "p0", 102d); + addDoubleColumn(dataframe, "q0", 151d); + + var generationDataframe = new DefaultUpdatingDataframe(1); + addStringColumn(generationDataframe, "id", "dl2"); + addDoubleColumn(generationDataframe, "min_p", 0); + addDoubleColumn(generationDataframe, "max_p", 200d); + addDoubleColumn(generationDataframe, "target_p", 102d); + addDoubleColumn(generationDataframe, "target_q", 151d); + addDoubleColumn(generationDataframe, "target_v", 100d); + addIntColumn(generationDataframe, "voltage_regulator_on", 1); + NetworkElementAdders.addElements(DataframeElementType.DANGLING_LINE, network, List.of(dataframe, generationDataframe)); + + assertEquals(2, network.getDanglingLineCount()); + DanglingLine dl = network.getDanglingLine("dl2"); + assertTrue(Optional.ofNullable(dl.getGeneration()).isPresent()); + assertTrue(dl.getGeneration().isVoltageRegulationOn()); + } + @Test void busbar() { var network = HvdcTestNetwork.createBase(); @@ -340,7 +375,6 @@ void hvdcAngleDroopActivePowerControlExtension() { HvdcLine l = network.getHvdcLine(lId); HvdcAngleDroopActivePowerControl extension = l.getExtension(HvdcAngleDroopActivePowerControl.class); assertNull(extension); - DefaultUpdatingDataframe dataframe = new DefaultUpdatingDataframe(1); addStringColumn(dataframe, "id", lId); addDoubleColumn(dataframe, "droop", droop); @@ -443,6 +477,37 @@ void secondaryVoltageControlExtension() { assertNotNull(extension); } + @Test + void cgmesMetadataModelExtension() { + var network = EurostagTutorialExample1Factory.create(); + String id = "id"; + String subset = "EQUIPMENT"; + String description = "description"; + int version = 1; + String modelingAuthoritySet = "modelingAuthoritySet"; + String profiles = "profiles"; + String dependentOn = "true"; + String supersedes = "true"; + + CgmesMetadataModels extension = network.getExtension(CgmesMetadataModels.class); + assertNull(extension); + + DefaultUpdatingDataframe dataframe = new DefaultUpdatingDataframe(1); + addStringColumn(dataframe, "id", id); + addStringColumn(dataframe, "cgmes_subset", subset); + addStringColumn(dataframe, "description", description); + addIntColumn(dataframe, "version", version); + addStringColumn(dataframe, "modeling_authority_set", modelingAuthoritySet); + addStringColumn(dataframe, "profiles", profiles); + addStringColumn(dataframe, "dependent_on", dependentOn); + addStringColumn(dataframe, "supersedes", supersedes); + + NetworkElementAdders.addExtensions("cgmesMetadataModels", network, singletonList(dataframe)); + extension = network.getExtension(CgmesMetadataModels.class); + + assertNotNull(extension); + } + @Test void threeWindingTransformer() { var network = EurostagTutorialExample1Factory.create(); @@ -481,4 +546,140 @@ void threeWindingTransformer() { NetworkElementAdders.addElements(DataframeElementType.THREE_WINDINGS_TRANSFORMER, network, singletonList(dataframe)); assertEquals(1, network.getThreeWindingsTransformerCount()); } + + @Test + void operationalLimits() { + var network = EurostagTutorialExample1Factory.createWithFixedCurrentLimits(); + DefaultUpdatingDataframe dataframe = new DefaultUpdatingDataframe(2); + addStringColumn(dataframe, "element_id", "NHV1_NHV2_1", "NHV1_NHV2_1"); + addStringColumn(dataframe, "name", "permanent_limit", "1'"); + addStringColumn(dataframe, "side", "ONE", "ONE"); + addStringColumn(dataframe, "type", "APPARENT_POWER", "APPARENT_POWER"); + addDoubleColumn(dataframe, "value", 600, 1000); + addIntColumn(dataframe, "acceptable_duration", -1, 60); + NetworkElementAdders.addElements(DataframeElementType.OPERATIONAL_LIMITS, network, singletonList(dataframe)); + Optional optionalLimits = network.getLine("NHV1_NHV2_1").getApparentPowerLimits(TwoSides.ONE); + assertTrue(optionalLimits.isPresent()); + ApparentPowerLimits limits = optionalLimits.get(); + assertEquals(600, limits.getPermanentLimit()); + assertEquals(1, limits.getTemporaryLimits().size()); + } + + @Test + void multipleOperationalLimitsGroups() { + var network = EurostagTutorialExample1Factory.createWithFixedCurrentLimits(); + DefaultUpdatingDataframe dataframe = new DefaultUpdatingDataframe(2); + addStringColumn(dataframe, "element_id", "NHV1_NHV2_1", "NHV1_NHV2_1"); + addStringColumn(dataframe, "name", "permanent_limit", "1'"); + addStringColumn(dataframe, "side", "ONE", "ONE"); + addStringColumn(dataframe, "type", "APPARENT_POWER", "APPARENT_POWER"); + addDoubleColumn(dataframe, "value", 600, 1000); + addIntColumn(dataframe, "acceptable_duration", -1, 60); + addStringColumn(dataframe, "group_name", "SUMMER", "SUMMER"); + NetworkElementAdders.addElements(DataframeElementType.OPERATIONAL_LIMITS, network, singletonList(dataframe)); + Optional optionalLimits = network.getLine("NHV1_NHV2_1").getApparentPowerLimits(TwoSides.ONE); + assertTrue(optionalLimits.isEmpty()); + Optional group = network.getLine("NHV1_NHV2_1").getOperationalLimitsGroup1("SUMMER"); + assertTrue(group.isPresent()); + optionalLimits = group.get().getApparentPowerLimits(); + assertTrue(optionalLimits.isPresent()); + assertEquals(600, optionalLimits.get().getPermanentLimit()); + assertEquals(1, optionalLimits.get().getTemporaryLimits().size()); + } + + @Test + void area() { + var network = EurostagTutorialExample1Factory.create(); + var dataframe = new DefaultUpdatingDataframe(1); + addStringColumn(dataframe, "id", "myArea"); + addStringColumn(dataframe, "name", "my area name"); + addStringColumn(dataframe, "area_type", "ControlArea"); + addDoubleColumn(dataframe, "interchange_target", 42.0); + NetworkElementAdders.addElements(DataframeElementType.AREA, network, singletonList(dataframe)); + assertEquals(1, network.getAreaCount()); + } + + @Test + void areaVoltageLevels() { + var network = EurostagTutorialExample1Factory.create(); + final String areaType = "ControlArea"; + var area1 = network.newArea() + .setId("area1") + .setAreaType(areaType) + .add(); + var area2 = network.newArea() + .setId("area2") + .setAreaType(areaType) + .add(); + var dataframe = new DefaultUpdatingDataframe(3); + addStringColumn(dataframe, "id", "area1", "area1", "area2"); + addStringColumn(dataframe, "voltage_level_id", "VLGEN", "VLHV1", "VLHV2"); + assertEquals(0, area1.getVoltageLevelStream().count()); + assertEquals(0, area2.getVoltageLevelStream().count()); + NetworkElementAdders.addElements(DataframeElementType.AREA_VOLTAGE_LEVELS, network, singletonList(dataframe)); + assertEquals(2, area1.getVoltageLevelStream().count()); + assertEquals(1, area2.getVoltageLevelStream().count()); + assertEquals(area1, network.getVoltageLevel("VLGEN").getArea(areaType).orElseThrow()); + assertEquals(area1, network.getVoltageLevel("VLHV1").getArea(areaType).orElseThrow()); + assertEquals(area2, network.getVoltageLevel("VLHV2").getArea(areaType).orElseThrow()); + + dataframe = new DefaultUpdatingDataframe(1); + addStringColumn(dataframe, "id", "area1"); + addStringColumn(dataframe, "voltage_level_id", ""); + NetworkElementAdders.addElements(DataframeElementType.AREA_VOLTAGE_LEVELS, network, singletonList(dataframe)); + assertEquals(0, area1.getVoltageLevelStream().count()); // cleared + assertEquals(1, area2.getVoltageLevelStream().count()); // unchanged because area2 is not in updating df + + dataframe = new DefaultUpdatingDataframe(2); + addStringColumn(dataframe, "id", "area1", "area1"); + addStringColumn(dataframe, "voltage_level_id", "", "VLGEN"); + NetworkElementAdders.addElements(DataframeElementType.AREA_VOLTAGE_LEVELS, network, singletonList(dataframe)); + assertEquals(1, area1.getVoltageLevelStream().count()); // empty ("") voltage level is ignored because not alone + assertEquals(1, area2.getVoltageLevelStream().count()); // unchanged because area2 is not in updating df + assertEquals(area1, network.getVoltageLevel("VLGEN").getArea(areaType).orElseThrow()); + assertFalse(network.getVoltageLevel("VLHV1").getArea(areaType).isPresent()); + assertEquals(area2, network.getVoltageLevel("VLHV2").getArea(areaType).orElseThrow()); + } + + @Test + void areaBoundaries() { + var network = EurostagTutorialExample1Factory.createWithTieLine(); + final String areaType = "ControlArea"; + var area1 = network.newArea() + .setId("area1") + .setAreaType(areaType) + .add(); + var area2 = network.newArea() + .setId("area2") + .setAreaType(areaType) + .add(); + var dataframe = new DefaultUpdatingDataframe(4); + addStringColumn(dataframe, "id", "area1", "area1", "area2", "area2"); + addStringColumn(dataframe, "boundary_type", "DANGLING_LINE", "DANGLING_LINE", "DANGLING_LINE", "DANGLING_LINE"); + addStringColumn(dataframe, "element", "NHV1_XNODE1", "NHV1_XNODE2", "XNODE1_NHV2", "XNODE2_NHV2"); + addIntColumn(dataframe, "ac", 1, 1, 1, 1); + assertEquals(0, area1.getAreaBoundaryStream().count()); + assertEquals(0, area2.getAreaBoundaryStream().count()); + NetworkElementAdders.addElements(DataframeElementType.AREA_BOUNDARIES, network, singletonList(dataframe)); + assertEquals(2, area1.getAreaBoundaryStream().count()); + assertEquals(2, area2.getAreaBoundaryStream().count()); + + dataframe = new DefaultUpdatingDataframe(1); + addStringColumn(dataframe, "id", "area1"); + addStringColumn(dataframe, "element", ""); + addIntColumn(dataframe, "ac", 1); + NetworkElementAdders.addElements(DataframeElementType.AREA_BOUNDARIES, network, singletonList(dataframe)); + assertEquals(0, area1.getAreaBoundaryStream().count()); // cleared + assertEquals(2, area2.getAreaBoundaryStream().count()); // unchanged because area2 is not in updating df + + dataframe = new DefaultUpdatingDataframe(1); + addStringColumn(dataframe, "id", "area1"); + addStringColumn(dataframe, "boundary_type", "TERMINAL"); + addStringColumn(dataframe, "element", "NGEN_NHV1"); + addStringColumn(dataframe, "side", "TWO"); + addIntColumn(dataframe, "ac", 1); + NetworkElementAdders.addElements(DataframeElementType.AREA_BOUNDARIES, network, singletonList(dataframe)); + assertEquals(1, area1.getAreaBoundaryStream().count()); + assertEquals(2, area2.getAreaBoundaryStream().count()); // unchanged because area2 is not in updating df + } } diff --git a/java/src/test/java/com/powsybl/dataframe/update/TestDoubleSeries.java b/java/src/test/java/com/powsybl/dataframe/update/TestDoubleSeries.java index f69f013221..c768569f45 100644 --- a/java/src/test/java/com/powsybl/dataframe/update/TestDoubleSeries.java +++ b/java/src/test/java/com/powsybl/dataframe/update/TestDoubleSeries.java @@ -12,7 +12,7 @@ import java.util.stream.Collectors; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class TestDoubleSeries implements DoubleSeries { diff --git a/java/src/test/java/com/powsybl/dataframe/update/TestIntSeries.java b/java/src/test/java/com/powsybl/dataframe/update/TestIntSeries.java index 601dabda68..48c72a4d8f 100644 --- a/java/src/test/java/com/powsybl/dataframe/update/TestIntSeries.java +++ b/java/src/test/java/com/powsybl/dataframe/update/TestIntSeries.java @@ -12,7 +12,7 @@ import java.util.stream.Collectors; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class TestIntSeries implements IntSeries { diff --git a/java/src/test/java/com/powsybl/dataframe/update/TestStringSeries.java b/java/src/test/java/com/powsybl/dataframe/update/TestStringSeries.java index 8652308a65..d17fda8c5f 100644 --- a/java/src/test/java/com/powsybl/dataframe/update/TestStringSeries.java +++ b/java/src/test/java/com/powsybl/dataframe/update/TestStringSeries.java @@ -10,7 +10,7 @@ import java.util.List; /** - * @author Sylvain Leclerc + * @author Sylvain Leclerc {@literal } */ public class TestStringSeries implements StringSeries { diff --git a/java/src/test/java/com/powsybl/python/contingency/ContingencyContainerTest.java b/java/src/test/java/com/powsybl/python/contingency/ContingencyContainerTest.java index 49bbf0bd85..36880cb961 100644 --- a/java/src/test/java/com/powsybl/python/contingency/ContingencyContainerTest.java +++ b/java/src/test/java/com/powsybl/python/contingency/ContingencyContainerTest.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ class ContingencyContainerTest { diff --git a/java/src/test/java/com/powsybl/python/loadflow/validation/LoadFlowValidationTest.java b/java/src/test/java/com/powsybl/python/loadflow/validation/LoadFlowValidationTest.java index d8ab3aaf46..a7209448b1 100644 --- a/java/src/test/java/com/powsybl/python/loadflow/validation/LoadFlowValidationTest.java +++ b/java/src/test/java/com/powsybl/python/loadflow/validation/LoadFlowValidationTest.java @@ -23,7 +23,7 @@ import org.junit.jupiter.api.condition.OS; /** - * @author Yichen TANG + * @author Yichen TANG {@literal } */ class LoadFlowValidationTest { diff --git a/java/src/test/java/com/powsybl/python/network/NetworkAreaDiagramUtilTest.java b/java/src/test/java/com/powsybl/python/network/NetworkAreaDiagramUtilTest.java index 7c649c9914..7b087160c0 100644 --- a/java/src/test/java/com/powsybl/python/network/NetworkAreaDiagramUtilTest.java +++ b/java/src/test/java/com/powsybl/python/network/NetworkAreaDiagramUtilTest.java @@ -21,6 +21,7 @@ import static com.powsybl.python.network.NetworkAreaDiagramUtil.createNadParameters; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * @author Geoffroy Jamgotchian {@literal } @@ -35,6 +36,15 @@ void test() throws IOException { TestUtil.normalizeLineSeparator(svg)); } + @Test + void testSvgAndMetadata() throws IOException { + Network network = IeeeCdfNetworkFactory.create14(); + List svgAndMeta = NetworkAreaDiagramUtil.getSvgAndMetadata(network, Collections.emptyList(), createNadParameters()); + assertEquals(TestUtil.normalizeLineSeparator(new String(ByteStreams.toByteArray(Objects.requireNonNull(NetworkAreaDiagramUtil.class.getResourceAsStream("/nad.svg"))), StandardCharsets.UTF_8)), + TestUtil.normalizeLineSeparator(svgAndMeta.get(0))); + assertFalse(svgAndMeta.get(1).isEmpty()); + } + @Test void testGetVisibleVoltageLevels() { Network network = EurostagTutorialExample1Factory.createWithTieLine(); diff --git a/java/src/test/java/com/powsybl/python/network/NetworkUtilTest.java b/java/src/test/java/com/powsybl/python/network/NetworkUtilTest.java index 662f509074..59e9b48d3a 100644 --- a/java/src/test/java/com/powsybl/python/network/NetworkUtilTest.java +++ b/java/src/test/java/com/powsybl/python/network/NetworkUtilTest.java @@ -7,20 +7,22 @@ */ package com.powsybl.python.network; -import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.python.commons.PyPowsyblApiHeader; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Optional; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; /** * @author Geoffroy Jamgotchian {@literal } */ -class NetworkUtilTest { +public class NetworkUtilTest { @Test void test() { @@ -28,4 +30,50 @@ void test() { List elementsIds = NetworkUtil.getElementsIds(network, PyPowsyblApiHeader.ElementType.TWO_WINDINGS_TRANSFORMER, Collections.singleton(24.0), Collections.singleton("FR"), true, true, false); assertEquals(Collections.singletonList("NGEN_NHV1"), elementsIds); } + + @Test + void testBusFromBusBreakerViewBus() { + Network network = createTopologyTestNetwork(); + var expected = Map.of( + "B1", "VL1_0", + "B2", "VL1_0", + "B3", "", + "VL2_0", "VL2_0", + "VL2_1", "VL2_0", + "VL2_2", ""); + expected.forEach((busBreakerBusId, busIdExpected) -> { + Optional bus = NetworkUtil.getBusViewBus(network.getBusBreakerView().getBus(busBreakerBusId)); + if (!busIdExpected.isEmpty()) { + assertTrue(bus.isPresent()); + assertEquals(busIdExpected, bus.orElseThrow().getId()); + } else { + assertTrue(bus.isEmpty()); + } + }); + } + + public static Network createTopologyTestNetwork() { + Network network = NetworkFactory.findDefault().createNetwork("test", "code"); + + var vl1 = network.newVoltageLevel().setTopologyKind(TopologyKind.BUS_BREAKER).setId("VL1").setNominalV(400.).add(); + vl1.getBusBreakerView().newBus().setId("B1").add(); + vl1.getBusBreakerView().newBus().setId("B2").add(); + vl1.getBusBreakerView().newBus().setId("B3").add(); + vl1.getBusBreakerView().newSwitch().setId("CB1.1").setOpen(false).setBus1("B1").setBus2("B2").add(); + vl1.getBusBreakerView().newSwitch().setId("CB1.2").setOpen(true).setBus1("B1").setBus2("B2").add(); + vl1.newLoad().setId("L1").setP0(10.).setQ0(3.).setConnectableBus("B1").setBus("B1").add(); + + var vl2 = network.newVoltageLevel().setTopologyKind(TopologyKind.NODE_BREAKER).setId("VL2").setNominalV(400.).add(); + vl2.getNodeBreakerView().newBusbarSection().setId("BBS1").setNode(0).add(); + vl2.getNodeBreakerView() + .newSwitch() + .setId("CB2.1").setOpen(false).setRetained(true).setKind(SwitchKind.BREAKER).setNode1(0).setNode2(1).add(); + vl2.getNodeBreakerView() + .newSwitch() + .setId("CB2.2").setOpen(true).setRetained(true).setKind(SwitchKind.BREAKER).setNode1(1).setNode2(2).add(); + vl2.newLoad().setId("L2").setP0(10.).setQ0(3.).setNode(3).add(); + vl2.getNodeBreakerView().newInternalConnection().setNode1(0).setNode2(3).add(); + + return network; + } } diff --git a/java/src/test/java/com/powsybl/python/security/SecurityAnalysisTest.java b/java/src/test/java/com/powsybl/python/security/SecurityAnalysisTest.java index 89271317e3..773599868a 100644 --- a/java/src/test/java/com/powsybl/python/security/SecurityAnalysisTest.java +++ b/java/src/test/java/com/powsybl/python/security/SecurityAnalysisTest.java @@ -29,7 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * @author Etienne Lesot + * @author Etienne Lesot {@literal } */ class SecurityAnalysisTest { diff --git a/java/src/test/java/com/powsybl/python/sensitivity/SensitivityAnalysisTest.java b/java/src/test/java/com/powsybl/python/sensitivity/SensitivityAnalysisTest.java index 2229989e70..f9f6f8b6b9 100644 --- a/java/src/test/java/com/powsybl/python/sensitivity/SensitivityAnalysisTest.java +++ b/java/src/test/java/com/powsybl/python/sensitivity/SensitivityAnalysisTest.java @@ -18,7 +18,7 @@ import static org.junit.jupiter.api.Assertions.fail; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ class SensitivityAnalysisTest { diff --git a/java/src/test/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisTest.java b/java/src/test/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisTest.java index 336b012079..a3aa219791 100644 --- a/java/src/test/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisTest.java +++ b/java/src/test/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisTest.java @@ -23,7 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * @author Christian Biasuzzi + * @author Christian Biasuzzi {@literal } */ class ShortCircuitAnalysisTest { @@ -91,7 +91,7 @@ void testShortCircuitAnalysisResults() { List feederResultsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisMagnitudeFeederResultsMapper(false), fakeResults); Assertions.assertThat(feederResultsSeries) .extracting(Series::getName) - .containsExactly("id", "connectable_id", "current"); + .containsExactly("id", "connectable_id", "current", "side"); Assertions.assertThat(feederResultsSeries.get(0).getStrings()) .containsExactly("f1", "f1"); Assertions.assertThat(feederResultsSeries.get(1).getStrings()) diff --git a/java/src/test/resources/nad.svg b/java/src/test/resources/nad.svg index a438b21875..2078cfd7e5 100644 --- a/java/src/test/resources/nad.svg +++ b/java/src/test/resources/nad.svg @@ -109,82 +109,6 @@ path.nad-arrow-in:not(.nad-state-in .nad-arrow-in) {visibility: hidden} 40% {stroke: #00BCD4; stroke-width: 15} } ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/java/src/test/resources/sld.svg b/java/src/test/resources/sld.svg index 5a92ba319e..dfa52d5513 100644 --- a/java/src/test/resources/sld.svg +++ b/java/src/test/resources/sld.svg @@ -130,7 +130,7 @@ .sld-unknown {stroke: none; fill: black} /* Fonts */ .sld-label {stroke: none; fill: black; font: 8px serif} -.sld-angle, .sld-voltage {font: 10px serif} +.sld-bus-legend-info {font: 10px serif} .sld-graph-label {font: 12px serif} /* Specific */ .sld-grid {stroke: #003700; stroke-dasharray: 1,10} diff --git a/mypy.ini b/mypy.ini index 33f832d97b..4c98f57cf6 100644 --- a/mypy.ini +++ b/mypy.ini @@ -5,4 +5,7 @@ disallow_untyped_defs = True ignore_missing_imports = True [mypy-prettytable.*] -ignore_missing_imports = True \ No newline at end of file +ignore_missing_imports = True + +[mypy-pandapower.*] +ignore_missing_imports = True diff --git a/pypowsybl/__init__.py b/pypowsybl/__init__.py index debb1e9a47..6dd6394e93 100644 --- a/pypowsybl/__init__.py +++ b/pypowsybl/__init__.py @@ -22,7 +22,7 @@ ) from pypowsybl.network import per_unit_view -__version__ = '1.7.0.dev1' +__version__ = '1.9.0.dev1' # set JVM java.library.path to pypowsybl module installation directory to be able to load math library _pypowsybl.set_java_library_path(_os.path.dirname(_inspect.getfile(_pypowsybl))) @@ -43,7 +43,7 @@ ] -# setup a default logger that is the powsybl logger with by default no handler to avoir printing logs >= WARNING +# setup a default logger that is the powsybl logger with by default no handler to avoid printing logs >= WARNING # to std err powsyblLogger = logging.getLogger('powsybl') powsyblLogger.addHandler(logging.NullHandler()) diff --git a/pypowsybl/_pypowsybl.pyi b/pypowsybl/_pypowsybl.pyi index 9ea77a377a..2d1a2c87f2 100644 --- a/pypowsybl/_pypowsybl.pyi +++ b/pypowsybl/_pypowsybl.pyi @@ -1,6 +1,6 @@ +from logging import Logger from typing import ClassVar, Dict, Iterator, List, Sequence, Optional, Union from numpy.typing import ArrayLike as _ArrayLike -from logging import Logger class ArrayStruct: def __init__(self) -> None: ... @@ -102,6 +102,89 @@ class SensitivityVariableType: @property def name(self) -> str: ... +class LimitType: + __members__: ClassVar[Dict[str, LimitType]] = ... # read-only + ACTIVE_POWER: ClassVar[LimitType] = ... + APPARENT_POWER: ClassVar[LimitType] = ... + CURRENT: ClassVar[LimitType] = ... + LOW_VOLTAGE: ClassVar[LimitType] = ... + HIGH_VOLTAGE: ClassVar[LimitType] = ... + LOW_VOLTAGE_ANGLE: ClassVar[LimitType] = ... + HIGH_VOLTAGE_ANGLE: ClassVar[LimitType] = ... + LOW_SHORT_CIRCUIT_CURRENT: ClassVar[LimitType] = ... + HIGH_SHORT_CIRCUIT_CURRENT: ClassVar[LimitType] = ... + OTHER: ClassVar[LimitType] = ... + def __init__(self, arg0: int) -> None: ... + def __eq__(self, arg0: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __int__(self) -> int: ... + def __ne__(self, arg0: object) -> bool: ... + def __setstate__(self, arg0: int) -> None: ... + @property + def name(self) -> str: ... + +class Side: + __members__: ClassVar[Dict[str, Side]] = ... # read-only + NONE: ClassVar[Side] = ... + ONE: ClassVar[Side] = ... + TWO: ClassVar[Side] = ... + THREE: ClassVar[Side] = ... + def __init__(self, arg0: int) -> None: ... + def __eq__(self, arg0: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __int__(self) -> int: ... + def __ne__(self, arg0: object) -> bool: ... + def __setstate__(self, arg0: int) -> None: ... + @property + def name(self) -> str: ... + +class LimitViolation: + @property + def acceptable_duration(self) -> int: ... + @property + def limit(self) -> float: ... + @property + def limit_name(self) -> str: ... + @property + def limit_reduction(self) -> float: ... + @property + def limit_type(self) -> LimitType: ... + @property + def side(self) -> Side: ... + @property + def subject_id(self) -> str: ... + @property + def subject_name(self) -> str: ... + @property + def value(self) -> float: ... + +class LimitViolationArray: + def __iter__(self) -> Iterator: ... + def __len__(self) -> int: ... + def __getitem__(self) -> LimitViolation: ... + +class PostContingencyComputationStatus: + __members__: ClassVar[Dict[str, PostContingencyComputationStatus]] = ... # read-only + CONVERGED: ClassVar[PostContingencyComputationStatus] = ... + FAILED: ClassVar[PostContingencyComputationStatus] = ... + MAX_ITERATION_REACHED: ClassVar[PostContingencyComputationStatus] = ... + SOLVER_FAILED: ClassVar[PostContingencyComputationStatus] = ... + NO_IMPACT: ClassVar[PostContingencyComputationStatus] = ... + def __init__(self, arg0: int) -> None: ... + def __eq__(self, arg0: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __int__(self) -> int: ... + def __ne__(self, arg0: object) -> bool: ... + def __setstate__(self, arg0: int) -> None: ... + @property + def name(self) -> str: ... + class PostContingencyResult: @property def contingency_id(self) -> str: ... @@ -118,6 +201,23 @@ class OperatorStrategyResult: @property def status(self) -> PostContingencyComputationStatus: ... +class LoadFlowComponentStatus: + __members__: ClassVar[Dict[str, LoadFlowComponentStatus]] = ... # read-only + CONVERGED: ClassVar[LoadFlowComponentStatus] = ... + FAILED: ClassVar[LoadFlowComponentStatus] = ... + MAX_ITERATION_REACHED: ClassVar[LoadFlowComponentStatus] = ... + NO_CALCULATION: ClassVar[LoadFlowComponentStatus] = ... + def __init__(self, arg0: int) -> None: ... + def __eq__(self, arg0: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __int__(self) -> int: ... + def __ne__(self, arg0: object) -> bool: ... + def __setstate__(self, arg0: int) -> None: ... + @property + def name(self) -> str: ... + class PreContingencyResult: @property def limit_violations(self) -> LimitViolationArray: ... @@ -143,6 +243,7 @@ class ElementType: BUS_FROM_BUS_BREAKER_VIEW: ClassVar[ElementType] = ... BUSBAR_SECTION: ClassVar[ElementType] = ... DANGLING_LINE: ClassVar[ElementType] = ... + DANGLING_LINE_GENERATION: ClassVar[ElementType] = ... TIE_LINE: ClassVar[ElementType] = ... GENERATOR: ClassVar[ElementType] = ... HVDC_LINE: ClassVar[ElementType] = ... @@ -150,9 +251,11 @@ class ElementType: INJECTION: ClassVar[ElementType] = ... LCC_CONVERTER_STATION: ClassVar[ElementType] = ... OPERATIONAL_LIMITS: ClassVar[ElementType] = ... + SELECTED_OPERATIONAL_LIMITS: ClassVar[ElementType] = ... LINE: ClassVar[ElementType] = ... LINEAR_SHUNT_COMPENSATOR_SECTION: ClassVar[ElementType] = ... LOAD: ClassVar[ElementType] = ... + GROUND: ClassVar[ElementType] = ... MINMAX_REACTIVE_LIMITS: ClassVar[ElementType] = ... NON_LINEAR_SHUNT_COMPENSATOR_SECTION: ClassVar[ElementType] = ... PHASE_TAP_CHANGER: ClassVar[ElementType] = ... @@ -170,6 +273,10 @@ class ElementType: VOLTAGE_LEVEL: ClassVar[ElementType] = ... VSC_CONVERTER_STATION: ClassVar[ElementType] = ... SUB_NETWORK: ClassVar[ElementType] = ... + AREA: ClassVar[ElementType] = ... + AREA_VOLTAGE_LEVELS: ClassVar[ElementType] = ... + AREA_BOUNDARIES: ClassVar[ElementType] = ... + INTERNAL_CONNECTION: ClassVar[ElementType] = ... def __init__(self, arg0: int) -> None: ... def __eq__(self, arg0: object) -> bool: ... def __getstate__(self) -> int: ... @@ -303,54 +410,6 @@ class NadParameters: edge_info_displayed: EdgeInfoType def __init__(self) -> None: ... -class LimitType: - __members__: ClassVar[Dict[str, LimitType]] = ... # read-only - ACTIVE_POWER: ClassVar[LimitType] = ... - APPARENT_POWER: ClassVar[LimitType] = ... - CURRENT: ClassVar[LimitType] = ... - LOW_VOLTAGE: ClassVar[LimitType] = ... - HIGH_VOLTAGE: ClassVar[LimitType] = ... - LOW_VOLTAGE_ANGLE: ClassVar[LimitType] = ... - HIGH_VOLTAGE_ANGLE: ClassVar[LimitType] = ... - LOW_SHORT_CIRCUIT_CURRENT: ClassVar[LimitType] = ... - HIGH_SHORT_CIRCUIT_CURRENT: ClassVar[LimitType] = ... - OTHER: ClassVar[LimitType] = ... - def __init__(self, arg0: int) -> None: ... - def __eq__(self, arg0: object) -> bool: ... - def __getstate__(self) -> int: ... - def __hash__(self) -> int: ... - def __index__(self) -> int: ... - def __int__(self) -> int: ... - def __ne__(self, arg0: object) -> bool: ... - def __setstate__(self, arg0: int) -> None: ... - @property - def name(self) -> str: ... - -class LimitViolation: - @property - def acceptable_duration(self) -> int: ... - @property - def limit(self) -> float: ... - @property - def limit_name(self) -> str: ... - @property - def limit_reduction(self) -> float: ... - @property - def limit_type(self) -> LimitType: ... - @property - def side(self) -> Side: ... - @property - def subject_id(self) -> str: ... - @property - def subject_name(self) -> str: ... - @property - def value(self) -> float: ... - -class LimitViolationArray: - def __iter__(self) -> Iterator: ... - def __len__(self) -> int: ... - def __getitem__(self) -> LimitViolation: ... - class SlackBusResult: @property def id(self) -> str: ... @@ -385,30 +444,11 @@ class LoadFlowComponentResultArray: def __len__(self) -> int: ... def __getitem__(self) -> LoadFlowComponentResult: ... -class LoadFlowComponentStatus: - __members__: ClassVar[Dict[str, LoadFlowComponentStatus]] = ... # read-only - CONVERGED: ClassVar[LoadFlowComponentStatus] = ... - FAILED: ClassVar[LoadFlowComponentStatus] = ... - MAX_ITERATION_REACHED: ClassVar[LoadFlowComponentStatus] = ... - NO_CALCULATION: ClassVar[LoadFlowComponentStatus] = ... - def __init__(self, arg0: int) -> None: ... - def __eq__(self, arg0: object) -> bool: ... - def __getstate__(self) -> int: ... - def __hash__(self) -> int: ... - def __index__(self) -> int: ... - def __int__(self) -> int: ... - def __ne__(self, arg0: object) -> bool: ... - def __setstate__(self, arg0: int) -> None: ... - @property - def name(self) -> str: ... - -class PostContingencyComputationStatus: - __members__: ClassVar[Dict[str, PostContingencyComputationStatus]] = ... # read-only - CONVERGED: ClassVar[PostContingencyComputationStatus] = ... - FAILED: ClassVar[PostContingencyComputationStatus] = ... - MAX_ITERATION_REACHED: ClassVar[PostContingencyComputationStatus] = ... - SOLVER_FAILED: ClassVar[PostContingencyComputationStatus] = ... - NO_IMPACT: ClassVar[PostContingencyComputationStatus] = ... +class VoltageInitMode: + __members__: ClassVar[Dict[str, VoltageInitMode]] = ... # read-only + DC_VALUES: ClassVar[VoltageInitMode] = ... + PREVIOUS_VALUES: ClassVar[VoltageInitMode] = ... + UNIFORM_VALUES: ClassVar[VoltageInitMode] = ... def __init__(self, arg0: int) -> None: ... def __eq__(self, arg0: object) -> bool: ... def __getstate__(self) -> int: ... @@ -485,7 +525,8 @@ class NetworkMetadata: @property def source_format(self) -> str: ... -class PyPowsyblError(Exception): ... +class PyPowsyblError(Exception): + ... class Series: @property @@ -513,23 +554,6 @@ class SeriesMetadata: @property def type(self) -> int: ... -class Side: - __members__: ClassVar[Dict[str, Side]] = ... # read-only - NONE: ClassVar[Side] = ... - ONE: ClassVar[Side] = ... - TWO: ClassVar[Side] = ... - THREE: ClassVar[Side] = ... - def __init__(self, arg0: int) -> None: ... - def __eq__(self, arg0: object) -> bool: ... - def __getstate__(self) -> int: ... - def __hash__(self) -> int: ... - def __index__(self) -> int: ... - def __int__(self) -> int: ... - def __ne__(self, arg0: object) -> bool: ... - def __setstate__(self, arg0: int) -> None: ... - @property - def name(self) -> str: ... - class ValidationLevel: __members__: ClassVar[Dict[str, ValidationLevel]] = ... # read-only EQUIPMENT: ClassVar[ValidationLevel] = ... @@ -566,22 +590,6 @@ class ValidationType: @property def name(self) -> str: ... -class VoltageInitMode: - __members__: ClassVar[Dict[str, VoltageInitMode]] = ... # read-only - DC_VALUES: ClassVar[VoltageInitMode] = ... - PREVIOUS_VALUES: ClassVar[VoltageInitMode] = ... - UNIFORM_VALUES: ClassVar[VoltageInitMode] = ... - def __init__(self, arg0: int) -> None: ... - def __eq__(self, arg0: object) -> bool: ... - def __getstate__(self) -> int: ... - def __hash__(self) -> int: ... - def __index__(self) -> int: ... - def __int__(self) -> int: ... - def __ne__(self, arg0: object) -> bool: ... - def __setstate__(self, arg0: int) -> None: ... - @property - def name(self) -> str: ... - class Zone: def __init__(self, id: str, injections_ids: List[str], injections_shift_keys: List[float]) -> None: ... @@ -590,6 +598,7 @@ class RescaleMode: NONE: ClassVar[RescaleMode] = ... ACER_METHODOLOGY: ClassVar[RescaleMode] = ... PROPORTIONAL: ClassVar[RescaleMode] = ... + MAX_CURRENT_OVERLOAD: ClassVar[RescaleMode] = ... def __init__(self, arg0: int) -> None: ... def __eq__(self, arg0: object) -> bool: ... def __getstate__(self) -> int: ... @@ -612,18 +621,38 @@ class FlowDecompositionParameters: class DynamicMappingType: __members__: ClassVar[Dict[str, DynamicMappingType]] = ... # read-only - ALPHA_BETA_LOAD: ClassVar[DynamicMappingType] = ... - ONE_TRANSFORMER_LOAD: ClassVar[DynamicMappingType] = ... - OMEGA_REF: ClassVar[DynamicMappingType] = ... - GENERATOR_SYNCHRONOUS: ClassVar[DynamicMappingType] = ... - GENERATOR_SYNCHRONOUS_THREE_WINDINGS: ClassVar[DynamicMappingType] = ... - GENERATOR_SYNCHRONOUS_THREE_WINDINGS_PROPORTIONAL_REGULATIONS: ClassVar[ - DynamicMappingType] = ... - GENERATOR_SYNCHRONOUS_FOUR_WINDINGS: ClassVar[DynamicMappingType] = ... - GENERATOR_SYNCHRONOUS_FOUR_WINDINGS_PROPORTIONAL_REGULATIONS: ClassVar[ - DynamicMappingType] = ... - CURRENT_LIMIT_AUTOMATON: ClassVar[DynamicMappingType] = ... - + BASE_LOAD: ClassVar[DynamicMappingType] = ... + LOAD_ONE_TRANSFORMER: ClassVar[DynamicMappingType] = ... + LOAD_ONE_TRANSFORMER_TAP_CHANGER: ClassVar[DynamicMappingType] = ... + LOAD_TWO_TRANSFORMERS: ClassVar[DynamicMappingType] = ... + LOAD_TWO_TRANSFORMERS_TAP_CHANGERS: ClassVar[DynamicMappingType] = ... + BASE_GENERATOR: ClassVar[DynamicMappingType] = ... + SYNCHRONIZED_GENERATOR: ClassVar[DynamicMappingType] = ... + SYNCHRONOUS_GENERATOR: ClassVar[DynamicMappingType] = ... + WECC: ClassVar[DynamicMappingType] = ... + GRID_FORMING_CONVERTER: ClassVar[DynamicMappingType] = ... + SIGNAL_N_GENERATOR: ClassVar[DynamicMappingType] = ... + HVDC_P: ClassVar[DynamicMappingType] = ... + HVDC_VSC: ClassVar[DynamicMappingType] = ... + BASE_TRANSFORMER: ClassVar[DynamicMappingType] = ... + BASE_STATIC_VAR_COMPENSATOR: ClassVar[DynamicMappingType] = ... + BASE_LINE: ClassVar[DynamicMappingType] = ... + BASE_BUS: ClassVar[DynamicMappingType] = ... + INFINITE_BUS: ClassVar[DynamicMappingType] = ... + OVERLOAD_MANAGEMENT_SYSTEM: ClassVar[DynamicMappingType] = ... + TWO_LEVELS_OVERLOAD_MANAGEMENT_SYSTEM: ClassVar[DynamicMappingType] = ... + UNDER_VOLTAGE: ClassVar[DynamicMappingType] = ... + PHASE_SHIFTER_I: ClassVar[DynamicMappingType] = ... + PHASE_SHIFTER_P: ClassVar[DynamicMappingType] = ... + PHASE_SHIFTER_BLOCKING_I: ClassVar[DynamicMappingType] = ... + TAP_CHANGER: ClassVar[DynamicMappingType] = ... + TAP_CHANGER_BLOCKING: ClassVar[DynamicMappingType] = ... + +class EventMappingType: + __members__: ClassVar[Dict[str, EventMappingType]] = ... # read-only + DISCONNECT: ClassVar[EventMappingType] = ... + NODE_FAULT: ClassVar[EventMappingType] = ... + ACTIVE_POWER_VARIATION: ClassVar[EventMappingType] = ... class VoltageInitializerStatus: __members__: ClassVar[Dict[str, @@ -775,10 +804,13 @@ def get_security_analysis_provider_parameters_names(provider: str) -> List[str]: def get_sensitivity_analysis_provider_parameters_names(provider: str) -> List[str]: ... def get_limit_violations(result: JavaHandle) -> SeriesArray: ... def get_network_area_diagram_svg(network: JavaHandle, voltage_level_ids: Union[str, List[str]], depth: int, high_nominal_voltage_bound: float, low_nominal_voltage_bound: float, nad_parameters: NadParameters) -> str: ... +def get_network_area_diagram_svg_and_metadata(network: JavaHandle, voltage_level_ids: Union[str, List[str]], depth: int, high_nominal_voltage_bound: float, low_nominal_voltage_bound: float, nad_parameters: NadParameters) -> List[str]: ... def get_network_area_diagram_displayed_voltage_levels(network: JavaHandle, voltage_level_ids: Union[str, List[str]], depth: int) -> List[str]: ... def get_network_elements_ids(network: JavaHandle, element_type: ElementType, nominal_voltages: List[float], countries: List[str], main_connected_component: bool, main_synchronous_component: bool, not_connected_to_same_bus_at_both_sides: bool) -> List[str]: ... def get_network_export_formats() -> List[str]: ... def get_network_import_formats() -> List[str]: ... +def get_network_import_supported_extensions() -> List[str]: ... +def get_network_import_post_processors() -> List[str]: ... def get_loadflow_provider_names() -> List[str]: ... def get_security_analysis_provider_names() -> List[str]: ... def get_sensitivity_analysis_provider_names() -> List[str]: ... @@ -805,9 +837,9 @@ def is_config_read() -> bool: ... def get_default_loadflow_provider() -> str: ... def get_default_security_analysis_provider() -> str: ... def get_default_sensitivity_analysis_provider() -> str: ... -def load_network(file: str, parameters: Dict[str,str], report: Optional[JavaHandle]) -> JavaHandle: ... -def load_network_from_string(file_name: str, file_content: str, parameters: Dict[str,str], report: Optional[JavaHandle]) -> JavaHandle: ... -def load_network_from_binary_buffers(file_content: List[memoryview], parameters: Dict[str,str], report: Optional[JavaHandle]) -> JavaHandle: ... +def load_network(file: str, parameters: Dict[str,str], post_processors: List[str], report: Optional[JavaHandle]) -> JavaHandle: ... +def load_network_from_string(file_name: str, file_content: str, parameters: Dict[str,str], post_processors: List[str], report: Optional[JavaHandle]) -> JavaHandle: ... +def load_network_from_binary_buffers(file_content: List[memoryview], parameters: Dict[str,str], post_processors: List[str], report: Optional[JavaHandle]) -> JavaHandle: ... def merge(networks: List[JavaHandle]) -> JavaHandle: ... def get_sub_network(network: JavaHandle, sub_network_id: str) -> JavaHandle: ... def detach_sub_network(network: JavaHandle) -> JavaHandle: ... @@ -832,7 +864,7 @@ def update_connectable_status(arg0: JavaHandle, arg1: str, arg2: bool) -> bool: def update_network_elements_with_series(network: JavaHandle, array: Dataframe, element_type: ElementType, per_unit: bool, nominal_apparent_power: float) -> None: ... def update_switch_position(arg0: JavaHandle, arg1: str, arg2: bool) -> bool: ... def validate(network: JavaHandle) -> ValidationLevel: ... -def write_network_area_diagram_svg(network: JavaHandle, svg_file: str, voltage_level_ids: Union[str, List[str]], depth: int, high_nominal_voltage_bound: float, low_nominal_voltage_bound: float, nad_parameters: NadParameters) -> None: ... +def write_network_area_diagram_svg(network: JavaHandle, svg_file: str, metadata_file: str, voltage_level_ids: Union[str, List[str]], depth: int, high_nominal_voltage_bound: float, low_nominal_voltage_bound: float, nad_parameters: NadParameters) -> None: ... def write_single_line_diagram_svg(network: JavaHandle, container_id: str, svg_file: str, metadata_file: str, parameters: SldParameters) -> None: ... def write_matrix_multi_substation_single_line_diagram_svg(network: JavaHandle, matrix_ids: List[List[str]], svg_file: str, metadata_file: str, parameters: SldParameters) -> None: ... def add_network_element_properties(network: JavaHandle, dataframe: Dataframe) -> None: ... @@ -862,16 +894,19 @@ def revert_connect_voltage_level_on_line(network: JavaHandle, line1_id: str, lin def get_connectables_order_positions(network: JavaHandle, voltage_level_id: str) -> SeriesArray: ... def get_unused_order_positions(network: JavaHandle, busbar_section_id: str, before_or_after: str) -> List[int]: ... def remove_aliases(network: JavaHandle, dataframe: Dataframe) -> None: ... +def remove_internal_connections(network: JavaHandle, dataframe: Dataframe) -> None: ... def close() -> None: ... def create_dynamic_simulation_context() -> JavaHandle: ... def create_dynamic_model_mapping() -> JavaHandle: ... def create_timeseries_mapping() -> JavaHandle: ... def create_event_mapping() -> JavaHandle: ... -def run_dynamic_model(dynamic_model: JavaHandle, network: JavaHandle, dynamic_mapping: JavaHandle, event_mapping: JavaHandle, timeseries_mapping: JavaHandle, start: int, stop: int) -> JavaHandle: ... -def add_all_dynamic_mappings(dynamic_mapping_handle: JavaHandle, mapping_type: DynamicMappingType, mapping_df: Dataframe) -> None: ... -def get_dynamic_mappings_meta_data(mapping_type: DynamicMappingType) -> List[SeriesMetadata]: ... +def run_dynamic_model(dynamic_model: JavaHandle, network: JavaHandle, dynamic_mapping: JavaHandle, event_mapping: JavaHandle, timeseries_mapping: JavaHandle, start: int, stop: int, report_node: Optional[JavaHandle]) -> JavaHandle: ... +def add_all_dynamic_mappings(dynamic_mapping_handle: JavaHandle, mapping_type: DynamicMappingType, dataframes: List[Optional[Dataframe]]) -> None: ... +def get_dynamic_mappings_meta_data(mapping_type: DynamicMappingType) -> List[List[SeriesMetadata]]: ... +def get_supported_models(mapping_type: DynamicMappingType) -> List[str]: ... def add_curve(curve_mapping_handle: JavaHandle, dynamic_id: str, variable: str) -> None: ... -def add_event_disconnection(event_mapping_handle: JavaHandle, static_id: str, event_time: float, disconnect_only: Side) -> None: ... +def add_all_event_mappings(event_mapping_handle: JavaHandle, mapping_type: EventMappingType, mapping_df: Dataframe) -> None: ... +def get_event_mappings_meta_data(mapping_type: EventMappingType) -> List[SeriesMetadata]: ... def set_powsybl_config_location(absolute_path_to_config:str, config_file_name: str) -> None: ... def get_dynamic_simulation_results_status(result_handle: JavaHandle) -> str: ... def get_dynamic_curve(report_handle: JavaHandle, curve_name: str) -> SeriesArray: ... diff --git a/pypowsybl/dynamic/__init__.py b/pypowsybl/dynamic/__init__.py index 3c13cdd529..f58c84c152 100644 --- a/pypowsybl/dynamic/__init__.py +++ b/pypowsybl/dynamic/__init__.py @@ -5,8 +5,7 @@ # SPDX-License-Identifier: MPL-2.0 # from .impl.curve_mapping import CurveMapping -from .impl.event_mapping import EventMapping +from .impl.event_mapping import EventMapping, EventMappingType from .impl.simulation_result import SimulationResult from .impl.simulation import Simulation -from .impl.model_mapping import ModelMapping, Side, DynamicMappingType -from .impl.util import EventType +from .impl.model_mapping import ModelMapping, DynamicMappingType diff --git a/pypowsybl/dynamic/impl/event_mapping.py b/pypowsybl/dynamic/impl/event_mapping.py index 72b8841a8e..1bf790f54d 100644 --- a/pypowsybl/dynamic/impl/event_mapping.py +++ b/pypowsybl/dynamic/impl/event_mapping.py @@ -4,10 +4,13 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # SPDX-License-Identifier: MPL-2.0 # -from typing import List +from typing import Optional +from numpy.typing import ArrayLike +from pandas import DataFrame from pypowsybl import _pypowsybl as _pp -from pypowsybl._pypowsybl import Side -from .util import EventType +from pypowsybl._pypowsybl import EventMappingType # pylint: disable=protected-access +from pypowsybl.utils import _add_index_to_kwargs, \ + _adapt_df_or_kwargs, _create_c_dataframe # pylint: disable=protected-access class EventMapping: @@ -18,17 +21,93 @@ class EventMapping: def __init__(self) -> None: self._handle = _pp.create_event_mapping() - def add_disconnection(self, static_id: str, event_time: float, disconnect_only: Side) -> None: - """ Creates a equipment disconnection event + def add_disconnection(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ Creates an equipment disconnection event Args: - static_id (str): network element to disconnect - event_time (float): timestep at which the event happens - disconnect_only (BranchSide): the disconnection is made on the provided side only + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to disconnect + - **start_time**: timestep at which the event happens + - **disconnect_only**: the disconnection is made on the provided side only for branch equipment (ONE or TWO) + + Examples: + Using keyword arguments: + + .. code-block:: python + + event_mapping.add_disconnection(static_id='LINE', start_time=3.3, disconnect_only='TWO') + """ + self._add_all_event_mappings(EventMappingType.DISCONNECT, df, **kwargs) + + def add_active_power_variation(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ Creates an equipment active power variation event + + Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the load or generator affected by the event + - **start_time**: timestep at which the event happens + - **delta_p**: active power variation + + Examples: + Using keyword arguments: + + .. code-block:: python + + event_mapping.add_active_power_variation(static_id='LOAD', start_time=14, delta_p=2) + """ + self._add_all_event_mappings(EventMappingType.ACTIVE_POWER_VARIATION, df, **kwargs) + + def add_node_fault(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ Creates a bus node fault event + + Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the bus affected by the event + - **start_time**: timestep at which the event happens + - **fault_time:** delta with start_time at which the event ends + - **r_pu**: r pu variation + - **x_pu**: x pu variation + + Examples: + Using keyword arguments: + + .. code-block:: python + + event_mapping.add_node_fault(static_id='BUS', start_time=12, fault_time=2, r_pu=0.1, x_pu=0.2) """ - _pp.add_event_disconnection( - self._handle, static_id, event_time, disconnect_only) + self._add_all_event_mappings(EventMappingType.NODE_FAULT, df, **kwargs) - @staticmethod - def get_possible_events() -> List[EventType]: - return list(EventType) + def _add_all_event_mappings(self, mapping_type: EventMappingType, mapping_df: Optional[DataFrame], **kwargs: ArrayLike) -> None: + metadata = _pp.get_event_mappings_meta_data(mapping_type) + if kwargs: + kwargs = _add_index_to_kwargs(metadata, **kwargs) + mapping_df = _adapt_df_or_kwargs(metadata, mapping_df, **kwargs) + c_mapping_df = _create_c_dataframe(mapping_df, metadata) + _pp.add_all_event_mappings(self._handle, mapping_type, c_mapping_df) diff --git a/pypowsybl/dynamic/impl/model_mapping.py b/pypowsybl/dynamic/impl/model_mapping.py index 9294fa31f9..b979e80fa5 100644 --- a/pypowsybl/dynamic/impl/model_mapping.py +++ b/pypowsybl/dynamic/impl/model_mapping.py @@ -4,12 +4,12 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # SPDX-License-Identifier: MPL-2.0 # -from typing import Union -import pandas as pd +from typing import List, Optional +from numpy.typing import ArrayLike +from pandas import DataFrame from pypowsybl import _pypowsybl as _pp -from pypowsybl._pypowsybl import DynamicMappingType, Side # pylint: disable=protected-access -from pypowsybl.utils import \ - _adapt_df_or_kwargs, _add_index_to_kwargs, _create_c_dataframe # pylint: disable=protected-access +from pypowsybl._pypowsybl import DynamicMappingType # pylint: disable=protected-access +from pypowsybl.utils import _get_c_dataframes # pylint: disable=protected-access class ModelMapping: @@ -20,101 +20,883 @@ class to map elements of a network to their respective dynamic behavior def __init__(self) -> None: self._handle = _pp.create_dynamic_model_mapping() - def add_alpha_beta_load(self, static_id: str, parameter_set_id: str) -> None: + def get_supported_models(self, mapping_type: DynamicMappingType) -> List[str]: + return _pp.get_supported_models(mapping_type) + + def add_base_load(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a load mapping + + Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_base_load(static_id='LOAD', + parameter_set_id='lab', + dynamic_model_id='DM_LOAD', + model_name='LoadPQ') + """ + self._add_all_dynamic_mappings(DynamicMappingType.BASE_LOAD, [df], **kwargs) + + def add_load_one_transformer(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a load with one transformer mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_load_one_transformer(static_id='LOAD', + parameter_set_id='lt', + dynamic_model_id='DM_LT', + model_name='LoadOneTransformer') + """ + self._add_all_dynamic_mappings(DynamicMappingType.LOAD_ONE_TRANSFORMER, [df], **kwargs) + + def add_load_one_transformer_tap_changer(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a load with one transformer and tap changer mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_load_one_transformer_tap_changer(static_id='LOAD', + parameter_set_id='lt_tc', + dynamic_model_id='DM_LT_TC', + model_name='LoadOneTransformerTapChanger') + """ + self._add_all_dynamic_mappings(DynamicMappingType.LOAD_ONE_TRANSFORMER_TAP_CHANGER, [df], **kwargs) + + def add_load_two_transformers(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a load with two transformers mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_load_two_transformers(static_id='LOAD', + parameter_set_id='ltt', + dynamic_model_id='DM_LTT', + model_name='LoadTwoTransformers') + """ + self._add_all_dynamic_mappings(DynamicMappingType.LOAD_TWO_TRANSFORMERS, [df], **kwargs) + + def add_load_two_transformers_tap_changers(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a load with two transformers and tap changers mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_load_two_transformers_tap_changers(static_id='LOAD', + parameter_set_id='ltt_tc', + dynamic_model_id='DM_LTT_TC', + model_name='LoadTwoTransformersTapChangers') + """ + self._add_all_dynamic_mappings(DynamicMappingType.LOAD_TWO_TRANSFORMERS_TAP_CHANGERS, [df], **kwargs) + + def add_base_generator(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a base generator mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_base_generator(static_id='GEN', + parameter_set_id='gen', + dynamic_model_id='DM_GEN', + model_name='GeneratorFictitious') """ - Add a alpha beta load mapping + self._add_all_dynamic_mappings(DynamicMappingType.BASE_GENERATOR, [df], **kwargs) - :param static_id: id of the network element to map - :param parameter_set_id: id of the parameter for this model given in the dynawaltz configuration + def add_synchronized_generator(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ - self.add_all_dynamic_mappings(static_id=static_id, - parameter_set_id=parameter_set_id, - mapping_type=DynamicMappingType.ALPHA_BETA_LOAD) + Add a synchronized generator mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: - def add_one_transformer_load(self, static_id: str, parameter_set_id: str) -> None: + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_synchronized_generator(static_id='GEN', + parameter_set_id='sgen', + dynamic_model_id='DM_SYNCH_GEN', + model_name='GeneratorPVFixed') """ - Add a one transformer load mapping + self._add_all_dynamic_mappings(DynamicMappingType.SYNCHRONIZED_GENERATOR, [df], **kwargs) - :param static_id: id of the network element to map - :param parameter_set_id: id of the parameter for this model given in the dynawaltz configuration + def add_synchronous_generator(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ - self.add_all_dynamic_mappings(static_id=static_id, - parameter_set_id=parameter_set_id, - mapping_type=DynamicMappingType.ONE_TRANSFORMER_LOAD) + Add a synchronous generator mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) - def add_generator_synchronous_three_windings(self, static_id: str, parameter_set_id: str) -> None: + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_synchronous_generator(static_id='GEN', + parameter_set_id='ssgen', + dynamic_model_id='DM_SYNCHRONOUS_GEN', + model_name='GeneratorSynchronousThreeWindings') """ - Add a generator synchronous three windings mapping + self._add_all_dynamic_mappings(DynamicMappingType.SYNCHRONOUS_GENERATOR, [df], **kwargs) - :param static_id: id of the network element to map - :param parameter_set_id: id of the parameter for this model given in the dynawaltz configuration + def add_wecc(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ - self.add_all_dynamic_mappings(static_id=static_id, - parameter_set_id=parameter_set_id, - mapping_type=DynamicMappingType.GENERATOR_SYNCHRONOUS_THREE_WINDINGS) + Add a WECC mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: - def add_generator_synchronous_three_windings_proportional_regulations(self, static_id: str, - parameter_set_id: str) -> None: + .. code-block:: python + + model_mapping.add_wecc(static_id='GEN', + parameter_set_id='wecc', + dynamic_model_id='DM_WECC', + model_name='WT4BWeccCurrentSource') """ - Add a generator synchronous three windings proportional regulations mapping + self._add_all_dynamic_mappings(DynamicMappingType.WECC, [df], **kwargs) - :param static_id: id of the network element to map - :param parameter_set_id: id of the parameter for this model given in the dynawaltz configuration + def add_grid_forming_converter(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ - self.add_all_dynamic_mappings(static_id=static_id, - parameter_set_id=parameter_set_id, - mapping_type=DynamicMappingType.GENERATOR_SYNCHRONOUS_THREE_WINDINGS_PROPORTIONAL_REGULATIONS) + Add a grid forming converter mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: - def add_generator_synchronous_four_windings(self, static_id: str, parameter_set_id: str) -> None: + .. code-block:: python + + model_mapping.add_grid_forming_converter(static_id='GEN', + parameter_set_id='gf', + dynamic_model_id='DM_GF', + model_name='GridFormingConverterMatchingControl') """ - Add a generator synchronous four windings mapping + self._add_all_dynamic_mappings(DynamicMappingType.GRID_FORMING_CONVERTER, [df], **kwargs) - :param static_id: id of the network element to map - :param parameter_set_id: id of the parameter for this model given in the dynawaltz configuration + def add_signal_n_generator(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ - self.add_all_dynamic_mappings(static_id=static_id, - parameter_set_id=parameter_set_id, - mapping_type=DynamicMappingType.GENERATOR_SYNCHRONOUS_FOUR_WINDINGS) + Add a signal N generator mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python - def add_generator_synchronous_four_windings_proportional_regulations(self, static_id: str, - parameter_set_id: str) -> None: + model_mapping.add_signal_n_generator(static_id='GEN', + parameter_set_id='signal_n', + dynamic_model_id='DM_SIGNAL_N', + model_name='GeneratorPVSignalN') """ - Add a generator synchronous four windings proportional regulations mapping + self._add_all_dynamic_mappings(DynamicMappingType.SIGNAL_N_GENERATOR, [df], **kwargs) + + def add_hvdc_p(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add an HVDC P mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) - :param static_id: id of the network element to map - :param parameter_set_id: id of the parameter for this model given in the dynawaltz configuration + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_hvdc_p(static_id='HVDC_LINE', + parameter_set_id='hvdc_p', + dynamic_model_id='DM_HVDC_P', + model_name='HvdcPV') """ - self.add_all_dynamic_mappings(static_id=static_id, - parameter_set_id=parameter_set_id, - mapping_type=DynamicMappingType.GENERATOR_SYNCHRONOUS_FOUR_WINDINGS_PROPORTIONAL_REGULATIONS) + self._add_all_dynamic_mappings(DynamicMappingType.HVDC_P, [df], **kwargs) - def add_current_limit_automaton(self, static_id: str, parameter_set_id: str, branch_side: Side) -> None: + def add_hvdc_vsc(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ - Add a current limit automaton mapping + Add an HVDC VSC mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: - :param branch_side: - :param static_id: id of the network element to map - :param parameter_set_id: id of the parameter for this model given in the dynawaltz configuration + .. code-block:: python + + model_mapping.add_hvdc_vsc(static_id='HVDC_LINE', + parameter_set_id='hvdc_vsc', + dynamic_model_id='DM_HVDC_VSC', + model_name='HvdcVSCDanglingP') """ - self.add_all_dynamic_mappings(static_id=static_id, - parameter_set_id=parameter_set_id, - branch_side=branch_side, - mapping_type=DynamicMappingType.CURRENT_LIMIT_AUTOMATON) + self._add_all_dynamic_mappings(DynamicMappingType.HVDC_VSC, [df], **kwargs) - def add_all_dynamic_mappings(self, mapping_type: DynamicMappingType, mapping_df: pd.DataFrame = None, - **kwargs: Union[str, Side, DynamicMappingType]) -> None: + def add_base_transformer(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ - Update the dynamic mapping of a simulation, must provide a :class:`~pandas.DataFrame` or as named arguments. + Add a transformer mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: - | The dataframe must contains these three columns: - | - static_id: id of the network element to map - | - parameter_set_id: set id in the parameter file - | - mapping_type: value of enum DynamicMappingType + .. code-block:: python + model_mapping.add_base_transformer(static_id='TFO', + parameter_set_id='tfo', + dynamic_model_id='DM_TFO', + model_name='TransformerFixedRatio') """ + self._add_all_dynamic_mappings(DynamicMappingType.BASE_TRANSFORMER, [df], **kwargs) + + def add_base_static_var_compensator(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a static var compensator mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_base_static_var_compensator(static_id='SVARC', + parameter_set_id='svarc', + dynamic_model_id='DM_SVARC', + model_name='StaticVarCompensatorPV') + """ + self._add_all_dynamic_mappings(DynamicMappingType.BASE_STATIC_VAR_COMPENSATOR, [df], **kwargs) + + def add_base_line(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a line mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + mmodel_mapping.add_base_line(static_id='LINE', + parameter_set_id='l', + dynamic_model_id='DM_LINE', + model_name='Line') + """ + self._add_all_dynamic_mappings(DynamicMappingType.BASE_LINE, [df], **kwargs) + + def add_base_bus(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a base bus mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_base_bus(static_id='BUS', + parameter_set_id='bus', + dynamic_model_id='DM_BUS', + model_name='Bus') + """ + self._add_all_dynamic_mappings(DynamicMappingType.BASE_BUS, [df], **kwargs) + + def add_infinite_bus(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add an infinite bus mapping + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **static_id**: id of the network element to map + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **dynamic_model_id**: id of the model mapping the network element (if none the static id will be used) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_infinite_bus(static_id='BUS', + parameter_set_id='inf_bus', + dynamic_model_id='DM_INF_BUS', + model_name='InfiniteBus') + """ + self._add_all_dynamic_mappings(DynamicMappingType.INFINITE_BUS, [df], **kwargs) + + def add_overload_management_system(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a dynamic overload management system (not link to a network element) + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **dynamic_model_id**: id of the overload management system + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **controlled_branch**: id of the branch controlled by the automation system + - **i_measurement**: id of the branch used for the current intensity measurement + - **i_measurement_side**: measured side of the i_measurement branch (ONE or TWO) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_overload_management_system(dynamic_model_id='DM_OV', + parameter_set_id='ov', + controlled_branch='LINE1', + i_measurement='LINE2', + i_measurement_side='TWO', + model_name='OverloadManagementSystem') + """ + self._add_all_dynamic_mappings(DynamicMappingType.OVERLOAD_MANAGEMENT_SYSTEM, [df], **kwargs) + + def add_two_levels_overload_management_system(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a dynamic two levels overload management system (not link to a network element) + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **dynamic_model_id**: id of the two levels overload management system + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **controlled_branch**: id of the branch controlled by the automation system + - **i_measurement_1**: id of the first branch used for the current intensity measurement + - **i_measurement_1_side**: measured side of the i_measurement_1 branch (ONE or TWO) + - **i_measurement_2**: id of the second branch used for the current intensity measurement + - **i_measurement_2_side**: measured side of the i_measurement_2 branch (ONE or TWO) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_two_levels_overload_management_system(dynamic_model_id='DM_TOV', + parameter_set_id='tov', + controlled_branch= 'LINE1', + i_measurement_1='LINE1', + i_measurement_1_side='TWO', + i_measurement_2='LINE2', + i_measurement_2_side='ONE', + model_name='TwoLevelsOverloadManagementSystem') + """ + self._add_all_dynamic_mappings(DynamicMappingType.TWO_LEVELS_OVERLOAD_MANAGEMENT_SYSTEM, [df], **kwargs) + + def add_under_voltage_automation_system(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a dynamic under voltage automation system (not link to a network element) + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **dynamic_model_id**: id of the under voltage automation system + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **generator**: id of the generator controlled by the automation system + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_under_voltage_automation_system(dynamic_model_id='DM_UV', + parameter_set_id='psi', + generator='GEN', + model_name='UnderVoltage' + """ + self._add_all_dynamic_mappings(DynamicMappingType.UNDER_VOLTAGE, [df], **kwargs) + + def add_phase_shifter_i_automation_system(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a dynamic phase shifter I automation system (not link to a network element) + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **dynamic_model_id**: id of the phase shifter I automation system + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **transformer**: id of the transformer controlled by the automation system + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_phase_shifter_i_automation_system(dynamic_model_id='DM_PS_I', + parameter_set_id='psi', + transformer='TRA', + model_name='PhaseShifterI') + """ + self._add_all_dynamic_mappings(DynamicMappingType.PHASE_SHIFTER_I, [df], **kwargs) + + def add_phase_shifter_p_automation_system(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a dynamic phase shifter P automation system (not link to a network element) + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **dynamic_model_id**: id of the phase shifter P automation system + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **transformer**: id of the transformer controlled by the automation system + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_phase_shifter_p_automation_system(dynamic_model_id='DM_PS_P', + parameter_set_id='ov', + transformer='TRA', + model_name='PhaseShifterP') + """ + self._add_all_dynamic_mappings(DynamicMappingType.PHASE_SHIFTER_P, [df], **kwargs) + + def add_phase_shifter_blocking_i_automation_system(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a dynamic phase shifter blocking I automation system (not link to a network element) + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **dynamic_model_id**: id of the phase shifter blocking I automation system + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **phase_shifter_id**: id of the phase shifter I automation system controlled by the automation system + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_phase_shifter_blocking_i_automation_system(dynamic_model_id='DM_PSB_I', + parameter_set_id='psb', + phase_shifter_id='PSI', + model_name='PhaseShifterBlockingI') + """ + self._add_all_dynamic_mappings(DynamicMappingType.PHASE_SHIFTER_BLOCKING_I, [df], **kwargs) + + def add_tap_changer_automation_system(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Add a dynamic tap changer automation system (not link to a network element) + + :Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **dynamic_model_id**: id of the tap changer automation system + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **static_id**: id of the load on which the tap changer is added + - **side**: transformer side of the tap changer (HIGH_VOLTAGE, LOW_VOLTAGE or NONE) + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Examples: + Using keyword arguments: + + .. code-block:: python + + model_mapping.add_tap_changer_automation_system(dynamic_model_id='DM_TC', + parameter_set_id='tc', + static_id='LOAD', + side='HIGH_VOLTAGE', + model_name='TapChangerAutomaton') + """ + self._add_all_dynamic_mappings(DynamicMappingType.TAP_CHANGER, [df], **kwargs) + + def add_tap_changer_blocking_automation_system(self, df: DataFrame, tfo_df: DataFrame, mp1_df: DataFrame, + mp2_df: DataFrame = None, mp3_df: DataFrame = None, + mp5_df: DataFrame = None, mp4_df: DataFrame = None) -> None: + """ + Add a dynamic tap changer blocking automation system (not link to a network element) + + :Args: + df: Primary attributes as a dataframe. + tfo_df: Dataframe for transformer data. + mpN_df: Dataframes for a measurement point data, the automation system can handle up to 5 measurement points, + at least 1 measurement point is expected. For each measurement point dataframe, alternative points can be input + (for example bus or busbar section) the first energized element found in the network will be used + + Notes: + + Valid attributes for the primary dataframes are: + + - **dynamic_model_id**: id of the tap changer blocking automation system + - **parameter_set_id**: id of the parameter for this model given in the dynawo configuration + - **model_name**: name of the model used for the mapping (if none the default model will be used) + + Valid attributes for the transformer dataframes are: + - **dynamic_model_id**: id of the tap changer blocking automation system + - **transformer_id**: id of a transformer controlled by the automation system + + Valid attributes for the measurement point dataframes are: + - **dynamic_model_id**: id of the tap changer blocking automation system + - **measurement_point_id**: id of the bus or busbar section used for the voltage measurement + + Examples: + + We need to provide 2 dataframes, 1 for tap changer blocking automation system basic data, and one for transformer data: + + .. code-block:: python + + df = pd.DataFrame.from_records( + index='dynamic_model_id', + columns=['dynamic_model_id', 'parameter_set_id', 'u_measurements', 'model_name'], + data=[('DM_TCB', 'tcb', 'BUS', 'TapChangerBlockingAutomaton')]) + tfo_df = pd.DataFrame.from_records( + index='dynamic_model_id', + columns=['dynamic_model_id', 'transformer_id'], + data=[('DM_TCB', 'TFO1'), + ('DM_TCB', 'TFO2'), + ('DM_TCB', 'TFO3')]) + measurement1_df = pd.DataFrame.from_records( + index='dynamic_model_id', + columns=['dynamic_model_id', 'measurement_point_id'], + data=[('DM_TCB', 'B1'), + ('DM_TCB', 'BS1')]) + measurement2_df = pd.DataFrame.from_records( + index='dynamic_model_id', + columns=['dynamic_model_id', 'measurement_point_id'], + data=[('DM_TCB', 'B4')]) + model_mapping.add_tap_changer_blocking_automation_system(df, tfo_df, measurement1_df, measurement2_df) + """ + dfs = [df, tfo_df, mp1_df, mp2_df, mp3_df, mp4_df, mp5_df] + self._add_all_dynamic_mappings(DynamicMappingType.TAP_CHANGER_BLOCKING, [DataFrame() if df is None else df for df in dfs]) + + def _add_all_dynamic_mappings(self, mapping_type: DynamicMappingType, mapping_dfs: List[Optional[DataFrame]], **kwargs: ArrayLike) -> None: metadata = _pp.get_dynamic_mappings_meta_data(mapping_type) - if kwargs: - kwargs = _add_index_to_kwargs(metadata, **kwargs) - mapping_df = _adapt_df_or_kwargs(metadata, mapping_df, **kwargs) - c_mapping_df = _create_c_dataframe(mapping_df, metadata) - _pp.add_all_dynamic_mappings(self._handle, mapping_type, c_mapping_df) + c_dfs = _get_c_dataframes(mapping_dfs, metadata, **kwargs) + _pp.add_all_dynamic_mappings(self._handle, mapping_type, c_dfs) diff --git a/pypowsybl/dynamic/impl/simulation.py b/pypowsybl/dynamic/impl/simulation.py index f817145305..50a4baf6ca 100644 --- a/pypowsybl/dynamic/impl/simulation.py +++ b/pypowsybl/dynamic/impl/simulation.py @@ -6,6 +6,7 @@ # from pypowsybl.network import Network from pypowsybl import _pypowsybl as _pp +from pypowsybl.report import ReportNode from .event_mapping import EventMapping from .model_mapping import ModelMapping from .simulation_result import SimulationResult @@ -23,8 +24,9 @@ def run(self, timeseries_mapping: CurveMapping, start: int, stop: int, + report_node: ReportNode = None ) -> SimulationResult: - """Run the dynawaltz simulation""" + """Run the dynawo simulation""" return SimulationResult( _pp.run_dynamic_model( self._handle, @@ -32,5 +34,7 @@ def run(self, model_mapping._handle, # pylint: disable=protected-access event_mapping._handle, # pylint: disable=protected-access timeseries_mapping._handle, # pylint: disable=protected-access - start, stop) + start, + stop, + None if report_node is None else report_node._report_node) # pylint: disable=protected-access ) diff --git a/pypowsybl/dynamic/impl/simulation_result.py b/pypowsybl/dynamic/impl/simulation_result.py index fbb69c10d3..cf2a38beb9 100644 --- a/pypowsybl/dynamic/impl/simulation_result.py +++ b/pypowsybl/dynamic/impl/simulation_result.py @@ -37,4 +37,4 @@ def _get_all_curves(self) -> pd.DataFrame: curve_name_lst = _pp.get_all_dynamic_curves_ids(self._handle) df_curves = [self._get_curve(curve_name) for curve_name in curve_name_lst] - return pd.concat(df_curves, axis=1) + return pd.concat(df_curves, axis=1) if df_curves else pd.DataFrame() diff --git a/pypowsybl/dynamic/impl/util.py b/pypowsybl/dynamic/impl/util.py deleted file mode 100644 index d2283c2847..0000000000 --- a/pypowsybl/dynamic/impl/util.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2023, RTE (http://www.rte-france.com) -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# SPDX-License-Identifier: MPL-2.0 -# -from enum import Enum - - -class EventType(Enum): - DISCONNECTION = 'DISCONNECTION' diff --git a/pypowsybl/loadflow/impl/loadflow.py b/pypowsybl/loadflow/impl/loadflow.py index c6ea7902fb..998c166b17 100644 --- a/pypowsybl/loadflow/impl/loadflow.py +++ b/pypowsybl/loadflow/impl/loadflow.py @@ -135,12 +135,16 @@ def get_provider_parameters(provider: str = None) -> DataFrame: .. doctest:: >>> parameters = pp.loadflow.get_provider_parameters('OpenLoadFlow') + >>> parameters['category_key']['slackBusSelectionMode'] + 'SlackDistribution' >>> parameters['description']['slackBusSelectionMode'] 'Slack bus selection mode' >>> parameters['type']['slackBusSelectionMode'] 'STRING' >>> parameters['default']['slackBusSelectionMode'] 'MOST_MESHED' + >>> parameters['possible_values']['slackBusSelectionMode'] + '[FIRST, MOST_MESHED, NAME, LARGEST_GENERATOR]' """ series_array = _pypowsybl.create_loadflow_provider_parameters_series_array('' if provider is None else provider) return create_data_frame_from_series_array(series_array) diff --git a/pypowsybl/network/__init__.py b/pypowsybl/network/__init__.py index c84e521a05..60c1c27c9f 100644 --- a/pypowsybl/network/__init__.py +++ b/pypowsybl/network/__init__.py @@ -26,7 +26,9 @@ create_ieee118, create_ieee300, create_eurostag_tutorial_example1_network, + create_eurostag_tutorial_example1_with_more_generators_network, create_eurostag_tutorial_example1_with_power_limits_network, + create_eurostag_tutorial_example1_with_tie_lines_and_areas, create_four_substations_node_breaker_network_with_extensions, create_four_substations_node_breaker_network, create_micro_grid_be_network, @@ -41,7 +43,9 @@ get_extensions_names, get_single_line_diagram_component_library_names, get_import_formats, + get_import_supported_extensions, get_export_formats, + get_import_post_processors, get_import_parameters, get_export_parameters, get_extensions_information @@ -72,3 +76,4 @@ remove_feeder_bays ) from .impl.perunit import (PerUnitView, per_unit_view) +from .impl.pandapower_converter import convert_from_pandapower diff --git a/pypowsybl/network/impl/bus_breaker_topology.py b/pypowsybl/network/impl/bus_breaker_topology.py index 956730d801..2f49860c27 100644 --- a/pypowsybl/network/impl/bus_breaker_topology.py +++ b/pypowsybl/network/impl/bus_breaker_topology.py @@ -32,13 +32,29 @@ def __init__(self, network_handle: _pp.JavaHandle, voltage_level_id: str): def switches(self) -> DataFrame: """ The list of switches of the bus breaker view, together with their connection status, as a dataframe. + + The dataframe includes the following columns: + + - **kind**: Switch kind (BREAKER, DISCONNECTOR, ...) + - **open**: True if the switch is opened + - **bus1_id**: node where the switch is connected at side 1 + - **bus2_id**: node where the switch is connected at side 2 + + This dataframe is indexed by the id of the switches. """ return self._switchs @property def buses(self) -> DataFrame: """ - The list of buses of the bus breaker view, as a dataframe. + The list of buses of the bus breaker view, as a dataframe. + + The dataframe includes the following columns: + + - **name**: Name of the bus breaker view bus + - **bus_id**: id of the corresponding bus in the bus view. + + This dataframe is indexed by the id of the bus breaker view bus. """ return self._buses @@ -47,6 +63,14 @@ def elements(self) -> DataFrame: """ The list of elements (lines, generators...) of this voltage level, together with the bus of the bus breaker view where they are connected. + + The dataframe includes the following columns: + + - **type**: Type of the connected element (GENERATOR, LINE, ...) + - **bus_id**: bus id of the bus breaker view + - **side**: Side of the connected element + + This dataframe is indexed by the id of the connected elements. """ return self._elements diff --git a/pypowsybl/network/impl/network.py b/pypowsybl/network/impl/network.py index 2beae6d3ad..91e69226f2 100644 --- a/pypowsybl/network/impl/network.py +++ b/pypowsybl/network/impl/network.py @@ -127,7 +127,7 @@ def __getstate__(self) -> Dict[str, Any]: 'nominal_apparent_power': self._nominal_apparent_power} def __setstate__(self, state: Dict[str, Any]) -> None: - self._handle = _pp.load_network_from_binary_buffers([state['biidm'].getbuffer()], {}, None) + self._handle = _pp.load_network_from_binary_buffers([state['biidm'].getbuffer()], {}, [], None) self._per_unit = state['per_unit'] self._nominal_apparent_power = state['nominal_apparent_power'] self.__init_from_handle() @@ -349,7 +349,7 @@ def write_network_area_diagram_svg(self, svg_file: PathOrStr, voltage_level_ids: edge_name_displayed: bool = False) -> None: """ .. deprecated:: 1.1.0 - Use :class:`write_network_area_diagram_svg` with `NadParameters` instead. + Use :class:`write_network_area_diagram` with `NadParameters` instead. Create a network area diagram in SVG format and write it to a file. Args: @@ -367,12 +367,14 @@ def write_network_area_diagram_svg(self, svg_file: PathOrStr, voltage_level_ids: def write_network_area_diagram(self, svg_file: PathOrStr, voltage_level_ids: Union[str, List[str]] = None, depth: int = 0, high_nominal_voltage_bound: float = -1, low_nominal_voltage_bound: float = -1, - nad_parameters: NadParameters = None) -> None: + nad_parameters: NadParameters = None, + metadata_file: PathOrStr = None) -> None: """ Create a network area diagram in SVG format and write it to a file. Args: svg_file: a svg file path + metadata_file: a json metadata file path (optional) voltage_level_ids: the voltage level ID, center of the diagram (None for the full diagram) depth: the diagram depth around the voltage level high_nominal_voltage_bound: high bound to filter voltage level according to nominal voltage @@ -385,8 +387,8 @@ def write_network_area_diagram(self, svg_file: PathOrStr, voltage_level_ids: Uni if isinstance(voltage_level_ids, str): voltage_level_ids = [voltage_level_ids] nad_p = nad_parameters._to_c_parameters() if nad_parameters is not None else _pp.NadParameters() # pylint: disable=protected-access - _pp.write_network_area_diagram_svg(self._handle, svg_file, voltage_level_ids, depth, high_nominal_voltage_bound, - low_nominal_voltage_bound, nad_p) + _pp.write_network_area_diagram_svg(self._handle, svg_file, '' if metadata_file is None else path_to_str(metadata_file), + voltage_level_ids, depth, high_nominal_voltage_bound, low_nominal_voltage_bound, nad_p) def get_network_area_diagram(self, voltage_level_ids: Union[str, List[str]] = None, depth: int = 0, high_nominal_voltage_bound: float = -1, low_nominal_voltage_bound: float = -1, @@ -409,9 +411,11 @@ def get_network_area_diagram(self, voltage_level_ids: Union[str, List[str]] = No if isinstance(voltage_level_ids, str): voltage_level_ids = [voltage_level_ids] nad_p = nad_parameters._to_c_parameters() if nad_parameters is not None else _pp.NadParameters() # pylint: disable=protected-access - return Svg(_pp.get_network_area_diagram_svg(self._handle, voltage_level_ids, depth, + svg_and_metadata: List[str] = _pp.get_network_area_diagram_svg_and_metadata(self._handle, voltage_level_ids, depth, high_nominal_voltage_bound, low_nominal_voltage_bound, - nad_p)) + nad_p) + return Svg(svg_and_metadata[0], svg_and_metadata[1]) + def get_network_area_diagram_displayed_voltage_levels(self, voltage_level_ids: Union[str, List[str]], depth: int = 0) -> List[str]: @@ -534,11 +538,14 @@ def get_buses(self, all_attributes: bool = False, attributes: List[str] = None, - **v_mag**: Get the voltage magnitude of the bus (in kV) - **v_angle**: the voltage angle of the bus (in degree) - - **connected_component**: the number of terminals connected to this bus - - **synchronous_component**: the number of synchronous components that the bus is part of + - **connected_component**: The connected component to which the bus belongs + - **synchronous_component**: The synchronous component to which the bus belongs - **voltage_level_id**: at which substation the bus is connected - This dataframe is indexed on the bus ID. + This dataframe is indexed on the bus ID in the bus view. + + See Also: + :meth:`get_bus_breaker_view_buses` Examples: @@ -601,8 +608,75 @@ def get_buses(self, all_attributes: bool = False, attributes: List[str] = None, def get_bus_breaker_view_buses(self, all_attributes: bool = False, attributes: List[str] = None, **kwargs: ArrayLike) -> DataFrame: r""" - Get a dataframe of buses from the bus/breaker view. - See :meth:`get_buses` for documentation as attributes are the same. + Get a dataframe of buses from the bus/breaker view. + + Args: + all_attributes: flag for including all attributes in the dataframe, default is false + attributes: attributes to include in the dataframe. The 2 parameters are mutually exclusive. + If no parameter is specified, the dataframe will include the default attributes. + kwargs: the data to be selected, as named arguments. + + Returns: + A dataframe of buses from the bus/breaker view + + Notes: + The resulting dataframe, depending on the parameters, will include the following columns: + + - **v_mag**: Get the voltage magnitude of the bus (in kV) + - **v_angle**: the voltage angle of the bus (in degree) + - **connected_component**: The connected component to which the bus belongs + - **synchronous_component**: The synchronous component to which the bus belongs + - **voltage_level_id**: at which substation the bus is connected + - **bus_id**: the bus ID in the bus view + + This dataframe is indexed on the bus ID in the bus/breaker view. + + See Also: + :meth:`get_buses` + + Examples: + + .. code-block:: python + + net = pp.network.create_four_substations_node_breaker_network() + net.get_bus_breaker_view_buses() + + It outputs something like: + + ======== ==== ========= ======== ==================== ====================== ================ ======== + \ name v_mag v_angle connected_component synchronous_component voltage_level_id bus_id + ======== ==== ========= ======== ==================== ====================== ================ ======== + id + S1VL1_0 224.6139 2.2822 0 1 S1VL1 S1VL1_0 + S1VL1_2 224.6139 2.2822 0 1 S1VL1 S1VL1_0 + S1VL1_4 224.6139 2.2822 0 1 S1VL1 S1VL1_0 + S1VL2_0 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S1VL2_1 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S1VL2_3 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S1VL2_5 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S1VL2_7 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S1VL2_9 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S1VL2_11 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S1VL2_13 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S1VL2_15 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S1VL2_17 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S1VL2_19 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S1VL2_21 400.0000 0.0000 0 1 S1VL2 S1VL2_0 + S2VL1_0 408.8470 0.7347 0 0 S2VL1 S2VL1_0 + S2VL1_2 408.8470 0.7347 0 0 S2VL1 S2VL1_0 + S2VL1_4 408.8470 0.7347 0 0 S2VL1 S2VL1_0 + S2VL1_6 408.8470 0.7347 0 0 S2VL1 S2VL1_0 + S3VL1_0 400.0000 0.0000 0 0 S3VL1 S3VL1_0 + S3VL1_2 400.0000 0.0000 0 0 S3VL1 S3VL1_0 + S3VL1_4 400.0000 0.0000 0 0 S3VL1 S3VL1_0 + S3VL1_6 400.0000 0.0000 0 0 S3VL1 S3VL1_0 + S3VL1_8 400.0000 0.0000 0 0 S3VL1 S3VL1_0 + S3VL1_10 400.0000 0.0000 0 0 S3VL1 S3VL1_0 + S4VL1_0 400.0000 -1.1259 0 0 S4VL1 S4VL1_0 + S4VL1_6 400.0000 -1.1259 0 0 S4VL1 S4VL1_0 + S4VL1_2 400.0000 -1.1259 0 0 S4VL1 S4VL1_0 + S4VL1_4 400.0000 -1.1259 0 0 S4VL1 S4VL1_0 + ======== ==== ========= ======== ==================== ====================== ================ ======== """ return self.get_elements(ElementType.BUS_FROM_BUS_BREAKER_VIEW, all_attributes, attributes, **kwargs) @@ -827,6 +901,34 @@ def get_loads(self, all_attributes: bool = False, attributes: List[str] = None, """ return self.get_elements(ElementType.LOAD, all_attributes, attributes, **kwargs) + def get_grounds(self, all_attributes: bool = False, attributes: List[str] = None, + **kwargs: ArrayLike) -> DataFrame: + r""" + Get a dataframe of grounds. + + Args: + all_attributes: flag for including all attributes in the dataframe, default is false + attributes: attributes to include in the dataframe. The 2 parameters are mutually exclusive. + If no parameter is specified, the dataframe will include the default attributes. + kwargs: the data to be selected, as named arguments. + + Returns: + the grounds dataframe + + Notes: + The resulting dataframe, depending on the parameters, will include the following columns: + + - **voltage_level_id**: at which substation this ground is connected + - **bus_id**: bus where this ground is connected + - **bus_breaker_bus_id** (optional): bus of the bus-breaker view where this ground is connected + - **node** (optional): node where this ground is connected, in node-breaker voltage levels + - **connected**: ``True`` if the ground is connected to a bus + + This dataframe is indexed on the ground ID. + + """ + return self.get_elements(ElementType.GROUND, all_attributes, attributes, **kwargs) + def get_batteries(self, all_attributes: bool = False, attributes: List[str] = None, **kwargs: ArrayLike) -> DataFrame: r""" @@ -903,7 +1005,9 @@ def get_lines(self, all_attributes: bool = False, attributes: List[str] = None, - **node2** (optional): node where this line is connected on side 2, in node-breaker voltage levels - **connected1**: ``True`` if the side "1" of the line is connected to a bus - **connected2**: ``True`` if the side "2" of the line is connected to a bus - - **fictitious** (optional): ``True`` if the line is part of the model and not of the actual network + - **fictitious** (optional): ``True`` if the line is part of the model and not of the actual network + - **selected_limits_group_1** (optional): Name of the selected operational limits group selected for side 1 + - **selected_limits_group_2** (optional): Name of the selected operational limits group selected for side 2 This dataframe is indexed by the id of the lines. @@ -997,6 +1101,8 @@ def get_2_windings_transformers(self, all_attributes: bool = False, attributes: - **connected1**: ``True`` if the side "1" of the transformer is connected to a bus - **connected2**: ``True`` if the side "2" of the transformer is connected to a bus - **fictitious** (optional): ``True`` if the transformer is part of the model and not of the actual network + - **selected_limits_group_1** (optional): Name of the selected operational limits group selected for side 1 + - **selected_limits_group_2** (optional): Name of the selected operational limits group selected for side 2 This dataframe is indexed by the id of the two windings transformers @@ -1243,7 +1349,7 @@ def get_dangling_lines(self, all_attributes: bool = False, attributes: List[str] .. code-block:: python - net = pp.network._create_dangling_lines_network() + net = pp.network.create_dangling_lines_network() net.get_dangling_lines() will output something like: @@ -1257,7 +1363,7 @@ def get_dangling_lines(self, all_attributes: bool = False, attributes: List[str] .. code-block:: python - net = pp.network._create_dangling_lines_network() + net = pp.network.create_dangling_lines_network() net.get_dangling_lines(all_attributes=True) will output something like: @@ -1271,7 +1377,7 @@ def get_dangling_lines(self, all_attributes: bool = False, attributes: List[str] .. code-block:: python - net = pp.network._create_dangling_lines_network() + net = pp.network.create_dangling_lines_network() net.get_dangling_lines(attributes=['p','q','i','voltage_level_id','bus_id','connected']) will output something like: @@ -1285,6 +1391,35 @@ def get_dangling_lines(self, all_attributes: bool = False, attributes: List[str] """ return self.get_elements(ElementType.DANGLING_LINE, all_attributes, attributes, **kwargs) + def get_dangling_lines_generation(self, all_attributes: bool = False, attributes: List[str] = None, + **kwargs: ArrayLike) -> DataFrame: + r""" + Get a dataframe of dangling lines generation part. + + Args: + all_attributes: flag for including all attributes in the dataframe, default is false + attributes: attributes to include in the dataframe. The 2 parameters are mutually exclusive. + If no parameter is specified, the dataframe will include the default attributes. + kwargs: the data to be selected, as named arguments. + + Returns: + A dataframe of dangling lines generation part. + + Notes: + The resulting dataframe, depending on the parameters, will include the following columns: + + - **min_p**: Minimum active power output of the dangling line's generation part + - **max_p**: Maximum active power output of the dangling line's generation part + - **target_p**: Active power target of the generation part + - **target_q**: Reactive power target of the generation part + - **target_v**: Voltage target of the generation part + - **voltage_regulator_on**: ``True`` if the generation part regulates voltage + + This dataframe is indexed by the id of the dangling lines + + """ + return self.get_elements(ElementType.DANGLING_LINE_GENERATION, all_attributes, attributes, **kwargs) + def get_tie_lines(self, all_attributes: bool = False, attributes: List[str] = None, **kwargs: ArrayLike) -> DataFrame: r""" @@ -2195,7 +2330,7 @@ def get_phase_tap_changers(self, all_attributes: bool = False, attributes: List[ - **regulation_mode**: regulation mode, among CURRENT_LIMITER, ACTIVE_POWER_CONTROL, and FIXED_TAP - **regulation_value**: the target value, in A or MW, depending on regulation_mode - **target_deadband**: the regulation deadband around the target value - - **regulationg_bus_id**: the bus where the phase shifter regulates + - **regulating_bus_id**: the bus where the phase shifter regulates - **regulated_side** (optional): the side bus where the phase shifter regulates current or active power - **fictitious** (optional): ``True`` if the tap changer is part of the model and not of the actual network @@ -2369,8 +2504,10 @@ def get_branches(self, all_attributes: bool = False, attributes: List[str] = Non - **p2**: the active flow on the branch at its "2" side, ``NaN`` if no loadflow has been computed (in MW) - **q2**: the reactive flow on the branch at its "2" side, ``NaN`` if no loadflow has been computed (in MVAr) - **i2**: the current on the branch at its "2" side, ``NaN`` if no loadflow has been computed (in A) + - **selected_limits_group_1** (optional): Name of the selected operational limits group selected for side 1 + - **selected_limits_group_2** (optional): Name of the selected operational limits group selected for side 2 - This dataframe is indexed on the branche ID. + This dataframe is indexed on the branch ID. """ return self.get_elements(ElementType.BRANCH, all_attributes, attributes, **kwargs) @@ -2397,6 +2534,159 @@ def get_terminals(self, all_attributes: bool = False, attributes: List[str] = No """ return self.get_elements(ElementType.TERMINAL, all_attributes, attributes) + def get_areas(self, all_attributes: bool = False, attributes: List[str] = None, + **kwargs: ArrayLike) -> DataFrame: + r""" + Get a dataframe of areas. + + Args: + all_attributes: flag for including all attributes in the dataframe, default is false + attributes: attributes to include in the dataframe. The 2 parameters are mutually exclusive. + If no parameter is specified, the dataframe will include the default attributes. + kwargs: the data to be selected, as named arguments. + + Returns: + the areas dataframe + + See Also: + - :meth:`get_areas_voltage_levels` to retrieve the voltage levels of the areas + - :meth:`get_areas_boundaries` to retrieve the voltage levels of the areas boundaries + - :meth:`create_areas` to create areas + - :meth:`update_areas` to update areas + + Notes: + The resulting dataframe, depending on the parameters, will include the following columns: + + - **area_type**: the type of area (e.g. ControlArea, BiddingZone, ...) + - **interchange_target**: target active power interchange (MW) + - **interchange**: total (AC + DC) active power interchange, in load sign convention (negative is export, positive is import) (MW) + - **ac_interchange**: AC active power interchange, in load sign convention (negative is export, positive is import) (MW) + - **dc_interchange**: DC active power interchange, in load sign convention (negative is export, positive is import) (MW) + - **fictitious** (optional): ``True`` if the area is part of the model and not of the actual network + + This dataframe is indexed on the area ID. + + Examples: + + .. code-block:: python + + net = pp.network.create_eurostag_tutorial_example1_with_tie_lines_and_areas() + net.get_areas() + + will output something like: + + ============= ============== =========== ================== =========== ============== ============== + \ name area_type interchange_target interchange ac_interchange dc_interchange + ============= ============== =========== ================== =========== ============== ============== + id + ControlArea_A Control Area A ControlArea -602.6 -602.948693 -602.948693 0.0 + ControlArea_B Control Area B ControlArea 602.6 602.944639 602.944639 0.0 + Region_AB Region AB Region NaN 0.000000 0.000000 0.0 + ============= ============== =========== ================== =========== ============== ============== + """ + return self.get_elements(ElementType.AREA, all_attributes, attributes, **kwargs) + + def get_areas_voltage_levels(self, all_attributes: bool = False, attributes: List[str] = None, + **kwargs: ArrayLike) -> DataFrame: + r""" + Get a dataframe of areas voltage levels. + + Args: + all_attributes: flag for including all attributes in the dataframe, default is false + attributes: attributes to include in the dataframe. The 2 parameters are mutually exclusive. + If no parameter is specified, the dataframe will include the default attributes. + kwargs: the data to be selected, as named arguments. + + Returns: + the areas voltage levels dataframe + + See Also: + :meth:`create_areas_voltage_levels` + + Notes: + The resulting dataframe, depending on the parameters, will include the following columns: + + - **id**: area identifier + - **voltage_level_id**: voltage level identifier + + This dataframe is indexed on the area ID. + + Examples: + + .. code-block:: python + + net = pp.network.create_eurostag_tutorial_example1_with_tie_lines_and_areas() + net.get_areas_voltage_levels() + + will output something like: + + ============= ================ + \ voltage_level_id + ============= ================ + id + ControlArea_A VLGEN + ControlArea_A VLHV1 + ControlArea_B VLHV2 + ControlArea_B VLLOAD + Region_AB VLGEN + Region_AB VLHV1 + Region_AB VLHV2 + Region_AB VLLOAD + ============= ================ + """ + return self.get_elements(ElementType.AREA_VOLTAGE_LEVELS, all_attributes, attributes, **kwargs) + + def get_areas_boundaries(self, all_attributes: bool = False, attributes: List[str] = None, + **kwargs: ArrayLike) -> DataFrame: + r""" + Get a dataframe of areas boundaries. + + Args: + all_attributes: flag for including all attributes in the dataframe, default is false + attributes: attributes to include in the dataframe. The 2 parameters are mutually exclusive. + If no parameter is specified, the dataframe will include the default attributes. + kwargs: the data to be selected, as named arguments. + + Returns: + the areas boundaries dataframe + + See Also: + :meth:`create_areas_boundaries` + + Notes: + The resulting dataframe, depending on the parameters, will include the following columns: + + - **id**: area identifier + - **boundary_type** (optional): either `DANGLING_LINE` or `TERMINAL` + - **element**: either identifier of the Dangling Line or the equipment terminal + - **side** (optional): equipment side + - **ac**: True if the boundary is considered as AC and not DC + - **p**: Active power at boundary (MW) + - **q**: Reactive power at boundary (MW) + + This dataframe is indexed on the area ID. + + Examples: + + .. code-block:: python + + net = pp.network.create_eurostag_tutorial_example1_with_tie_lines_and_areas() + net.get_areas_boundaries() + + will output something like: + + ============= ================ ===== =========== =========== + \ element ac p q + ============= ================ ===== =========== =========== + id + ControlArea_A NHV1_XNODE1 True -301.474347 -116.518644 + ControlArea_A NVH1_XNODE2 True -301.474347 -116.518644 + ControlArea_B XNODE1_NHV2 True 301.472320 116.434157 + ControlArea_B XNODE2_NHV2 True 301.472320 116.434157 + ============= ================ ===== =========== =========== + """ + return self.get_elements(ElementType.AREA_BOUNDARIES, all_attributes, attributes, **kwargs) + def _update_elements(self, element_type: ElementType, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ Update network elements with data provided as a :class:`~pandas.DataFrame` or as named arguments.for a specified element type. @@ -2547,6 +2837,33 @@ def update_loads(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ return self._update_elements(ElementType.LOAD, df, **kwargs) + def update_grounds(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Update grounds with data provided as a :class:`~pandas.DataFrame` or as named arguments. + + Args: + df: the data to be updated, as a dataframe. + kwargs: the data to be updated, as named arguments. + Arguments can be single values or any type of sequence. + In the case of sequences, all arguments must have the same length. + + Notes: + Attributes that can be updated are: + + - `connected` + + See Also: + :meth:`get_grounds` + + Examples: + Some examples using keyword arguments: + + .. code-block:: python + + network.update_grounds(id='L-1', connected=False) + """ + return self._update_elements(ElementType.GROUND, df, **kwargs) + def update_batteries(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ Update batteries with data provided as a :class:`~pandas.DataFrame` or as named arguments. @@ -2605,6 +2922,7 @@ def update_dangling_lines(self, df: DataFrame = None, **kwargs: ArrayLike) -> No - `fictitious` - `pairing_key` - `bus_breaker_bus_id` if the dangling line is in a voltage level with `BUS_BREAKER` topology + - `selected_limits_group` See Also: :meth:`get_dangling_lines` @@ -2619,6 +2937,39 @@ def update_dangling_lines(self, df: DataFrame = None, **kwargs: ArrayLike) -> No """ return self._update_elements(ElementType.DANGLING_LINE, df, **kwargs) + def update_dangling_lines_generation(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Update dangling lines generation part with data provided as a :class:`~pandas.DataFrame` or as named arguments. + + Args: + df: the data to be updated, as a dataframe. + kwargs: the data to be updated, as named arguments. + Arguments can be single values or any type of sequence. + In the case of sequences, all arguments must have the same length. + + Notes: + Attributes that can be updated are: + + - `min_p` + - `max_p` + - `target_p` + - `target_q` + - `target_v` + - `voltage_regulator_on` + + See Also: + :meth:`get_dangling_lines_generation` + + Examples: + Some examples using keyword arguments: + + .. code-block:: python + + network.update_dangling_lines_generation(id='DL', voltage_regulator_on=True, target_v=225) + network.update_dangling_lines_generation(id=['DL', 'DL2'], voltage_regulator_on=[True, True], target_v=[225, 400]) + """ + return self._update_elements(ElementType.DANGLING_LINE_GENERATION, df, **kwargs) + def update_vsc_converter_stations(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ Update VSC converter stations with data provided as a :class:`~pandas.DataFrame` or as named arguments. @@ -2786,6 +3137,8 @@ def update_lines(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: - `connected1` - `connected2` - `fictitious` + - `selected_limits_group_1` + - `selected_limits_group_2` See Also: :meth:`get_lines` @@ -2827,6 +3180,8 @@ def update_2_windings_transformers(self, df: DataFrame = None, **kwargs: ArrayLi - `connected1` - `connected2` - `fictitious` + - `selected_limits_group_1` + - `selected_limits_group_2` See Also: :meth:`get_2_windings_transformers` @@ -2865,6 +3220,7 @@ def update_3_windings_transformers(self, df: DataFrame = None, **kwargs: ArrayLi - `connected1` - `ratio_tap_position1` - `phase_tap_position1` + - `selected_limits_group_1` - `r2` - `x2` - `g2` @@ -2876,6 +3232,7 @@ def update_3_windings_transformers(self, df: DataFrame = None, **kwargs: ArrayLi - `connected2` - `ratio_tap_position2` - `phase_tap_position2` + - `selected_limits_group_2` - `r3` - `x3` - `g3` @@ -2887,6 +3244,7 @@ def update_3_windings_transformers(self, df: DataFrame = None, **kwargs: ArrayLi - `connected3` - `ratio_tap_position3` - `phase_tap_position3` + - `selected_limits_group_3` - `fictitious` See Also: @@ -3308,6 +3666,35 @@ def update_tie_lines(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ return self._update_elements(ElementType.TIE_LINE, df, **kwargs) + def update_areas(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Update areas with data provided as a :class:`~pandas.DataFrame` or as named arguments. + + Args: + df: the data to be updated, as a dataframe. + kwargs: the data to be updated, as named arguments. + Arguments can be single values or any type of sequence. + In the case of sequences, all arguments must have the same length. + + Notes: + Attributes that can be updated are: + + - `interchange_target` + - `fictitious` + + See Also: + :meth:`get_areas` + + Examples: + Some examples using keyword arguments: + + .. code-block:: python + + network.update_areas(id='ControlArea_A', interchange_target=-500) + network.update_areas(id=['ControlArea_A', 'ControlArea_B'], interchange_target=[-500, 500]) + """ + return self._update_elements(ElementType.AREA, df, **kwargs) + def update_extensions(self, extension_name: str, df: DataFrame = None, table_name: str = "", **kwargs: ArrayLike) -> None: """ @@ -3422,7 +3809,7 @@ def get_current_limits(self, all_attributes: bool = False, attributes: List[str] columns.append('fictitious') return current_limits[columns] - def get_operational_limits(self, all_attributes: bool = False, attributes: List[str] = None) -> DataFrame: + def get_operational_limits(self, all_attributes: bool = False, attributes: List[str] = None, show_inactive_sets: bool = False) -> DataFrame: """ Get the list of operational limits. @@ -3438,17 +3825,22 @@ def get_operational_limits(self, all_attributes: bool = False, attributes: List[ - **value**: The value of the limit - **acceptable_duration**: The duration, in seconds, for which the element can securely be operated under the limit value. By convention, the value -1 represents an infinite duration. - - **is_fictitious**: true if this limit is fictitious + - **fictitious** (optional): `True` if this limit is fictitious + - **group_name** (optional): The name of the operational limit group this limit is in + - **selected** (optional): `True` if this limit's operational group is the selected one Args: all_attributes: flag for including all attributes in the dataframe, default is false attributes: attributes to include in the dataframe. The 2 parameters are mutually exclusive. If no parameter is specified, the dataframe will include the default attributes. + only_selected_sets: flag to choose whether inactive limit sets should also be included in the dataframe Returns: All limits on the network """ - return self.get_elements(ElementType.OPERATIONAL_LIMITS, all_attributes, attributes) + if show_inactive_sets: + return self.get_elements(ElementType.OPERATIONAL_LIMITS, all_attributes, attributes) + return self.get_elements(ElementType.SELECTED_OPERATIONAL_LIMITS, all_attributes, attributes) def get_node_breaker_topology(self, voltage_level_id: str) -> NodeBreakerTopology: """ @@ -3693,6 +4085,41 @@ def create_loads(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ return self._create_elements(ElementType.LOAD, [df], **kwargs) + def create_grounds(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Create grounds. + + Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **id**: the identifier of the new ground + - **voltage_level_id**: the voltage level where the new ground will be created. + The voltage level must already exist. + - **bus_id**: the bus where the new ground will be connected, + if the voltage level has a bus-breaker topology kind. + - **connectable_bus_id**: the bus where the new ground will be connectable, + if the voltage level has a bus-breaker topology kind. + - **node**: the node where the new ground will be connected, + if the voltage level has a node-breaker topology kind. + - **name**: an optional human-readable name + + Examples: + Using keyword arguments: + + .. code-block:: python + + network.create_loads(id='GROUND-1', voltage_level_id='VL1', bus_id='B1') + """ + return self._create_elements(ElementType.GROUND, [df], **kwargs) + def create_batteries(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ Creates batteries. @@ -3733,17 +4160,18 @@ def create_batteries(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ return self._create_elements(ElementType.BATTERY, [df], **kwargs) - def create_dangling_lines(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + def create_dangling_lines(self, df: DataFrame = None, generation_df: DataFrame = pd.DataFrame(), **kwargs: ArrayLike) -> None: """ Creates dangling lines. Args: df: Attributes as a dataframe. + generation_df: Attributes of the dangling lines optional generation part, only as a dataframe kwargs: Attributes as keyword arguments. Notes: - Data may be provided as a dataframe or as keyword arguments. + General dangling line data may be provided as a dataframe or as keyword arguments. In the latter case, all arguments must have the same length. Valid attributes are: @@ -3767,6 +4195,17 @@ def create_dangling_lines(self, df: DataFrame = None, **kwargs: ArrayLike) -> No - **pairing-key**: the optional pairing key associated to the dangling line, to be used for creating tie lines. - **ucte-x-node-code**: deprecated, use pairing-key instead. + Dangling line generation information must be provided as a dataframe. + Valid attributes are: + + - **id**: Identifier of the dangling line that contains this generation part + - **min_p**: Minimum active power output of the dangling line's generation part + - **max_p**: Maximum active power output of the dangling line's generation part + - **target_p**: Active power target of the generation part + - **target_q**: Reactive power target of the generation part + - **target_v**: Voltage target of the generation part + - **voltage_regulator_on**: ``True`` if the generation part regulates voltage + Examples: Using keyword arguments: @@ -3785,7 +4224,7 @@ def create_dangling_lines(self, df: DataFrame = None, **kwargs: ArrayLike) -> No warnings.warn(ucte_xnode_code_str + " is deprecated, use pairing_key", DeprecationWarning) kwargs['pairing_key'] = ucte_x_node_code kwargs.pop(ucte_xnode_code_str) - return self._create_elements(ElementType.DANGLING_LINE, [df], **kwargs) + return self._create_elements(ElementType.DANGLING_LINE, [df, generation_df], **kwargs) def create_lcc_converter_stations(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ @@ -4435,14 +4874,13 @@ def create_operational_limits(self, df: DataFrame = None, **kwargs: ArrayLike) - Valid attributes are: - **element_id**: the ID of the network element on which we want to create new limits - - **element_type**: the type of the network element (LINE, TWO_WINDINGS_TRANSFORMER, THREE_WINDINGS_TRANSFORMER, DANGLING_LINE) - **side**: the side of the network element where we want to create new limits (ONE, TWO, THREE) - **name**: the name of the limit - **type**: the type of limit to be created (CURRENT, APPARENT_POWER, ACTIVE_POWER) - **value**: the value of the limit in A, MVA or MW - **acceptable_duration**: the maximum number of seconds during which we can operate under that limit - - **is_fictitious**: fictitious limit ? + - **fictitious**: fictitious limit ? For each location of the network defined by a couple (element_id, side): @@ -4456,6 +4894,20 @@ def create_operational_limits(self, df: DataFrame = None, **kwargs: ArrayLike) - """ if df is not None: df['acceptable_duration'] = df['acceptable_duration'].map(lambda x: -1 if x == inf else int(x)) + if 'is_fictitious' in df.columns: + warnings.warn("operation limits is_fictitious attribute has been renamed fictitious", DeprecationWarning) + df = df.rename(columns={'is_fictitious': 'fictitious'}) + if 'element_type' in df.columns: + warnings.warn("useless operation limits element_type attribute has been removed", DeprecationWarning) + df = df.drop(columns=['element_type']) + + if kwargs.get('is_fictitious') is not None: + warnings.warn("operation limits is_fictitious attribute has been renamed fictitious", DeprecationWarning) + kwargs['fictitious'] = kwargs.pop('is_fictitious') + if kwargs.get('element_type') is not None: + warnings.warn("useless operation limits element_type attribute has been removed", DeprecationWarning) + kwargs.pop('element_type') + return self._create_elements(ElementType.OPERATIONAL_LIMITS, [df], **kwargs) def create_minmax_reactive_limits(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: @@ -4571,6 +5023,156 @@ def create_tie_lines(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ return self._create_elements(ElementType.TIE_LINE, [df], **kwargs) + def create_areas(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Create areas. + + Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + See Also: + :meth:`get_areas` + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **id**: the identifier of the new area + - **name**: an optional human-readable name + - **area_type**: the type of Area (e.g. ControlArea, BiddingZone …) + - **interchange_target**: Target active power interchange (MW) + + Examples: + Using keyword arguments: + + .. code-block:: python + + network.create_areas(id='Area1', area_type='ControlArea', interchange_target=120.5) + """ + return self._create_elements(ElementType.AREA, [df], **kwargs) + + def create_areas_voltage_levels(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Associate voltage levels to (existing) areas. + + Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Important: The provided voltage levels for an area replace all existing voltage levels of that area, + i.e. the entire list of voltage levels must be provided for the areas being edited. + + Valid attributes are: + + - **id**: the identifier of the area + - **voltage_level_id**: the identifier of the voltage level to be associated with the area + + See Also: + :meth:`get_areas_voltage_levels` + + Examples: + To associate voltage levels VL1 and VL2 to Area1. + + .. code-block:: python + + network.create_areas_voltage_levels(id=['Area1', 'Area1'], voltage_level_id=['VL1', 'VL2']) + + To dissociate all VoltageLevels of a given area, provide an empty string in voltage_level_id. + + .. code-block:: python + + network.create_areas_voltage_levels(id=['Area1'], voltage_level_id=['']) + """ + return self._create_elements(ElementType.AREA_VOLTAGE_LEVELS, [df], **kwargs) + + def create_areas_boundaries(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Define boundaries of (existing) areas. + + Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Important: The provided boundaries for an area replace all existing boundaries of that area, + i.e. the entire list of boundaries must be provided for the areas being edited. + + Valid attributes are: + + - **id**: the identifier of the area + - **boundary_type**: either `DANGLING_LINE` or `TERMINAL`, defaults to `DANGLING_LINE`. + - **element**: dangling line identifier, or any connectable + - **side**: if element is not a dangling line (e.g. a branch or transformer), the terminal side + - **ac**: True is boundary is to be considered as AC + + See Also: + :meth:`get_areas_boundaries` + + Examples: + + .. code-block:: python + + # define dangling lines NHV1_XNODE1 and NVH1_XNODE2 as boundaries of AreaA, and + # define dangling lines XNODE1_NHV2 and XNODE2_NHV2 as boundaries of AreaB + network.create_areas_boundaries(id=['AreaA', 'AreaA', 'AreaB', 'AreaB'], + boundary_type=['DANGLING_LINE', 'DANGLING_LINE', 'DANGLING_LINE', 'DANGLING_LINE'], + element=['NHV1_XNODE1', 'NVH1_XNODE2', 'XNODE1_NHV2', 'XNODE2_NHV2'], + ac=[True, True, True, True]) + + To dissociate all Boundaries of a given area, provide an empty string in element. + + .. code-block:: python + + network.create_areas_boundaries(id=['Area1'], element=['']) + """ + return self._create_elements(ElementType.AREA_BOUNDARIES, [df], **kwargs) + + def create_internal_connections(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Creates internal connections. + + Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + See Also: + - :meth:`get_node_breaker_topology` + - :meth:`remove_internal_connections` + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **voltage_level_id**: voltage level identifier. The voltage level must be in Node/Breaker topology kind. + - **node1**: node 1 of the internal connection + - **node2**: node 2 of the internal connection + + Examples: + Using keyword arguments: + + .. code-block:: python + + network.create_internal_connections(voltage_level_id='VL1', node1=3, node2=6) + + """ + return self._create_elements(ElementType.INTERNAL_CONNECTION, [df], **kwargs) + def add_aliases(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ Adds aliases to network elements. @@ -4688,12 +5290,48 @@ def remove_elements(self, elements_ids: Union[str, List[str]]) -> None: .. code-block:: python network.remove_elements('GENERATOR-1') # Removes only 1 element - network.remove_elements(['GENERATOR-1', 'BUS]) + network.remove_elements(['GENERATOR-1', 'BUS']) """ if isinstance(elements_ids, str): elements_ids = [elements_ids] _pp.remove_elements(self._handle, elements_ids) + def remove_internal_connections(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: + """ + Removes internal connections. + + Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + See Also: + - :meth:`get_node_breaker_topology` + - :meth:`create_internal_connections` + + Notes: + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **voltage_level_id**: voltage level identifier. The voltage level must be in Node/Breaker topology kind. + - **node1**: node 1 of the internal connection + - **node2**: node 2 of the internal connection + + Examples: + Using keyword arguments: + + .. code-block:: python + + network.remove_internal_connections(voltage_level_id='VL1', node1=3, node2=6) + + """ + metadata = _pp.get_network_elements_creation_dataframes_metadata(ElementType.INTERNAL_CONNECTION)[0] + df = _adapt_df_or_kwargs(metadata, df, **kwargs) + c_df = _create_c_dataframe(df, metadata) + _pp.remove_internal_connections(self._handle, c_df) + def get_extensions(self, extension_name: str, table_name: str = "") -> DataFrame: """ Get an extension as a :class:`~pandas.DataFrame` for a specified extension name. diff --git a/pypowsybl/network/impl/network_creation_util.py b/pypowsybl/network/impl/network_creation_util.py index 4248cbaeb5..4a73665651 100644 --- a/pypowsybl/network/impl/network_creation_util.py +++ b/pypowsybl/network/impl/network_creation_util.py @@ -102,6 +102,15 @@ def create_eurostag_tutorial_example1_network() -> Network: """ return _create_network('eurostag_tutorial_example1') +def create_eurostag_tutorial_example1_with_more_generators_network() -> Network: + """ + Create an instance of example 1 network of Eurostag tutorial, with a second generator + + Returns: + a new instance of example 1 network of Eurostag tutorial with a second generator + """ + return _create_network('eurostag_tutorial_example1_with_more_generators') + def create_eurostag_tutorial_example1_with_power_limits_network() -> Network: """ @@ -113,6 +122,16 @@ def create_eurostag_tutorial_example1_with_power_limits_network() -> Network: return _create_network('eurostag_tutorial_example1_with_power_limits') +def create_eurostag_tutorial_example1_with_tie_lines_and_areas() -> Network: + """ + Create an instance of example 1 network of Eurostag tutorial with tie lines and areas + + Returns: + a new instance of example 1 network of Eurostag tutorial with tie lines and areas + """ + return _create_network('eurostag_tutorial_example1_with_tie_lines_and_areas') + + def create_four_substations_node_breaker_network() -> Network: """ Create an instance of powsybl "4 substations" test case. @@ -166,7 +185,7 @@ def create_metrix_tutorial_six_buses_network() -> Network: return _create_network('metrix_tutorial_six_buses') -def load(file: Union[str, PathLike], parameters: Dict[str, str] = None, reporter: ReportNode = None, +def load(file: Union[str, PathLike], parameters: Dict[str, str] = None, post_processors: List[str] = None, reporter: ReportNode = None, report_node: ReportNode = None) -> Network: """ Load a network from a file. File should be in a supported format. @@ -176,6 +195,7 @@ def load(file: Union[str, PathLike], parameters: Dict[str, str] = None, reporter Args: file: path to the network file parameters: a dictionary of import parameters + post_processors: a list of import post processors (will be added to the ones defined by the platform config) reporter: deprecated, use report_node instead report_node: the reporter to be used to create an execution report, default is None (no report) @@ -198,13 +218,13 @@ def load(file: Union[str, PathLike], parameters: Dict[str, str] = None, reporter warnings.warn(DEPRECATED_REPORTER_WARNING, DeprecationWarning) report_node = reporter file = path_to_str(file) - if parameters is None: - parameters = {} - return Network(_pp.load_network(file, parameters, + return Network(_pp.load_network(file, + {} if parameters is None else parameters, + [] if post_processors is None else post_processors, None if report_node is None else report_node._report_node)) # pylint: disable=protected-access -def load_from_binary_buffer(buffer: io.BytesIO, parameters: Dict[str, str] = None, +def load_from_binary_buffer(buffer: io.BytesIO, parameters: Dict[str, str] = None, post_processors: List[str] = None, reporter: ReportNode = None, report_node: ReportNode = None) -> Network: """ Load a network from a binary buffer. @@ -212,6 +232,7 @@ def load_from_binary_buffer(buffer: io.BytesIO, parameters: Dict[str, str] = Non Args: buffer: The BytesIO data buffer parameters: A dictionary of import parameters + post_processors: a list of import post processors (will be added to the ones defined by the platform config) reporter: deprecated, use report_node instead report_node: the reporter to be used to create an execution report, default is None (no report) @@ -221,10 +242,13 @@ def load_from_binary_buffer(buffer: io.BytesIO, parameters: Dict[str, str] = Non if reporter is not None: warnings.warn(DEPRECATED_REPORTER_WARNING, DeprecationWarning) report_node = reporter - return load_from_binary_buffers([buffer], parameters, report_node) + return load_from_binary_buffers([buffer], + {} if parameters is None else parameters, + [] if post_processors is None else post_processors, + report_node) -def load_from_binary_buffers(buffers: List[io.BytesIO], parameters: Dict[str, str] = None, +def load_from_binary_buffers(buffers: List[io.BytesIO], parameters: Dict[str, str] = None, post_processors: List[str] = None, reporter: ReportNode = None, report_node: ReportNode = None) -> Network: """ Load a network from a list of binary buffers. Only zipped CGMES are supported for several zipped source load. @@ -232,6 +256,7 @@ def load_from_binary_buffers(buffers: List[io.BytesIO], parameters: Dict[str, st Args: buffers: The list of BytesIO data buffer parameters: A dictionary of import parameters + post_processors: a list of import post processors (will be added to the ones defined by the platform config) reporter: deprecated, use report_node instead report_node: the reporter to be used to create an execution report, default is None (no report) @@ -241,16 +266,16 @@ def load_from_binary_buffers(buffers: List[io.BytesIO], parameters: Dict[str, st if reporter is not None: warnings.warn(DEPRECATED_REPORTER_WARNING, DeprecationWarning) report_node = reporter - if parameters is None: - parameters = {} buffer_list = [] for buff in buffers: buffer_list.append(buff.getbuffer()) - return Network(_pp.load_network_from_binary_buffers(buffer_list, parameters, + return Network(_pp.load_network_from_binary_buffers(buffer_list, + {} if parameters is None else parameters, + [] if post_processors is None else post_processors, None if report_node is None else report_node._report_node)) # pylint: disable=protected-access -def load_from_string(file_name: str, file_content: str, parameters: Dict[str, str] = None, +def load_from_string(file_name: str, file_content: str, parameters: Dict[str, str] = None, post_processors: List[str] = None, reporter: ReportNode = None, report_node: ReportNode = None) -> Network: """ Load a network from a string. File content should be in a supported format. @@ -259,6 +284,7 @@ def load_from_string(file_name: str, file_content: str, parameters: Dict[str, st file_name: file name file_content: file content parameters: a dictionary of import parameters + post_processors: a list of import post processors (will be added to the ones defined by the platform config) reporter: deprecated, use report_node instead report_node: the reporter to be used to create an execution report, default is None (no report) @@ -268,7 +294,7 @@ def load_from_string(file_name: str, file_content: str, parameters: Dict[str, st if reporter is not None: warnings.warn(DEPRECATED_REPORTER_WARNING, DeprecationWarning) report_node = reporter - if parameters is None: - parameters = {} - return Network(_pp.load_network_from_string(file_name, file_content, parameters, + return Network(_pp.load_network_from_string(file_name, file_content, + {} if parameters is None else parameters, + [] if post_processors is None else post_processors, None if report_node is None else report_node._report_node)) # pylint: disable=protected-access diff --git a/pypowsybl/network/impl/network_element_modification_util.py b/pypowsybl/network/impl/network_element_modification_util.py index 025fe45d19..b2422df7ef 100644 --- a/pypowsybl/network/impl/network_element_modification_util.py +++ b/pypowsybl/network/impl/network_element_modification_util.py @@ -297,14 +297,16 @@ def create_generator_bay(network: Network, df: DataFrame = None, raise_exception return _create_feeder_bay(network, [df], ElementType.GENERATOR, raise_exception, reporter, report_node, **kwargs) -def create_dangling_line_bay(network: Network, df: DataFrame = None, raise_exception: bool = True, - reporter: ReportNode = None, report_node: ReportNode = None, **kwargs: ArrayLike) -> None: +def create_dangling_line_bay(network: Network, df: DataFrame = None, generation_df: DataFrame = pd.DataFrame(), + raise_exception: bool = True, reporter: ReportNode = None, report_node: ReportNode = None, + **kwargs: ArrayLike) -> None: """ Creates a dangling line, connects it to the network on a given bus or busbar section and creates the associated topology. Args: network: the network to which we want to add the dangling line df: Attributes as a dataframe. + generation_df: Optional dangling lines' generation part, only as a dataframe raise_exception: optionally, whether the calculation should throw exceptions. In any case, errors will be logged. Default is True. reporter: deprecated, use report_node instead @@ -318,7 +320,7 @@ def create_dangling_line_bay(network: Network, df: DataFrame = None, raise_excep busbar section with an open disconnector. If the voltage level is bus/breaker, the dangling line is just connected to the bus. - Valid attributes are: + Valid attributes for dangling line dataframe or named arguments are: - **id**: the identifier of the new line - **name**: an optional human-readable name @@ -332,8 +334,18 @@ def create_dangling_line_bay(network: Network, df: DataFrame = None, raise_excep - **position_order**: in node/breaker, the order of the dangling line, will fill the ConnectablePosition extension - **direction**: optionally, in node/breaker, the direction of the dangling line, will fill the ConnectablePosition extension, default is BOTTOM. + Dangling line generation information must be provided as a dataframe. + Valid attributes are: + + - **id**: Identifier of the dangling line that contains this generation part + - **min_p**: Minimum active power output of the dangling line's generation part + - **max_p**: Maximum active power output of the dangling line's generation part + - **target_p**: Active power target of the generation part + - **target_q**: Reactive power target of the generation part + - **target_v**: Voltage target of the generation part + - **voltage_regulator_on**: ``True`` if the generation part regulates voltage """ - return _create_feeder_bay(network, [df], ElementType.DANGLING_LINE, raise_exception, reporter, report_node, + return _create_feeder_bay(network, [df, generation_df], ElementType.DANGLING_LINE, raise_exception, reporter, report_node, **kwargs) @@ -647,8 +659,7 @@ def create_voltage_level_topology(network: Network, df: DataFrame = None, raise_ def transform_list_to_str(entry: Union[str, List[str]]) -> str: if isinstance(entry, list): return ','.join(str(e.replace(' ', '')) for e in entry) - if isinstance(entry, str): - return entry.replace(' ', '') + return entry.replace(' ', '') def create_coupling_device(network: Network, df: DataFrame = None, raise_exception: bool = True, diff --git a/pypowsybl/network/impl/node_breaker_topology.py b/pypowsybl/network/impl/node_breaker_topology.py index 4f63861911..a423929d65 100644 --- a/pypowsybl/network/impl/node_breaker_topology.py +++ b/pypowsybl/network/impl/node_breaker_topology.py @@ -31,6 +31,17 @@ def __init__(self, network_handle: _pp.JavaHandle, voltage_level_id: str): def switches(self) -> DataFrame: """ The list of switches of the voltage level, together with their connection status, as a dataframe. + + The dataframe includes the following columns: + + - **name**: Switch name + - **kind**: Switch kind (BREAKER, DISCONNECTOR, ...) + - **open**: True if the switch is opened + - **retained**: True if the switch is to be retained in the Bus/Breaker view + - **node1**: node where the switch is connected at side 1 + - **node2**: node where the switch is connected at side 2 + + This dataframe is indexed by the id of the switches. """ return self._switchs @@ -39,6 +50,12 @@ def nodes(self) -> DataFrame: """ The list of nodes of the voltage level, together with their corresponding network element (if any), as a dataframe. + + The dataframe includes the following columns: + + - **connectable_id**: Connected element, if any. + + This dataframe is indexed by the id of the nodes. """ return self._nodes @@ -46,6 +63,11 @@ def nodes(self) -> DataFrame: def internal_connections(self) -> DataFrame: """ The list of internal connection of the voltage level, together with the nodes they connect. + + The dataframe includes the following columns: + + - **node1**: node where the internal connection is connected at side 1 + - **node2**: node where the internal connection is connected at side 2 """ return self._internal_connections diff --git a/pypowsybl/network/impl/pandapower_converter.py b/pypowsybl/network/impl/pandapower_converter.py new file mode 100644 index 0000000000..1198b43e97 --- /dev/null +++ b/pypowsybl/network/impl/pandapower_converter.py @@ -0,0 +1,308 @@ +# Copyright (c) 2024, RTE (http://www.rte-france.com) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# +import math +import typing +from enum import Enum +from importlib import util +from typing import Dict + +import numpy as np +import pandas as pd +from pandas import Series +from pandas import DataFrame +import pypowsybl._pypowsybl as _pp +try: + from pandapower import pandapowerNet +except ImportError: + pandapowerNet = any + +from .network import Network +from .network_creation_util import create_empty + +MIN_TARGET_P_TO_NOT_BE_DISCADED_FROM_ACTIVE_POWER_CONTROL = 0.0001 + +DEFAULT_MIN_P = -4999.0 +DEFAULT_MAX_P = 4999.0 + + +def convert_from_pandapower(n_pdp: pandapowerNet) -> Network: + if util.find_spec("pandapower") is None: + raise _pp.PyPowsyblError("pandapower is not installed") + + n = create_empty(n_pdp.name if n_pdp.name else 'network') + + # create one giant substation + n.create_substations(id='s') + create_buses(n, n_pdp) + create_loads(n, n_pdp) + slack_weight_by_gen_id: Dict[str, float] = {} + create_generators(n, n_pdp, slack_weight_by_gen_id) + create_shunts(n, n_pdp) + create_lines(n, n_pdp) + create_transformers(n, n_pdp) + create_extensions(n, slack_weight_by_gen_id) + + return n + + +def create_extensions(n: Network, slack_weight_by_gen_id: Dict[str, float]) -> None: + if len(slack_weight_by_gen_id) > 0: + highest_weight_gen_id = max(slack_weight_by_gen_id, key=lambda k: slack_weight_by_gen_id[k]) + for index, (gen_id, weight) in enumerate(slack_weight_by_gen_id.items()): + if gen_id == highest_weight_gen_id: + # create slack bus extension for first one + generators = n.get_generators(attributes=['voltage_level_id']) + slack_gen = generators.loc[gen_id] + n.create_extensions(extension_name='slackTerminal', element_id=gen_id, + voltage_level_id=slack_gen['voltage_level_id']) + + # create active power control extension to define the distribution key + n.create_extensions(extension_name='activePowerControl', id=gen_id, + participate=abs(weight) > 0.001, droop=weight) + + +def get_name(df: pd.DataFrame, name: str) -> pd.Series: + name_col = df[name] + replace_none = np.vectorize(lambda x: '' if x is None else x, otypes=[np.string_]) + name_cleaned = replace_none(name_col) + return name_cleaned.astype(str) + + +def build_voltage_level_id(bus: Series) -> pd.Series: + return 'sub_' + bus + + +def build_bus_id(bus: Series) -> pd.Series: + return 'bus_' + bus + + +def build_injection_id(prefix: str, bus: pd.Series, index: int) -> str: + return f'{prefix}_{bus}_{index}' # because it is required by grid2op to build IDs like this is case of missing name + + +def generate_injection_id(df: pd.DataFrame, prefix: str) -> pd.Series: + return df.apply(lambda row: build_injection_id(prefix, row['bus'], row.name), axis=1) + + +def build_line_id(row: pd.Series, index: int) -> str: + from_bus = row['from_bus'] + to_bus = row['to_bus'] + return f'{from_bus}_{to_bus}_{index}' # because it is required by grid2op to build IDs like this is case of missing name + + +def generate_line_id(df: pd.DataFrame) -> pd.Series: + return df.apply(lambda row: build_line_id(row, row.name), axis=1) + + +def build_transformer_id(row: pd.Series, index: int, index_offset: int) -> str: + hv_bus = row['hv_bus'] + lv_bus = row['lv_bus'] + return f'{hv_bus}_{lv_bus}_{index_offset + index}' # because it is required by grid2op to build IDs like this is case of missing name + + +def generate_transformer_id(df: pd.DataFrame, index_offset: int) -> pd.Series: + return df.apply(lambda row: build_transformer_id(row, row.name, index_offset), axis=1) + + +def create_transformers(n: Network, n_pdp: pandapowerNet) -> None: + if len(n_pdp.trafo) > 0: + bus = n_pdp.bus[['vn_kv']] + trafo_and_bus = n_pdp.trafo.merge(bus.rename(columns=lambda x: x + '_lv_bus'), left_on='lv_bus', right_index=True, how='left') + trafo_id = generate_transformer_id(trafo_and_bus, len(n_pdp.line)) + name = get_name(trafo_and_bus, 'name') + vl1_id = build_voltage_level_id(trafo_and_bus['hv_bus'].astype(str)) + vl2_id = build_voltage_level_id(trafo_and_bus['lv_bus'].astype(str)) + connectable_bus1_id = build_bus_id(trafo_and_bus['hv_bus'].astype(str)) + connectable_bus2_id = build_bus_id(trafo_and_bus['lv_bus'].astype(str)) + bus1_id = np.where(trafo_and_bus['in_service'], connectable_bus1_id, "") + bus2_id = np.where(trafo_and_bus['in_service'], connectable_bus2_id, "") + n_tap = np.where(~np.isnan(trafo_and_bus['tap_pos']) & ~np.isnan(trafo_and_bus['tap_neutral']) & ~np.isnan(trafo_and_bus['tap_step_percent']), + 1.0 + (trafo_and_bus['tap_pos'] - trafo_and_bus['tap_neutral']) * trafo_and_bus['tap_step_percent'] / 100.0, 1.0) + rated_u1 = np.where(trafo_and_bus['tap_side'] == "hv", trafo_and_bus['vn_hv_kv'] * n_tap, trafo_and_bus['vn_hv_kv']) + rated_u2 = np.where(trafo_and_bus['tap_side'] == "lv", trafo_and_bus['vn_lv_kv'] * n_tap, trafo_and_bus['vn_lv_kv']) + c = n_pdp.sn_mva / n_pdp.trafo['sn_mva'] + rk = trafo_and_bus['vkr_percent'] / 100 * c + zk = trafo_and_bus['vk_percent'] / 100 * c + xk = np.sqrt(zk ** 2 - rk ** 2) + ym = trafo_and_bus['i0_percent'] / 100 + gm = trafo_and_bus['pfe_kw'] / (trafo_and_bus['sn_mva'] * 1000) / c + bm = -1 * np.sign(ym) * np.sqrt(ym ** 2 - gm ** 2) + + zb_tr = (rated_u2 ** 2) / n_pdp.sn_mva + r = rk * zb_tr / trafo_and_bus['parallel'] + x = xk * zb_tr / trafo_and_bus['parallel'] + g = gm / zb_tr * trafo_and_bus['parallel'] + b = bm / zb_tr * trafo_and_bus['parallel'] + + n.create_2_windings_transformers(id=trafo_id, name=name, + voltage_level1_id=vl1_id, connectable_bus1_id=connectable_bus1_id, bus1_id=bus1_id, + voltage_level2_id=vl2_id, connectable_bus2_id=connectable_bus2_id, bus2_id=bus2_id, + rated_u1=rated_u1, rated_u2=rated_u2, + r=r, x=x, g=g, b=b) + + +def create_limits(n: Network, n_pdp: pandapowerNet, id: pd.Series) -> None: + limit_side = ['ONE'] * len(n_pdp.line) # create on side on, why not... + limit_name = ['permanent'] * len(n_pdp.line) + limit_type = ['CURRENT'] * len(n_pdp.line) + limit_value = n_pdp.line['max_i_ka'] * 1000.0 / n_pdp.line['parallel'] + limit_value *= n_pdp.line['df'] + acceptable_duration = [-1] * len(n_pdp.line) + n.create_operational_limits(element_id=id, side=limit_side, name=limit_name, type=limit_type, value=limit_value, + acceptable_duration=acceptable_duration) + +def create_lines(n: Network, n_pdp: pandapowerNet) -> None: + if len(n_pdp.line) > 0: + line_id = generate_line_id(n_pdp.line) + name = get_name(n_pdp.line, 'name') + vl1_id = build_voltage_level_id(n_pdp.line['from_bus'].astype(str)) + vl2_id = build_voltage_level_id(n_pdp.line['to_bus'].astype(str)) + connectable_bus1_id = build_bus_id(n_pdp.line['from_bus'].astype(str)) + connectable_bus2_id = build_bus_id(n_pdp.line['to_bus'].astype(str)) + bus1_id = np.where(n_pdp.line['in_service'], connectable_bus1_id, "") + bus2_id = np.where(n_pdp.line['in_service'], connectable_bus2_id, "") + r = n_pdp.line['length_km'] * n_pdp.line['r_ohm_per_km'] / n_pdp.line['parallel'] + x = n_pdp.line['length_km'] * n_pdp.line['x_ohm_per_km'] / n_pdp.line['parallel'] + g = n_pdp.line['length_km'] * n_pdp.line['g_us_per_km'] * 1e-6 * n_pdp.line['parallel'] / 2 + b = n_pdp.line['length_km'] * n_pdp.line['c_nf_per_km'] * 1e-9 * 2 * math.pi * n_pdp.f_hz * n_pdp.line['parallel'] / 2 + + n.create_lines(id=line_id, name=name, + voltage_level1_id=vl1_id, connectable_bus1_id=connectable_bus1_id, bus1_id=bus1_id, + voltage_level2_id=vl2_id, connectable_bus2_id=connectable_bus2_id, bus2_id=bus2_id, + r=r, x=x, g1=g, g2=g, b1=b, b2=b) + create_limits(n, n_pdp, line_id) + + +def create_shunts(n: Network, n_pdp: pandapowerNet) -> None: + if len(n_pdp.shunt) > 0: + shunt_id = generate_injection_id(n_pdp.shunt, 'shunt') + name = get_name(n_pdp.shunt, 'name').tolist() + vl_id = build_voltage_level_id(n_pdp.shunt['bus'].astype(str)).tolist() + connectable_bus_id = build_bus_id(n_pdp.shunt['bus'].astype(str)).tolist() + bus_id = np.where(n_pdp.shunt['in_service'], connectable_bus_id, "") + model_type = ['LINEAR'] * len(n_pdp.shunt) + section_count = n_pdp.shunt['step'].tolist() + shunt_df = pd.DataFrame(data={ + 'name': name, + 'voltage_level_id': vl_id, + 'connectable_bus_id': connectable_bus_id, + 'bus_id': bus_id, + 'model_type': model_type, + 'section_count': section_count + }, index=shunt_id) + g_per_section = (n_pdp.shunt['p_mw'] / (n_pdp.shunt['vn_kv'] ** 2) * -1.0).tolist() + b_per_section = (n_pdp.shunt['q_mvar'] / (n_pdp.shunt['vn_kv'] ** 2) * -1.0).tolist() + max_section_count = n_pdp.shunt['max_step'].tolist() + linear_model_df = pd.DataFrame(data={ + 'g_per_section': g_per_section, + 'b_per_section': b_per_section, + 'max_section_count': max_section_count, + }, index=shunt_id) + n.create_shunt_compensators(shunt_df=shunt_df, linear_model_df=linear_model_df) + +class PandaPowerGeneratorType(Enum): + GENERATOR = 1 + EXT_GRID = 2 + STATIC_GENERATOR = 3 + +def _create_generators(n: Network, gen: DataFrame, bus: DataFrame, slack_weight_by_gen_id: Dict[str, float], generator_type: PandaPowerGeneratorType) -> None: + if len(gen) > 0: + gen_and_bus: DataFrame = gen.merge(bus, left_on='bus', right_index=True, how='left', suffixes=('', '_x')) + gen_id = generate_injection_id(gen_and_bus, 'gen' if generator_type != PandaPowerGeneratorType.STATIC_GENERATOR else 'sgen') + name = get_name(gen_and_bus, 'name') + vl_id = build_voltage_level_id(gen_and_bus['bus'].astype(str)) + connectable_bus_id = build_bus_id(gen_and_bus['bus'].astype(str)).tolist() + bus_id = np.where(gen_and_bus['in_service'], connectable_bus_id, "") + target_p = create_generator_target_p(gen_and_bus, generator_type) + voltage_regulator_on = [generator_type != PandaPowerGeneratorType.STATIC_GENERATOR] * len(gen_and_bus) + target_v = create_generator_target_v(gen_and_bus, generator_type) + target_q = create_generator_target_q(gen_and_bus, generator_type) + min_p = create_generator_min_p(gen_and_bus, generator_type) + max_p = create_generator_max_p(gen_and_bus, generator_type) + min_q = gen_and_bus['min_q_mvar'] if 'min_q_mvar' in gen_and_bus.columns else None + max_q = gen_and_bus['max_q_mvar'] if 'max_q_mvar' in gen_and_bus.columns else None + + fill_generator_slack_weight(gen_and_bus, generator_type, slack_weight_by_gen_id) + + n.create_generators(id=gen_id, name=name, + voltage_level_id=vl_id, connectable_bus_id=connectable_bus_id, bus_id=bus_id, + target_p=target_p, voltage_regulator_on=voltage_regulator_on, target_v=target_v, target_q=target_q, + min_p=min_p, max_p=max_p) + if min_q is not None and max_q is not None: + n.create_minmax_reactive_limits(id=gen_id, min_q=min_q, max_q=max_q) + + +def create_generator_target_p(gen_and_bus: DataFrame, generator_type: PandaPowerGeneratorType) -> pd.Series: + return pd.Series([MIN_TARGET_P_TO_NOT_BE_DISCADED_FROM_ACTIVE_POWER_CONTROL] * len( + gen_and_bus)) if generator_type == PandaPowerGeneratorType.EXT_GRID else gen_and_bus['p_mw'] # keep a small value of target_p to avoid be discarded to slack distribution + + +def create_generator_max_p(gen_and_bus: DataFrame, generator_type: PandaPowerGeneratorType) -> pd.Series: + return pd.Series([DEFAULT_MAX_P] * len( + gen_and_bus)) if generator_type == PandaPowerGeneratorType.EXT_GRID or 'max_p_mw' not in gen_and_bus.columns else pd.Series( + np.nan_to_num(gen_and_bus['max_p_mw'], nan=DEFAULT_MAX_P)) + + +def create_generator_min_p(gen_and_bus: DataFrame, generator_type: PandaPowerGeneratorType) -> pd.Series: + return pd.Series([DEFAULT_MIN_P] * len( + gen_and_bus)) if generator_type == PandaPowerGeneratorType.EXT_GRID or 'min_p_mw' not in gen_and_bus.columns else pd.Series( + np.nan_to_num(gen_and_bus['min_p_mw'], nan=DEFAULT_MIN_P)) + + +def create_generator_target_q(gen_and_bus: DataFrame, generator_type: PandaPowerGeneratorType) -> pd.Series: + return gen_and_bus[ + 'q_mvar'] if generator_type == PandaPowerGeneratorType.STATIC_GENERATOR and 'q_mvar' in gen_and_bus.columns else pd.Series( + [0.0] * len(gen_and_bus)) + + +def create_generator_target_v(gen_and_bus: DataFrame, generator_type: PandaPowerGeneratorType) -> pd.Series: + return gen_and_bus['vm_pu'] * gen_and_bus[ + 'vn_kv'] if generator_type != PandaPowerGeneratorType.STATIC_GENERATOR else pd.Series([1.0] * len(gen_and_bus)) + + +def fill_generator_slack_weight(gen_and_bus: DataFrame, generator_type: PandaPowerGeneratorType, slack_weight_by_gen_id: Dict[str, float]) -> None: + if generator_type != PandaPowerGeneratorType.STATIC_GENERATOR: + for index, row in gen_and_bus.iterrows(): + index = typing.cast(int, index) # safe cas from hashtable to int needed by mypy + weight = row['slack_weight'] if generator_type == PandaPowerGeneratorType.EXT_GRID or row['slack'] else 0.0 + slack_weight_by_gen_id[build_injection_id('gen', row['bus'], index)] = weight + + +def create_generators(n: Network, n_pdp: pandapowerNet, slack_weight_by_gen_id: Dict[str, float]) -> None: + _create_generators(n, n_pdp.gen, n_pdp.bus, slack_weight_by_gen_id, PandaPowerGeneratorType.GENERATOR) + _create_generators(n, n_pdp.ext_grid, n_pdp.bus, slack_weight_by_gen_id, PandaPowerGeneratorType.EXT_GRID) + _create_generators(n, n_pdp.sgen, n_pdp.bus, slack_weight_by_gen_id, PandaPowerGeneratorType.STATIC_GENERATOR) + + +def create_loads(n: Network, n_pdp: pandapowerNet) -> None: + if len(n_pdp.load) > 0: + load_id = generate_injection_id(n_pdp.load, 'load') + name = get_name(n_pdp.load, 'name') + vl_id = build_voltage_level_id(n_pdp.load['bus'].astype(str)) + connectable_bus_id = build_bus_id(n_pdp.load['bus'].astype(str)).tolist() + bus_id = np.where(n_pdp.load['in_service'], connectable_bus_id, "") + p0 = n_pdp.load['p_mw'] + q0 = n_pdp.load['q_mvar'] + n.create_loads(id=load_id, name=name, + voltage_level_id=vl_id, connectable_bus_id=connectable_bus_id, bus_id=bus_id, + p0=p0, q0=q0) + + +def create_buses(n: Network, n_pdp: pandapowerNet) -> None: + if len(n_pdp.bus) > 0: + vl_id = build_voltage_level_id(n_pdp.bus.index.astype(str)) + topology_kind = ['BUS_BREAKER'] * len(n_pdp.bus) + nominal_v = n_pdp.bus['vn_kv'] + low_voltage_limit = n_pdp.bus['min_vm_pu'] * nominal_v if 'min_vm_pu' in n_pdp.bus.columns else None + high_voltage_limit = n_pdp.bus['max_vm_pu'] * nominal_v if 'max_vm_pu' in n_pdp.bus.columns else None + substation_id = ['s'] * len(n_pdp.bus) + # TODO topology kind should have a default value + n.create_voltage_levels(id=vl_id, substation_id=substation_id, topology_kind=topology_kind, nominal_v=nominal_v, + low_voltage_limit=low_voltage_limit, high_voltage_limit=high_voltage_limit) + bus_id = build_bus_id(n_pdp.bus.index.astype(str)) + name = get_name(n_pdp.bus, 'name') + n.create_buses(id=bus_id, name=name, voltage_level_id=vl_id) diff --git a/pypowsybl/network/impl/perunit.py b/pypowsybl/network/impl/perunit.py index c50395a085..8b20aefb4e 100644 --- a/pypowsybl/network/impl/perunit.py +++ b/pypowsybl/network/impl/perunit.py @@ -4,25 +4,23 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # SPDX-License-Identifier: MPL-2.0 # -from typing import List -import math +import pickle +import warnings + import pandas as pd -import numpy as np from numpy.typing import ArrayLike -from pypowsybl import _pypowsybl -from pypowsybl.utils import _adapt_df_or_kwargs -from .network import ElementType, Network - -def _adapt_to_dataframe(element_type: ElementType, df: pd.DataFrame = None, **kwargs: ArrayLike) -> pd.DataFrame: - metadata = _pypowsybl.get_network_elements_dataframe_metadata(element_type) - return _adapt_df_or_kwargs(metadata, df, **kwargs) +from .network import Network class PerUnitView: # pylint: disable=too-many-public-methods """ A per-unit view of a network, providing getters and update methods for some of the network tables. + + .. deprecated:: 1.8.0 + Use per unit mode of network directly instead. + """ def __init__(self, network: Network, sn: float = 100): @@ -33,9 +31,10 @@ def __init__(self, network: Network, sn: float = 100): network: the underlying network sn: the base power, in MW """ - self._network = network - self._sn = sn - self.sqrt3 = math.sqrt(3) + warnings.warn("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead", DeprecationWarning) + self._network = pickle.loads(pickle.dumps(network)) + self._network.per_unit = True + self._network.nominal_apparent_power = sn @property def network(self) -> Network: @@ -49,97 +48,10 @@ def sn(self) -> float: """ The base power, in MW, used for per-uniting """ - return self._sn - - def _get_nominal_v(self) -> pd.Series: - """ A series of nominal voltages for all voltage levels - """ - return self._network.get_voltage_levels()['nominal_v'] - - def _get_indexed_nominal_v(self, df: pd.DataFrame, vl_attr: str = 'voltage_level_id') -> pd.Series: - """ A series of nominal voltages indexed on the provided dataframe index. - """ - return pd.merge(df, self._get_nominal_v(), left_on=vl_attr, right_index=True)['nominal_v'] - - def _per_unit_p(self, df: pd.DataFrame, columns: List[str]) -> None: - df[columns] /= self.sn - - def _per_unit_v(self, df: pd.DataFrame, columns: List[str], nominal_v: pd.Series) -> None: - for col in columns: - df[col] /= nominal_v - - def _per_unit_angle(self, df: pd.DataFrame, columns: List[str]) -> None: - for col in columns: - df[col] = np.deg2rad(df[col]) - - def _per_unit_r(self, df: pd.DataFrame, columns: List[str], nominal_v: pd.Series) -> None: - factor = nominal_v ** 2 / self.sn - for col in columns: - df[col] /= factor - - def _per_unit_r_not_same_nom_v(self, df: pd.DataFrame, columns: List[str], nominal_v1: pd.Series, nominal_v2: pd.Series) -> None: - factor = nominal_v1 * nominal_v2 / self.sn - for col in columns: - df[col] /= factor - - def _per_unit_g(self, df: pd.DataFrame, columns: List[str], nominal_v: pd.Series) -> None: - factor = nominal_v ** 2 / self.sn - for col in columns: - df[col] *= factor - - def _y(self, df: pd.DataFrame, r_col: str, x_col: str) -> pd.Series: - return pd.Series(df.apply(lambda row: np.reciprocal(np.complex128(row[r_col] + row[x_col] * 1j)), axis=1)) - - def _per_unit_g_not_same_nom_v(self, df: pd.DataFrame, column: str, ytr: pd.Series, nominal_v1: pd.Series, nominal_v2: pd.Series) -> None: - df[column] = (df[column] * nominal_v1 * nominal_v1 + (nominal_v1 - nominal_v2) * nominal_v1 * ytr.apply(lambda row: row.real)) / self.sn - - def _per_unit_b_not_same_nom_v(self, df: pd.DataFrame, column: str, ytr: pd.Series, nominal_v1: pd.Series, nominal_v2: pd.Series) -> None: - df[column] = (df[column] * nominal_v1 * nominal_v1 + (nominal_v1 - nominal_v2) * nominal_v1 * ytr.apply(lambda row: row.imag)) / self.sn - - def _per_unit_i(self, df: pd.DataFrame, columns: List[str], nominal_v: pd.Series) -> None: - factor = self.sn * 10 ** 3 / (self.sqrt3 * nominal_v) - for col in columns: - df[col] /= factor - - def _un_per_unit_p(self, df: pd.DataFrame, columns: List[str]) -> None: - for col in columns: - if col in df.columns: - df[col] *= self.sn - - def _un_per_unit_v(self, df: pd.DataFrame, columns: List[str], nominal_v: pd.Series) -> None: - for col in columns: - if col in df.columns: - df[col] *= nominal_v - - def _un_per_unit_angle(self, df: pd.DataFrame, columns: List[str]) -> None: - for col in columns: - if col in df.columns: - df[col] = np.rad2deg(df[col]) - - def _un_per_unit_r(self, df: pd.DataFrame, columns: List[str], nominal_v: pd.Series) -> None: - factor = nominal_v ** 2 / self.sn - for col in columns: - if col in df.columns: - df[col] *= factor - - def _un_per_unit_g(self, df: pd.DataFrame, columns: List[str], nominal_v: pd.Series) -> None: - factor = nominal_v ** 2 / self.sn - for col in columns: - if col in df.columns: - df[col] /= factor - - def _un_per_unit_i(self, df: pd.DataFrame, columns: List[str], nominal_v: pd.Series) -> None: - factor = self.sn * 10 ** 3 / (self.sqrt3 * nominal_v) - for col in columns: - if col in df.columns: - df[col] *= factor + return self._network.nominal_apparent_power() def get_buses(self) -> pd.DataFrame: - buses = self._network.get_buses() - nominal_v = self._get_indexed_nominal_v(buses) - self._per_unit_v(buses, ['v_mag'], nominal_v) - self._per_unit_angle(buses, ['v_angle']) - return buses + return self._network.get_buses() def get_generators(self) -> pd.DataFrame: """ @@ -148,12 +60,7 @@ def get_generators(self) -> pd.DataFrame: Returns: a per-united dataframe of generators. """ - generators = self._network.get_generators() - nominal_v = self._get_indexed_nominal_v(generators) - self._per_unit_p(generators, ['target_p', 'target_q', 'min_p', 'min_q', 'max_p', 'max_q', 'p', 'q']) - self._per_unit_v(generators, ['target_v'], nominal_v) - self._per_unit_i(generators, ['i'], nominal_v) - return generators + return self._network.get_generators() def get_loads(self) -> pd.DataFrame: """ @@ -162,12 +69,7 @@ def get_loads(self) -> pd.DataFrame: Returns: a per-united dataframe of loads. """ - loads = self._network.get_loads() - self._per_unit_p(loads, ['p0', 'q0', 'p', 'q']) - nominal_v = pd.merge(loads, self._get_nominal_v(), - left_on='voltage_level_id', right_index=True)['nominal_v'] - self._per_unit_i(loads, ['i'], nominal_v) - return loads + return self._network.get_loads() def get_lines(self) -> pd.DataFrame: """ @@ -176,19 +78,7 @@ def get_lines(self) -> pd.DataFrame: Returns: a per-united dataframe of lines. """ - lines = self._network.get_lines() - nominal_v1 = self._get_indexed_nominal_v(lines, 'voltage_level1_id') - nominal_v2 = self._get_indexed_nominal_v(lines, 'voltage_level2_id') - self._per_unit_p(lines, ['p1', 'p2', 'q1', 'q2']) - self._per_unit_i(lines, ['i1'], nominal_v1) - self._per_unit_i(lines, ['i2'], nominal_v2) - ytr = self._y(lines, 'r', 'x') - self._per_unit_r_not_same_nom_v(lines, ['r', 'x'], nominal_v1, nominal_v2) - self._per_unit_g_not_same_nom_v(lines, 'g1', ytr, nominal_v1, nominal_v2) - self._per_unit_g_not_same_nom_v(lines, 'g2', ytr, nominal_v2, nominal_v1) - self._per_unit_b_not_same_nom_v(lines, 'b1', ytr, nominal_v1, nominal_v2) - self._per_unit_b_not_same_nom_v(lines, 'b2', ytr, nominal_v2, nominal_v1) - return lines + return self._network.get_lines() def get_2_windings_transformers(self) -> pd.DataFrame: """ @@ -197,17 +87,7 @@ def get_2_windings_transformers(self) -> pd.DataFrame: Returns: a per-united dataframe of 2 windings transformers. """ - two_windings_transformers = self._network.get_2_windings_transformers() - self._per_unit_p(two_windings_transformers, ['p1', 'q1', 'p2', 'q2']) - nominal_v1 = self._get_indexed_nominal_v(two_windings_transformers, 'voltage_level1_id') - nominal_v2 = self._get_indexed_nominal_v(two_windings_transformers, 'voltage_level2_id') - self._per_unit_i(two_windings_transformers, ['i1'], nominal_v1) - self._per_unit_i(two_windings_transformers, ['i2'], nominal_v2) - self._per_unit_r(two_windings_transformers, ['r', 'x'], nominal_v2) - self._per_unit_g(two_windings_transformers, ['g', 'b'], nominal_v2) - two_windings_transformers['rated_u1'] /= nominal_v1 - two_windings_transformers['rated_u2'] /= nominal_v2 - return two_windings_transformers + return self._network.get_2_windings_transformers() def get_3_windings_transformers(self) -> pd.DataFrame: """ @@ -216,22 +96,7 @@ def get_3_windings_transformers(self) -> pd.DataFrame: Returns: a per-united dataframe of 3 windings transformers. """ - three_windings_transformers = self._network.get_3_windings_transformers() - self._per_unit_p(three_windings_transformers, ['p1', 'q1', 'p2', 'q2', 'p3', 'q3']) - nominal_v1 = self._get_indexed_nominal_v(three_windings_transformers, 'voltage_level1_id') - nominal_v2 = self._get_indexed_nominal_v(three_windings_transformers, 'voltage_level2_id') - nominal_v3 = self._get_indexed_nominal_v(three_windings_transformers, 'voltage_level3_id') - nominal_v0 = three_windings_transformers['rated_u0'] - self._per_unit_i(three_windings_transformers, ['i1'], nominal_v1) - self._per_unit_i(three_windings_transformers, ['i2'], nominal_v2) - self._per_unit_i(three_windings_transformers, ['i3'], nominal_v3) - self._per_unit_r(three_windings_transformers, ['r1', 'x1', 'r2', 'x2', 'r3', 'x3'], nominal_v0) - self._per_unit_g(three_windings_transformers, ['g1', 'b1', 'g2', 'b2', 'g3', 'b3'], nominal_v0) - three_windings_transformers['rated_u1'] /= nominal_v1 - three_windings_transformers['rated_u2'] /= nominal_v2 - three_windings_transformers['rated_u3'] /= nominal_v3 - three_windings_transformers['rated_u0'] = 1 - return three_windings_transformers + return self._network.get_3_windings_transformers() def get_shunt_compensators(self) -> pd.DataFrame: """ @@ -240,12 +105,7 @@ def get_shunt_compensators(self) -> pd.DataFrame: Returns: a per-united dataframe of shunt compensators. """ - shunt_compensators = self._network.get_shunt_compensators() - nominal_v = self._get_indexed_nominal_v(shunt_compensators) - self._per_unit_p(shunt_compensators, ['p', 'q']) - self._per_unit_g(shunt_compensators, ['g', 'b'], nominal_v) - self._per_unit_i(shunt_compensators, ['i'], nominal_v) - return shunt_compensators + return self._network.get_shunt_compensators() def get_dangling_lines(self) -> pd.DataFrame: """ @@ -254,13 +114,7 @@ def get_dangling_lines(self) -> pd.DataFrame: Returns: a per-united dataframe of dangling lines. """ - dangling_lines = self._network.get_dangling_lines() - nominal_v = self._get_indexed_nominal_v(dangling_lines) - self._per_unit_p(dangling_lines, ['p', 'q', 'q0', 'p0']) - self._per_unit_i(dangling_lines, ['i'], nominal_v) - self._per_unit_r(dangling_lines, ['r', 'x'], nominal_v) - self._per_unit_g(dangling_lines, ['g', 'b'], nominal_v) - return dangling_lines + return self._network.get_dangling_lines() def get_lcc_converter_stations(self) -> pd.DataFrame: """ @@ -269,11 +123,7 @@ def get_lcc_converter_stations(self) -> pd.DataFrame: Returns: a per-united dataframe of LCC converter stations. """ - lcc_converter_stations = self._network.get_lcc_converter_stations() - nominal_v = self._get_indexed_nominal_v(lcc_converter_stations) - self._per_unit_p(lcc_converter_stations, ['p', 'q']) - self._per_unit_i(lcc_converter_stations, ['i'], nominal_v) - return lcc_converter_stations + return self._network.get_lcc_converter_stations() def get_vsc_converter_stations(self) -> pd.DataFrame: """ @@ -282,12 +132,7 @@ def get_vsc_converter_stations(self) -> pd.DataFrame: Returns: a per-united dataframe of VSC converter stations. """ - vsc_converter_stations = self._network.get_vsc_converter_stations() - nominal_v = self._get_indexed_nominal_v(vsc_converter_stations) - self._per_unit_p(vsc_converter_stations, ['p', 'q', 'target_q', 'max_q', 'min_q']) - self._per_unit_i(vsc_converter_stations, ['i'], nominal_v) - self._per_unit_v(vsc_converter_stations, ['target_v'], nominal_v) - return vsc_converter_stations + return self._network.get_vsc_converter_stations() def get_static_var_compensators(self) -> pd.DataFrame: """ @@ -296,12 +141,7 @@ def get_static_var_compensators(self) -> pd.DataFrame: Returns: a per-united dataframe of static var compensators. """ - static_var_compensators = self._network.get_static_var_compensators() - nominal_v = self._get_indexed_nominal_v(static_var_compensators) - self._per_unit_p(static_var_compensators, ['p', 'q', 'target_q']) - self._per_unit_i(static_var_compensators, ['i'], nominal_v) - self._per_unit_v(static_var_compensators, ['target_v'], nominal_v) - return static_var_compensators + return self._network.get_static_var_compensators() def get_voltage_levels(self) -> pd.DataFrame: """ @@ -310,9 +150,7 @@ def get_voltage_levels(self) -> pd.DataFrame: Returns: a per-united dataframe of voltage levels. """ - voltage_levels = self._network.get_voltage_levels() - self._per_unit_v(voltage_levels, ['low_voltage_limit', 'high_voltage_limit'], voltage_levels['nominal_v']) - return voltage_levels + return self._network.get_voltage_levels() def get_busbar_sections(self) -> pd.DataFrame: """ @@ -321,11 +159,7 @@ def get_busbar_sections(self) -> pd.DataFrame: Returns: a per-united dataframe of busbar sections. """ - busbar_sections = self._network.get_busbar_sections() - nominal_v = self._get_indexed_nominal_v(busbar_sections) - self._per_unit_v(busbar_sections, ['v'], nominal_v) - self._per_unit_angle(busbar_sections, ['angle']) - return busbar_sections + return self._network.get_busbar_sections() def get_hvdc_lines(self) -> pd.DataFrame: """ @@ -334,10 +168,7 @@ def get_hvdc_lines(self) -> pd.DataFrame: Returns: a per-united dataframe of HVDC lines. """ - hvdc_lines = self._network.get_hvdc_lines() - self._per_unit_p(hvdc_lines, ['max_p', 'target_p']) - self._per_unit_r(hvdc_lines, ['r'], hvdc_lines['nominal_v']) - return hvdc_lines + return self._network.get_hvdc_lines() def get_reactive_capability_curve_points(self) -> pd.DataFrame: """ @@ -346,9 +177,7 @@ def get_reactive_capability_curve_points(self) -> pd.DataFrame: Returns: A per-united dataframe of reactive capability curves. """ - reactive_capability_curve_points = self._network.get_reactive_capability_curve_points() - self._per_unit_p(reactive_capability_curve_points, ['p', 'min_q', 'max_q']) - return reactive_capability_curve_points + return self._network.get_reactive_capability_curve_points() def get_batteries(self) -> pd.DataFrame: """ @@ -357,11 +186,7 @@ def get_batteries(self) -> pd.DataFrame: Returns: A per-united dataframe of batteries. """ - batteries = self._network.get_batteries() - nominal_v = self._get_indexed_nominal_v(batteries) - self._per_unit_p(batteries, ['target_p', 'target_q', 'p', 'q', 'min_p', 'max_p', 'min_q', 'max_q']) - self._per_unit_i(batteries, ['i'], nominal_v) - return batteries + return self._network.get_batteries() def get_ratio_tap_changers(self) -> pd.DataFrame: """ @@ -370,154 +195,79 @@ def get_ratio_tap_changers(self) -> pd.DataFrame: Returns: A per-united dataframe of ratio tap changers. """ - ratio_tap_changers = self._network.get_ratio_tap_changers() - voltage_levels = ratio_tap_changers[[]].merge( - self.get_2_windings_transformers()[['voltage_level1_id', 'voltage_level2_id']], - left_index=True, right_index=True) - nominal_v1 = self._get_indexed_nominal_v(voltage_levels, 'voltage_level1_id') - nominal_v2 = self._get_indexed_nominal_v(voltage_levels, 'voltage_level2_id') - ratio_tap_changers['rho'] *= nominal_v1 / nominal_v2 - self._per_unit_angle(ratio_tap_changers, ['alpha']) - return ratio_tap_changers + return self._network.get_ratio_tap_changers() def update_buses(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update buses from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.BUS, df, **kwargs).copy() - nominal_v = self._get_indexed_nominal_v(self._network.get_buses()) - self._un_per_unit_v(to_update, ['v_mag'], nominal_v) - self._un_per_unit_angle(to_update, ['v_angle']) - self._network.update_buses(to_update) + self._network.update_buses(df, **kwargs) def update_generators(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update generators from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.GENERATOR, df, **kwargs).copy() - nominal_v = self._get_indexed_nominal_v(self._network.get_generators()) - self._un_per_unit_v(to_update, ['target_v'], nominal_v) - self._un_per_unit_p(to_update, ['target_p', 'target_q', 'p', 'q']) - self._un_per_unit_i(to_update, ['i'], nominal_v) - self._network.update_generators(to_update) + self._network.update_generators(df, **kwargs) def update_loads(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update loads from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.LOAD, df, **kwargs).copy() - nominal_v = self._get_indexed_nominal_v(self._network.get_loads()) - self._un_per_unit_p(to_update, ['p0', 'q0', 'p', 'q']) - self._un_per_unit_i(to_update, ['i'], nominal_v) - self._network.update_loads(to_update) + self._network.update_loads(df, **kwargs) def update_batteries(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update batteries from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.BATTERY, df, **kwargs).copy() - nominal_v = self._get_indexed_nominal_v(self._network.get_batteries()) - self._un_per_unit_p(to_update, ['target_p', 'target_q', 'p', 'q', 'max_p', 'min_p', 'min_q', 'max_q']) - self._un_per_unit_i(to_update, ['i'], nominal_v) - self._network.update_batteries(to_update) + self._network.update_batteries(df, **kwargs) def update_dangling_lines(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update dangling lines from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.DANGLING_LINE, df, **kwargs).copy() - nominal_v = self._get_indexed_nominal_v(self._network.get_dangling_lines()) - self._un_per_unit_p(to_update, ['p0', 'q0', 'p', 'q']) - self._un_per_unit_i(to_update, ['i'], nominal_v) - self._un_per_unit_r(to_update, ['r', 'x'], nominal_v) - self._un_per_unit_g(to_update, ['g', 'b'], nominal_v) - self._network.update_dangling_lines(to_update) + self._network.update_dangling_lines(df, **kwargs) def update_vsc_converter_stations(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update VSC converter stations from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.VSC_CONVERTER_STATION, df, **kwargs).copy() - nominal_v = self._get_indexed_nominal_v(self._network.get_vsc_converter_stations()) - self._un_per_unit_p(to_update, ['p', 'q', 'target_q', 'max_q', 'min_q']) - self._un_per_unit_i(to_update, ['i'], nominal_v) - self._un_per_unit_v(to_update, ['target_v'], nominal_v) - self._network.update_vsc_converter_stations(to_update) + self._network.update_vsc_converter_stations(df, **kwargs) def update_static_var_compensators(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update static var compensators from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.STATIC_VAR_COMPENSATOR, df, **kwargs).copy() - nominal_v = self._get_indexed_nominal_v(self._network.get_static_var_compensators()) - self._un_per_unit_p(to_update, ['p', 'q', 'target_q']) - self._un_per_unit_i(to_update, ['i'], nominal_v) - self._un_per_unit_v(to_update, ['target_v'], nominal_v) - self._network.update_static_var_compensators(to_update) + self._network.update_static_var_compensators(df, **kwargs) def update_hvdc_lines(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update HVDC lines from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.HVDC_LINE, df, **kwargs).copy() - nominal_v = self._network.get_hvdc_lines()['nominal_v'] - self._un_per_unit_p(to_update, ['target_p']) - self._un_per_unit_r(to_update, ['r'], nominal_v) - self._network.update_hvdc_lines(to_update) + self._network.update_hvdc_lines(df, **kwargs) def update_lines(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update lines from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.LINE, df, **kwargs).copy() - nominal_v = self._get_indexed_nominal_v(self._network.get_lines(), 'voltage_level1_id') - self._un_per_unit_p(to_update, ['p1', 'p2', 'q1', 'q2']) - self._un_per_unit_r(to_update, ['r', 'x'], nominal_v) - self._un_per_unit_g(to_update, ['g1', 'g2', 'b1', 'b2'], nominal_v) - self._network.update_lines(to_update) + self._network.update_lines(df, **kwargs) def update_2_windings_transformers(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update 2 windings transformers from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.TWO_WINDINGS_TRANSFORMER, df, **kwargs).copy() - ref = self._network.get_2_windings_transformers() - nominal_v1 = self._get_indexed_nominal_v(ref, 'voltage_level1_id') - nominal_v2 = self._get_indexed_nominal_v(ref, 'voltage_level2_id') - self._un_per_unit_p(to_update, ['p1', 'p2', 'q1', 'q2']) - self._un_per_unit_r(to_update, ['r', 'x'], nominal_v2) - self._un_per_unit_g(to_update, ['g', 'b'], nominal_v2) - self._un_per_unit_v(to_update, ['rated_u1'], nominal_v1) - self._un_per_unit_v(to_update, ['rated_u2'], nominal_v2) - self._network.update_2_windings_transformers(to_update) + self._network.update_2_windings_transformers(df, **kwargs) def update_3_windings_transformers(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update 3 windings transformers from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.THREE_WINDINGS_TRANSFORMER, df, **kwargs).copy() - ref = self._network.get_3_windings_transformers() - nominal_v1 = self._get_indexed_nominal_v(ref, 'voltage_level1_id') - nominal_v2 = self._get_indexed_nominal_v(ref, 'voltage_level2_id') - nominal_v3 = self._get_indexed_nominal_v(ref, 'voltage_level3_id') - nominal_v0 = ref['rated_u0'] - self._un_per_unit_p(to_update, ['p1', 'p2', 'p3', 'q1', 'q2', 'q3']) - self._un_per_unit_r(to_update, ['r1', 'x1', 'r2', 'x2', 'r3', 'x3'], nominal_v0) - self._un_per_unit_g(to_update, ['g1', 'b1', 'g2', 'b2', 'g3', 'b3'], nominal_v0) - self._un_per_unit_v(to_update, ['rated_u1'], nominal_v1) - self._un_per_unit_v(to_update, ['rated_u2'], nominal_v2) - self._un_per_unit_v(to_update, ['rated_u3'], nominal_v3) - self._network.update_3_windings_transformers(to_update) + self._network.update_3_windings_transformers(df, **kwargs) def update_lcc_converter_station(self, df: pd.DataFrame = None, **kwargs: ArrayLike) -> None: """ Update LCC converter stations from per-united data. """ - to_update = _adapt_to_dataframe(ElementType.LCC_CONVERTER_STATION, df, **kwargs).copy() - nominal_v = self._get_indexed_nominal_v(self._network.get_lcc_converter_stations()) - self._un_per_unit_p(to_update, ['p', 'q']) - self._un_per_unit_i(to_update, ['i'], nominal_v) - self._network.update_lcc_converter_stations(to_update) + self._network.update_lcc_converter_stations(df, **kwargs) def per_unit_view(network: Network, sn: float = 100) -> PerUnitView: diff --git a/pypowsybl/network/impl/util.py b/pypowsybl/network/impl/util.py index 32e47b65e1..ea7b2be2bb 100644 --- a/pypowsybl/network/impl/util.py +++ b/pypowsybl/network/impl/util.py @@ -31,6 +31,16 @@ def get_import_formats() -> List[str]: return _pp.get_network_import_formats() +def get_import_supported_extensions() -> List[str]: + """ + Get list of supported import extensions + + Returns: + the list of supported import extensions + """ + return _pp.get_network_import_supported_extensions() + + def get_export_formats() -> List[str]: """ Get list of supported export formats @@ -41,6 +51,16 @@ def get_export_formats() -> List[str]: return _pp.get_network_export_formats() +def get_import_post_processors() -> List[str]: + """ + Get list of supported import post processors + + Returns: + the list of supported import post processors + """ + return _pp.get_network_import_post_processors() + + def get_import_parameters(fmt: str) -> DataFrame: """ Supported import parameters for a given format. diff --git a/pypowsybl/shortcircuit/impl/short_circuit_analysis_result.py b/pypowsybl/shortcircuit/impl/short_circuit_analysis_result.py index 34f3bc3573..48882ffe4c 100644 --- a/pypowsybl/shortcircuit/impl/short_circuit_analysis_result.py +++ b/pypowsybl/shortcircuit/impl/short_circuit_analysis_result.py @@ -38,9 +38,10 @@ def fault_results(self) -> pd.DataFrame: def feeder_results(self) -> pd.DataFrame: """ contains the contributions of each feeder to the short-circuit current, in a dataframe representation. The rows - are the ids of contributing connectable ids sorted by faults and the columns is the current, either in - three-phase magnitude or detailed with magnitude and angle for each phase. The magnitude of the currents are in - A. It should be empty when the parameter with_feeder_result is set to false + are the ids of the contributing feeder IDs, sorted by fault and the columns are the current, either in + three-phase magnitude or detailed with magnitude and angle for each phase. The current magnitudes are in + A. If the feeder is a branch or a three-winding transformer, the side to which the result applies. + The dataframe should be empty if the with_feeder_result parameter is set to false. """ return create_data_frame_from_series_array(_pypowsybl.get_feeder_results(self._handle, self._with_fortescue_result)) diff --git a/pypowsybl/utils/impl/dataframes.py b/pypowsybl/utils/impl/dataframes.py index ed28e5dc6a..28fe5aafd8 100644 --- a/pypowsybl/utils/impl/dataframes.py +++ b/pypowsybl/utils/impl/dataframes.py @@ -41,14 +41,15 @@ def _adapt_kwargs(metadata: List[_pp.SeriesMetadata], **kwargs: _Any) -> DataFra columns = {} expected_size = None for key, value in kwargs.items(): - col = _to_array(value) - size = col.shape[0] - if expected_size is None: - expected_size = size - elif size != expected_size: - raise ValueError(f'Network elements update: all arguments must have the same size, ' - f'got size {size} for series {key}, expected {expected_size}') - columns[key] = col + if value is not None: + col = _to_array(value) + size = col.shape[0] + if expected_size is None: + expected_size = size + elif size != expected_size: + raise ValueError(f'Network elements update: all arguments must have the same size, ' + f'got size {size} for series {key}, expected {expected_size}') + columns[key] = col index = None if len(index_columns) == 1: @@ -101,7 +102,11 @@ def _create_c_dataframe(df: DataFrame, series_metadata: List[_pp.SeriesMetadata] series = df[series_name] series_type = metadata_by_name[series_name].type columns_types.append(series_type) - columns_values.append(series.values) + if series.values.size and isinstance(series.values[0], np.bool_): + # to avoid DeprecationWarning: In future, it will be an error for 'np.bool_' scalars to be interpreted as an index + columns_values.append(series.values.astype(int)) + else: + columns_values.append(series.values) is_index.append(False) return _pp.create_dataframe(columns_values, columns_names, columns_types, is_index) diff --git a/pypowsybl/voltage_initializer/impl/voltage_initializer_parameters.py b/pypowsybl/voltage_initializer/impl/voltage_initializer_parameters.py index 3dca62dd6b..d435aeea4c 100644 --- a/pypowsybl/voltage_initializer/impl/voltage_initializer_parameters.py +++ b/pypowsybl/voltage_initializer/impl/voltage_initializer_parameters.py @@ -184,7 +184,7 @@ def set_reactive_slack_buses_mode(self, reactive_slack_buses_mode: VoltageInitia log_level_solver: the log level. """ voltage_initializer_set_reactive_slack_buses_mode(self._handle, reactive_slack_buses_mode) - + def set_min_plausible_low_voltage_limit(self, min_plausible_low_voltage_level: float) -> None: """ Changes the minimal plausible value for low voltage limits (in p.u.) in ACOPF solving. @@ -332,4 +332,3 @@ def set_twt_ratio_variable_scaling_factor(self, twt_ratio_variable_scaling_facto twt_ratio_variable_scaling_factor: is > 0. """ voltage_initializer_set_twt_ratio_variable_scaling_factor(self._handle, twt_ratio_variable_scaling_factor) - diff --git a/requirements.txt b/requirements.txt index 102420a2dc..5565731104 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,13 @@ # actual dependencies pandas==2.2.2; python_version >= "3.9" pandas==2.0.3; python_version <= "3.8" -prettytable==2.0.0 +prettytable==3.11.0 # last version supporting python 3.8 networkx -matplotlib==3.9.0; python_version >= "3.9" -matplotlib; python_version <= "3.8" +matplotlib==3.9.2; python_version >= "3.9" +matplotlib==3.7.5; python_version <= "3.8" + +# optional dependencies +pandapower==2.14.11 # documentation dependencies sphinx==7.1.2 @@ -14,7 +17,7 @@ furo==2024.1.29 setuptools==73.0.1 wheel==0.44.0 coverage==7.3.2 -pytest>=6.2.5 +pytest>=8.3.3 mypy==0.982 pandas-stubs==2.2.2.240603; python_version >= "3.9" pandas-stubs==2.0.3.230814; python_version <= "3.8" diff --git a/setup.cfg b/setup.cfg index 72363d4b3a..51f6b9bf10 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,6 +16,7 @@ classifiers = Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 +; Programming Language :: Python :: 3.13 Programming Language :: Python :: Implementation :: CPython License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) Operating System :: POSIX :: Linux @@ -33,5 +34,9 @@ install_requires = pandas>=2.0.3; python_version <= "3.8" networkx +[options.extras_require] +pandapower = + pandapower>=2.4.11 + [options.package_data] pypowsybl: py.typed, *.pyi diff --git a/tests/test_dynamic.py b/tests/test_dynamic.py index e7c0770a21..ae225547de 100644 --- a/tests/test_dynamic.py +++ b/tests/test_dynamic.py @@ -15,48 +15,117 @@ def set_up(): pp.set_config_read(False) -def test_get_possible_events(): - assert set(dyn.EventMapping.get_possible_events()) == {dyn.EventType.DISCONNECTION} - - def test_add_mapping(): - id = "test_id" - parameter_id = "test_parameter" model_mapping = dyn.ModelMapping() - model_mapping.add_alpha_beta_load(id, parameter_id) - model_mapping.add_one_transformer_load(id, parameter_id) - model_mapping.add_generator_synchronous_three_windings(id, parameter_id) - model_mapping.add_generator_synchronous_three_windings_proportional_regulations( - id, parameter_id) - model_mapping.add_generator_synchronous_four_windings(id, parameter_id) - model_mapping.add_generator_synchronous_four_windings_proportional_regulations( - id, parameter_id) - model_mapping.add_current_limit_automaton( - id, parameter_id, dyn.Side.TWO) - - -def test_dataframe_mapping(): + # Equipments + model_mapping.add_base_load(static_id='LOAD', parameter_set_id='lab', dynamic_model_id='DM_LOAD', model_name='LoadPQ') + model_mapping.add_load_one_transformer(static_id='LOAD', parameter_set_id='lt', dynamic_model_id='DM_LT', model_name='LoadOneTransformer') + model_mapping.add_load_one_transformer_tap_changer(static_id='LOAD', parameter_set_id='lt_tc', dynamic_model_id='DM_LT_TC', model_name='LoadOneTransformerTapChanger') + model_mapping.add_load_two_transformers(static_id='LOAD', parameter_set_id='ltt', dynamic_model_id='DM_LTT', model_name='LoadTwoTransformers') + model_mapping.add_load_two_transformers_tap_changers(static_id='LOAD', parameter_set_id='ltt_tc', dynamic_model_id='DM_LTT_TC', model_name='LoadTwoTransformersTapChangers') + model_mapping.add_base_generator(static_id='GEN', parameter_set_id='gen', dynamic_model_id='DM_GEN', model_name='GeneratorFictitious') + model_mapping.add_synchronized_generator(static_id='GEN', parameter_set_id='sgen', dynamic_model_id='DM_SYNCH_GEN', model_name='GeneratorPVFixed') + model_mapping.add_synchronous_generator(static_id='GEN', parameter_set_id='ssgen', dynamic_model_id='DM_SYNCHRONOUS_GEN', model_name='GeneratorSynchronousThreeWindings') + model_mapping.add_wecc(static_id='GEN', parameter_set_id='wecc', dynamic_model_id='DM_WECC', model_name='WT4BWeccCurrentSource') + model_mapping.add_grid_forming_converter(static_id='GEN', parameter_set_id='gf', dynamic_model_id='DM_GF', model_name='GridFormingConverterMatchingControl') + model_mapping.add_signal_n_generator(static_id='GEN', parameter_set_id='signal_n', dynamic_model_id='DM_SIGNAL_N', model_name='GeneratorPVSignalN') + model_mapping.add_hvdc_p(static_id='HVDC_LINE', parameter_set_id='hvdc_p', dynamic_model_id='DM_HVDC_P', model_name='HvdcPV') + model_mapping.add_hvdc_vsc(static_id='HVDC_LINE', parameter_set_id='hvdc_vsc', dynamic_model_id='DM_HVDC_VSC', model_name='HvdcVSCDanglingP') + model_mapping.add_base_transformer(static_id='TFO', parameter_set_id='tfo', dynamic_model_id='DM_TFO', model_name='TransformerFixedRatio') + model_mapping.add_base_static_var_compensator(static_id='SVARC', parameter_set_id='svarc', dynamic_model_id='DM_SVARC', model_name='StaticVarCompensatorPV') + model_mapping.add_base_line(static_id='LINE', parameter_set_id='l', dynamic_model_id='DM_LINE', model_name='Line') + model_mapping.add_base_bus(static_id='BUS', parameter_set_id='bus', dynamic_model_id='DM_BUS', model_name='Bus') + model_mapping.add_infinite_bus(static_id='BUS', parameter_set_id='inf_bus', dynamic_model_id='DM_INF_BUS', model_name='InfiniteBus') + # Dynamic automation systems + model_mapping.add_overload_management_system(dynamic_model_id='DM_OV', parameter_set_id='ov', controlled_branch='LINE1', + i_measurement='LINE2', i_measurement_side='TWO', model_name='OverloadManagementSystem') + model_mapping.add_two_levels_overload_management_system(dynamic_model_id='DM_TOV', parameter_set_id='tov', + controlled_branch= 'LINE1', + i_measurement_1='LINE1', i_measurement_1_side='TWO', + i_measurement_2='LINE2', i_measurement_2_side='ONE', + model_name='TwoLevelsOverloadManagementSystem') + model_mapping.add_under_voltage_automation_system(dynamic_model_id='DM_UV', parameter_set_id='psi', + generator='GEN', model_name='UnderVoltage') + model_mapping.add_phase_shifter_i_automation_system(dynamic_model_id='DM_PS_I', parameter_set_id='psi', + transformer='TRA', model_name='PhaseShifterI') + model_mapping.add_phase_shifter_p_automation_system(dynamic_model_id='DM_PS_P', parameter_set_id='psp', + transformer='TRA', model_name='PhaseShifterP') + model_mapping.add_phase_shifter_blocking_i_automation_system(dynamic_model_id='DM_PSB_I', parameter_set_id='psb', + phase_shifter_id='PSI', model_name='PhaseShifterBlockingI') + model_mapping.add_tap_changer_automation_system(dynamic_model_id='DM_TC', parameter_set_id='tc', static_id='LOAD', + side='HIGH_VOLTAGE', model_name='TapChangerAutomaton') + # Equipment with default model name and dynamic id + model_mapping.add_base_load(static_id='LOAD', parameter_set_id='lab') + # Equipment model from Supported models + model_name = model_mapping.get_supported_models(dyn.DynamicMappingType.BASE_LOAD)[0] + model_mapping.add_base_load(static_id='LOAD', parameter_set_id='lab', dynamic_model_id='DM_LOAD', model_name=model_name) + + +def test_dynamic_dataframe(): network = pp.network.create_ieee9() model_mapping = dyn.ModelMapping() - load_mapping_df = pd.DataFrame.from_dict({"static_id": [network.get_loads().loc[l].name for l in network.get_loads().index], - "parameter_set_id": ["LAB" for l in network.get_loads().index]}) - generator_mapping_df = pd.DataFrame.from_dict({"static_id": [network.get_generators().loc[l].name for l in network.get_generators().index], - "parameter_set_id": ["GSTWPR" for l in network.get_generators().index]}) - model_mapping.add_all_dynamic_mappings(dyn.DynamicMappingType.ALPHA_BETA_LOAD, - load_mapping_df.set_index("static_id")) - model_mapping.add_all_dynamic_mappings( - dyn.DynamicMappingType.GENERATOR_SYNCHRONOUS_THREE_WINDINGS_PROPORTIONAL_REGULATIONS, generator_mapping_df.set_index("static_id")) + load_mapping_df = pd.DataFrame( + index=pd.Series(name='static_id', data=network.get_loads().index), + data={ + 'parameter_set_id': 'LAB', + 'model_name': 'LoadPQ' + } + ) + model_mapping.add_base_load(load_mapping_df) + + generator_mapping_df = pd.DataFrame( + index=pd.Series(name='static_id', data=network.get_generators().index), + data={ + 'parameter_set_id': 'GSTWPR', + 'model_name': 'GeneratorSynchronousThreeWindings' + } + ) + model_mapping.add_synchronous_generator(generator_mapping_df) + + tcb_df = pd.DataFrame.from_records( + index='dynamic_model_id', + columns=['dynamic_model_id', 'parameter_set_id', 'model_name'], + data=[('DM_TCB', 'tcb', 'TapChangerBlockingAutomaton')]) + tfo_df = pd.DataFrame.from_records( + index='dynamic_model_id', + columns=['dynamic_model_id', 'transformer_id'], + data=[('DM_TCB', 'TFO1'), + ('DM_TCB', 'TFO2'), + ('DM_TCB', 'TFO3')]) + measurement1_df = pd.DataFrame.from_records( + index='dynamic_model_id', + columns=['dynamic_model_id', 'measurement_point_id'], + data=[('DM_TCB', 'B1'), + ('DM_TCB', 'BS1')]) + measurement2_df = pd.DataFrame.from_records( + index='dynamic_model_id', + columns=['dynamic_model_id', 'measurement_point_id'], + data=[('DM_TCB', 'B4')]) + model_mapping.add_tap_changer_blocking_automation_system(tcb_df, tfo_df, measurement1_df, measurement2_df) def test_add_event(): - events = dyn.EventMapping() - events.add_disconnection("test_quadripole_id", 5, pp.dynamic.Side.ONE) - events.add_disconnection("test_generator_id", 3.3, pp.dynamic.Side.TWO) - events.add_disconnection("test_generator_id", 8.2, pp.dynamic.Side.TWO) + event_mapping = dyn.EventMapping() + event_mapping.add_disconnection(static_id='GEN', start_time=5) + event_mapping.add_disconnection(static_id='LINE', start_time=3.3, disconnect_only='TWO') + event_mapping.add_active_power_variation(static_id='LOAD', start_time=14, delta_p=2) + event_mapping.add_node_fault(static_id='BUS', start_time=12, fault_time=2, r_pu=0.1, x_pu=0.2) + + +def test_add_event_dataframe(): + event_mapping = dyn.EventMapping() + event_mapping_df = pd.DataFrame( + index=pd.Series(name='static_id', data=['GEN', 'LOAD']), + data={ + 'start_time': [10, 15], + 'delta_p': [2, 4] + }, + ) + event_mapping.add_active_power_variation(event_mapping_df) def test_add_curve(): timeseries = dyn.CurveMapping() - timeseries.add_curves("test_load_id_1", ["load_PPu", "load_QPu"]) - timeseries.add_curve("test_load_id_2", "load_PPu") + timeseries.add_curves('test_load_id_1', ['load_PPu', 'load_QPu']) + timeseries.add_curve('test_load_id_2', 'load_PPu') diff --git a/tests/test_flow_decomposition.py b/tests/test_flow_decomposition.py index 39047d2680..ca0100febe 100644 --- a/tests/test_flow_decomposition.py +++ b/tests/test_flow_decomposition.py @@ -142,7 +142,7 @@ def test_flow_decomposition_parameters(): 'enable_losses_compensation': [True, False], 'losses_compensation_epsilon': [-1, 1e-3, 1e-5], 'sensitivity_epsilon': [-1, 1e-3, 1e-5], - 'rescale_mode': [pp.flowdecomposition.RescaleMode.NONE, pp.flowdecomposition.RescaleMode.PROPORTIONAL, pp.flowdecomposition.RescaleMode.ACER_METHODOLOGY], + 'rescale_mode': [pp.flowdecomposition.RescaleMode.NONE, pp.flowdecomposition.RescaleMode.PROPORTIONAL, pp.flowdecomposition.RescaleMode.ACER_METHODOLOGY, pp.flowdecomposition.RescaleMode.MAX_CURRENT_OVERLOAD], 'dc_fallback_enabled_after_ac_divergence': [True, False], 'sensitivity_variable_batch_size' : [100, 1000, 5000, 15000] } diff --git a/tests/test_java_perunit.py b/tests/test_java_perunit.py index 35e55f9079..9af6120ca2 100644 --- a/tests/test_java_perunit.py +++ b/tests/test_java_perunit.py @@ -73,7 +73,7 @@ def test_generator_per_unit(): 'target_v', 'target_q', 'voltage_regulator_on', 'regulated_element_id', 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], - data=[['GEN', '', 'OTHER', 6.07, -100, 49.99, -100, 100, None, 'MIN_MAX', 1.02, 3.01, True, 'GEN', -3.03, + data=[['GEN', '', 'OTHER', 6.07, -100, 49.99, -100, 100, nan, 'MIN_MAX', 1.02, 3.01, True, 'GEN', -3.03, -1.12641, 3.16461, 'VLGEN', 'VLGEN_0', True], ['GEN2', '', 'OTHER', 6.07, -100, 49.99, -1.79769e+306, 1.79769e+306, None, 'MIN_MAX', 1.02, 3.01, True, 'GEN2', -3.03, -1.13, 3.16, 'VLGEN', 'VLGEN_0', True]]) @@ -89,7 +89,7 @@ def test_generator_per_unit(): 'target_v', 'target_q', 'voltage_regulator_on', 'regulated_element_id', 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], - data=[['GEN', '', 'OTHER', 6.08, -100, 49.99, -100, 100, None, 'MIN_MAX', 1.1, 3.02, False, 'GEN', -3.03, + data=[['GEN', '', 'OTHER', 6.08, -100, 49.99, -100, 100, nan, 'MIN_MAX', 1.1, 3.02, False, 'GEN', -3.03, -1.12641, nan, 'VLGEN', '', False], ['GEN2', '', 'OTHER', 6.07, -100, 49.99, -1.79769e+306, 1.79769e+306, None, 'MIN_MAX', 1.02, 3.01, True, 'GEN2', -3.03, -1.13, 3.16, 'VLGEN', 'VLGEN_0', True]]) @@ -461,12 +461,12 @@ def test_ratio_tap_changer_steps_per_unit(): n = pp.network.create_eurostag_tutorial_example1_network() n.per_unit = True steps = n.get_ratio_tap_changer_steps() - assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(2.15, rel=1e-1) - assert steps.loc['NHV2_NLOAD', 1]['rho'] == pytest.approx(2.54, rel=1e-1) - assert steps.loc['NHV2_NLOAD', 2]['rho'] == pytest.approx(2.92, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(0.850566, rel=1e-6) + assert steps.loc['NHV2_NLOAD', 1]['rho'] == pytest.approx(1.000666, rel=1e-6) + assert steps.loc['NHV2_NLOAD', 2]['rho'] == pytest.approx(1.150766, rel=1e-6) n.update_ratio_tap_changer_steps(id='NHV2_NLOAD', position=0, r=1, x=3, g=4, b=2) steps = n.get_ratio_tap_changer_steps() - assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(2.15, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(0.850566, rel=1e-1) assert steps.loc['NHV2_NLOAD', 0]['r'] == pytest.approx(1, rel=1e-1) assert steps.loc['NHV2_NLOAD', 0]['x'] == pytest.approx(3, rel=1e-1) assert steps.loc['NHV2_NLOAD', 0]['g'] == pytest.approx(4, rel=1e-1) diff --git a/tests/test_loadflow.py b/tests/test_loadflow.py index 2e6e2bbf6e..d7b1f53003 100644 --- a/tests/test_loadflow.py +++ b/tests/test_loadflow.py @@ -6,6 +6,7 @@ # import pathlib import json +import re import pypowsybl as pp import pypowsybl.loadflow as lf @@ -274,11 +275,14 @@ def test_get_provider_parameters_names(): 'voltageTargetPriorities', 'transformerVoltageControlUseInitialTapPosition', 'generatorVoltageControlMinNominalVoltage', - 'fictitiousGeneratorVoltageControlCheckMode'] + 'fictitiousGeneratorVoltageControlCheckMode', + 'areaInterchangeControl', + 'areaInterchangeControlAreaType', + 'areaInterchangePMaxMismatch'] def test_get_provider_parameters(): specific_parameters = pp.loadflow.get_provider_parameters('OpenLoadFlow') - assert 68 == len(specific_parameters) + assert 71 == len(specific_parameters) assert 'Slack bus selection mode' == specific_parameters['description']['slackBusSelectionMode'] assert 'STRING' == specific_parameters['type']['slackBusSelectionMode'] assert 'MOST_MESHED' == specific_parameters['default']['slackBusSelectionMode'] @@ -312,21 +316,22 @@ def test_run_lf_with_report(): assert len(report3) > len(report2) def test_run_lf_with_deprecated_reporter(): - n = pp.network.create_ieee14() - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - pp.loadflow.run_ac(n, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - json_report = report_node.to_json() - assert len(json_report) > 0 - json.loads(json_report) - - n2 = pp.network.create_eurostag_tutorial_example1_network() - pp.loadflow.run_ac(n2, reporter=report_node) - report3 = str(report_node) - assert len(report3) > len(report2) + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + n = pp.network.create_ieee14() + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 + pp.loadflow.run_ac(n, reporter=report_node) + report2 = str(report_node) + assert len(report2) > len(report1) + json_report = report_node.to_json() + assert len(json_report) > 0 + json.loads(json_report) + + n2 = pp.network.create_eurostag_tutorial_example1_network() + pp.loadflow.run_ac(n2, reporter=report_node) + report3 = str(report_node) + assert len(report3) > len(report2) def test_result_status_as_bool(): @@ -340,3 +345,6 @@ def test_wrong_regulated_bus_id(): pp.loadflow.run_ac(net) parameters = lf.ValidationParameters() validation = pp.loadflow.run_validation(net, validation_parameters=parameters) + +def test(): + pass \ No newline at end of file diff --git a/tests/test_network.py b/tests/test_network.py index 97cb24fcab..74a56861be 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -6,6 +6,7 @@ # import copy import datetime +import math import os import pathlib import re @@ -61,6 +62,11 @@ def test_load_cgmes_two_zip(): assert 3 == len(n.get_substations()) +def test_load_post_processor(): + assert ['loadflowResultsCompletion', 'odreGeoDataImporter', 'replaceTieLinesByLines'] == pp.network.get_import_post_processors() + pp.network.load(DATA_DIR.joinpath('CGMES_Full.zip'), post_processors=['replaceTieLinesByLines']) + + def test_save_cgmes_zip(): n = pp.network.create_eurostag_tutorial_example1_network() buffer = n.save_to_binary_buffer(format='CGMES') @@ -151,6 +157,11 @@ def test_get_import_format(): assert ['BIIDM', 'CGMES', 'IEEE-CDF', 'JIIDM', 'MATPOWER', 'POWER-FACTORY', 'PSS/E', 'UCTE', 'XIIDM'] == formats +def test_get_import_supported_extensions(): + extensions = pp.network.get_import_supported_extensions() + assert ['RAW', 'RAWX', 'UCT', 'biidm', 'bin', 'dgs', 'iidm', 'jiidm', 'json', 'mat', 'raw', 'rawx', 'txt', 'uct', 'xiidm', 'xml'] == extensions + + def test_get_import_parameters(): parameters = pp.network.get_import_parameters('PSS/E') assert 1 == len(parameters) @@ -273,6 +284,16 @@ def test_loads_data_frame(): data=[['S1VL1_2', 2], ['S1VL2_13', 13], ['S1VL2_15', 15], ['S1VL2_17', 17], ['S3VL1_4', 4], ['S4VL1_2', 2]]) pd.testing.assert_frame_equal(expected, loads, check_dtype=False, atol=1e-2) +def test_grounds(): + n = pp.network.create_eurostag_tutorial_example1_network() + grounds = n.get_grounds(all_attributes=True) + assert grounds.empty + + n.create_grounds(id='GROUND', voltage_level_id='VLHV1', bus_id='NHV1') + expected = pd.DataFrame(index=pd.Series(name='id', data=['GROUND']), + columns=['name', 'voltage_level_id', 'bus_id', 'connected'], + data=[['', 'VLHV1', 'VLHV1_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_grounds(), check_dtype=False, atol=1e-2) def test_batteries_data_frame(): n = pp.network.load(str(TEST_DIR.joinpath('battery.xiidm'))) @@ -595,6 +616,171 @@ def test_regulated_terminal_bus_breaker(): assert 'LOAD' == generators['regulated_element_id']['GEN'] +def test_areas_data_frame(): + n = pp.network.create_eurostag_tutorial_example1_with_tie_lines_and_areas() + areas = n.get_areas(all_attributes=True) + assert 3 == len(areas) + assert not areas['fictitious']['ControlArea_A'] + areas = n.get_areas() + expected = pd.DataFrame( + index=pd.Series(name='id', data=['ControlArea_A', 'ControlArea_B', 'Region_AB']), + columns=['name', 'area_type', 'interchange_target', 'interchange', 'ac_interchange', 'dc_interchange'], + data=[['Control Area A', 'ControlArea', -602.6, -602.88, -602.88, 0.0], + ['Control Area B', 'ControlArea', +602.6, +602.88, +602.88, 0.0], + ['Region AB', 'Region', nan, 0.0, 0.0, 0.0]]) + pd.testing.assert_frame_equal(expected, areas, check_dtype=False, atol=1e-2) + + n.update_areas(id='ControlArea_A', name='Awesome Control Area A', interchange_target=-400) + areas = n.get_areas() + assert -400 == areas.loc['ControlArea_A']['interchange_target'] + assert 'Awesome Control Area A' == areas.loc['ControlArea_A']['name'] + n.update_areas(id=['ControlArea_A', 'ControlArea_B'], interchange_target=[-500, 500]) + areas = n.get_areas() + assert -500 == areas.loc['ControlArea_A']['interchange_target'] + assert +500 == areas.loc['ControlArea_B']['interchange_target'] + + n.create_areas(id='testArea', area_type='testAreaType') + areas = n.get_areas() + assert 4 == len(areas) + assert 'testAreaType' == areas.loc['testArea']['area_type'] + assert np.isnan(areas.loc['testArea']['interchange_target']) + + n.create_areas(id=['testAreaA', 'testAreaB'], + area_type=['testAreaType', 'testAreaType'], + interchange_target=[10., nan]) + areas = n.get_areas() + assert 6 == len(areas) + assert 10. == areas.loc['testAreaA']['interchange_target'] + assert np.isnan(areas.loc['testAreaB']['interchange_target']) + + n.remove_elements(['testArea', 'testAreaA', 'testAreaB']) + areas = n.get_areas() + assert 3 == len(areas) + + +def test_areas_voltage_levels_data_frame(): + n = pp.network.create_eurostag_tutorial_example1_with_tie_lines_and_areas() + areas_voltage_levels = n.get_areas_voltage_levels().sort_values(by=['id', 'voltage_level_id']) + expected = pd.DataFrame( + index=pd.Series(name='id', data=[ + 'ControlArea_A', 'ControlArea_A', + 'ControlArea_B', 'ControlArea_B', + 'Region_AB', 'Region_AB', 'Region_AB', 'Region_AB']), + columns=['voltage_level_id'], + data=[['VLGEN'], ['VLHV1'], + ['VLHV2'], ['VLLOAD'], + ['VLGEN'], ['VLHV1'], ['VLHV2'], ['VLLOAD']]) + pd.testing.assert_frame_equal(expected, areas_voltage_levels, check_dtype=False) + + # test adding boundaries to areas + n = pp.network.create_eurostag_tutorial_example1_network() + n.create_areas(id=['testAreaA', 'testAreaB'], + area_type=['testAreaType', 'testAreaType'], + interchange_target=[10., nan]) + n.create_areas_voltage_levels(id=['testAreaA', 'testAreaA', 'testAreaB'], + voltage_level_id=['VLGEN', 'VLHV1', 'VLLOAD']) + areas_voltage_levels = n.get_areas_voltage_levels().sort_values(by=['id', 'voltage_level_id']) + expected = pd.DataFrame( + index=pd.Series(name='id', data=[ + 'testAreaA', 'testAreaA', + 'testAreaB']), + columns=['voltage_level_id'], + data=[['VLGEN'], ['VLHV1'], + ['VLLOAD']]) + pd.testing.assert_frame_equal(expected, areas_voltage_levels, check_dtype=False) + + # test removal + n.create_areas_voltage_levels(id=['testAreaA'], voltage_level_id=['']) + areas_voltage_levels = n.get_areas_voltage_levels().sort_values(by=['id', 'voltage_level_id']) + expected = pd.DataFrame( + index=pd.Series(name='id', data=['testAreaB']), + columns=['voltage_level_id'], + data=[['VLLOAD']]) + pd.testing.assert_frame_equal(expected, areas_voltage_levels, check_dtype=False) + + +def test_areas_boundaries_data_frame(): + n = pp.network.create_eurostag_tutorial_example1_with_tie_lines_and_areas() + areas_boundaries = n.get_areas_boundaries(all_attributes=True).sort_values(by=['id', 'element']) + expected = pd.DataFrame( + index=pd.Series(name='id', data=[ + 'ControlArea_A', 'ControlArea_A', + 'ControlArea_B', 'ControlArea_B']), + columns=['boundary_type', 'element', 'side', 'ac', 'p', 'q'], + data=[['DANGLING_LINE', 'NHV1_XNODE1', '', True, -301.44, -116.55], + ['DANGLING_LINE', 'NHV1_XNODE2', '', True, -301.44, -116.55], + ['DANGLING_LINE', 'XNODE1_NHV2', '', True, +301.44, +116.55], + ['DANGLING_LINE', 'XNODE2_NHV2', '', True, +301.44, +116.55]]) + pd.testing.assert_frame_equal(expected, areas_boundaries, check_dtype=False, atol=1e-2) + + # test adding boundaries to area + n = pp.network.create_eurostag_tutorial_example1_with_tie_lines_and_areas() + n.remove_elements(elements_ids=['ControlArea_A', 'ControlArea_B']) + n.create_areas(id=['testAreaA', 'testAreaB'], + area_type=['testAreaType', 'testAreaType'], + interchange_target=[10., nan]) + n.create_areas_boundaries(id=['testAreaA', 'testAreaA', 'testAreaB', 'testAreaB'], + boundary_type=['DANGLING_LINE', 'DANGLING_LINE', 'DANGLING_LINE', 'DANGLING_LINE'], + element=['NHV1_XNODE1', 'NHV1_XNODE2', 'XNODE1_NHV2', 'XNODE2_NHV2'], + ac=[True, True, True, True]) + areas_boundaries = n.get_areas_boundaries(all_attributes=True).sort_values(by=['id', 'element']) + expected = pd.DataFrame( + index=pd.Series(name='id', data=[ + 'testAreaA', 'testAreaA', + 'testAreaB', 'testAreaB']), + columns=['boundary_type', 'element', 'side', 'ac', 'p', 'q'], + data=[['DANGLING_LINE', 'NHV1_XNODE1', '', True, -301.44, -116.55], + ['DANGLING_LINE', 'NHV1_XNODE2', '', True, -301.44, -116.55], + ['DANGLING_LINE', 'XNODE1_NHV2', '', True, +301.44, +116.55], + ['DANGLING_LINE', 'XNODE2_NHV2', '', True, +301.44, +116.55]]) + pd.testing.assert_frame_equal(expected, areas_boundaries, check_dtype=False, atol=1e-2) + + # test removal + n.create_areas_boundaries(id=['testAreaA'], + boundary_type=[''], + element=['']) + areas_boundaries = n.get_areas_boundaries(all_attributes=True).sort_values(by=['id', 'element']) + expected = pd.DataFrame( + index=pd.Series(name='id', data=[ + 'testAreaB', 'testAreaB']), + columns=['boundary_type', 'element', 'side', 'ac', 'p', 'q'], + data=[['DANGLING_LINE', 'XNODE1_NHV2', '', True, +301.44, +116.55], + ['DANGLING_LINE', 'XNODE2_NHV2', '', True, +301.44, +116.55]]) + pd.testing.assert_frame_equal(expected, areas_boundaries, check_dtype=False, atol=1e-2) + + # test using terminals instead of dangling lines, e.g. boundary located at NGEN_NHV1 HV side + n = pp.network.create_eurostag_tutorial_example1_network() + n.create_areas(id=['testAreaA', 'testAreaB'], + area_type=['testAreaType', 'testAreaType'], + interchange_target=[10., nan]) + n.update_2_windings_transformers(id='NGEN_NHV1', p2=-600, q2=-50) + n.update_lines(id=['NHV1_NHV2_1', 'NHV1_NHV2_2'], p1=[300, 300], q1=[25, 25]) + n.create_areas_boundaries(id=['testAreaA', 'testAreaB', 'testAreaB'], + boundary_type=['TERMINAL', 'TERMINAL', 'TERMINAL'], + element=['NGEN_NHV1', 'NHV1_NHV2_1', 'NHV1_NHV2_2'], + side=['TWO', 'ONE', 'ONE'], + ac=[True, True, True]) + areas_boundaries = n.get_areas_boundaries(all_attributes=True).sort_values(by=['id', 'element']) + expected = pd.DataFrame( + index=pd.Series(name='id', data=['testAreaA', 'testAreaB', 'testAreaB']), + columns=['boundary_type', 'element', 'side', 'ac', 'p', 'q'], + data=[['TERMINAL', 'NGEN_NHV1', 'TWO', True, -600, -50], + ['TERMINAL', 'NHV1_NHV2_1', 'ONE', True, 300, 25], + ['TERMINAL', 'NHV1_NHV2_2', 'ONE', True, 300, 25]]) + pd.testing.assert_frame_equal(expected, areas_boundaries, check_dtype=False, atol=1e-2) + + # test removal + n.create_areas_boundaries(id=['testAreaA'], + element=['']) + areas_boundaries = n.get_areas_boundaries(all_attributes=True).sort_values(by=['id', 'element']) + expected = pd.DataFrame( + index=pd.Series(name='id', data=['testAreaB', 'testAreaB']), + columns=['boundary_type', 'element', 'side', 'ac', 'p', 'q'], + data=[['TERMINAL', 'NHV1_NHV2_1', 'ONE', True, 300, 25], + ['TERMINAL', 'NHV1_NHV2_2', 'ONE', True, 300, 25]]) + pd.testing.assert_frame_equal(expected, areas_boundaries, check_dtype=False, atol=1e-2) + + def test_update_unknown_data(): n = pp.network.create_eurostag_tutorial_example1_network() update = pd.DataFrame(data=[['blob']], columns=['unknown'], index=['GEN']) @@ -807,19 +993,21 @@ def test_sld_parameters(): def test_layout_parameters(): - parameters = LayoutParameters() - assert not parameters.use_name - assert not parameters.center_name - assert not parameters.diagonal_label - assert parameters.topological_coloring - assert not parameters.nodes_infos - parameters = LayoutParameters(use_name=True, center_name=True, diagonal_label=True, topological_coloring=False, - nodes_infos=True) - assert parameters.use_name - assert parameters.center_name - assert parameters.diagonal_label - assert not parameters.topological_coloring - assert parameters.nodes_infos + with pytest.warns(DeprecationWarning, match=re.escape("LayoutParameters is deprecated, use SldParameters instead")): + parameters = LayoutParameters() + assert not parameters.use_name + assert not parameters.center_name + assert not parameters.diagonal_label + assert parameters.topological_coloring + assert not parameters.nodes_infos + with pytest.warns(DeprecationWarning, match=re.escape("LayoutParameters is deprecated, use SldParameters instead")): + parameters = LayoutParameters(use_name=True, center_name=True, diagonal_label=True, topological_coloring=False, + nodes_infos=True) + assert parameters.use_name + assert parameters.center_name + assert parameters.diagonal_label + assert not parameters.topological_coloring + assert parameters.nodes_infos def test_sld_svg(): @@ -869,20 +1057,23 @@ def test_sld_svg(): def test_sld_svg_backward_compatibility(): n = pp.network.create_four_substations_node_breaker_network() - sld = n.get_single_line_diagram('S1VL1', LayoutParameters(use_name=True, center_name=True, diagonal_label=True, - topological_coloring=False)) - assert re.search('.* 0 - sld1 = n.get_single_line_diagram('S1VL1', LayoutParameters(use_name=True, center_name=True, diagonal_label=True, - topological_coloring=True, nodes_infos=True)) - assert re.search('.* 0 + with pytest.warns(DeprecationWarning, match=re.escape("LayoutParameters is deprecated, use SldParameters instead")): + sld = n.get_single_line_diagram('S1VL1', LayoutParameters(use_name=True, center_name=True, diagonal_label=True, + topological_coloring=False)) + assert re.search('.* 0 + with pytest.warns(DeprecationWarning, match=re.escape("LayoutParameters is deprecated, use SldParameters instead")): + sld1 = n.get_single_line_diagram('S1VL1', LayoutParameters(use_name=True, center_name=True, diagonal_label=True, + topological_coloring=True, nodes_infos=True)) + assert re.search('.* 0 def test_nad(): n = pp.network.create_ieee14() nad = n.get_network_area_diagram() assert re.search('.* 0 nad = n.get_network_area_diagram(voltage_level_ids=None) assert re.search('.* 0 - pp.network.load(str(DATA_DIR.joinpath('ieee14.dgs')), reporter=reporter) - report2 = str(reporter) + pp.network.load(str(DATA_DIR.joinpath('ieee14.dgs')), report_node=report_node) + report2 = str(report_node) assert len(report2) >= len(report1) @@ -1932,22 +2206,22 @@ def test_load_network_from_string_with_report(): ##ZBE BBE1AA1 0 2 400.00 3000.00 0.00000 -1500.0 0.00000 0.00000 -9000.0 9000.00 -9000.0 F """ - reporter = rp.ReportNode() - report1 = str(reporter) + report_node = rp.ReportNode() + report1 = str(report_node) assert len(report1) > 0 - pp.network.load_from_string('simple-eu.uct', file_content, reporter=reporter) - report2 = str(reporter) + pp.network.load_from_string('simple-eu.uct', file_content, report_node=report_node) + report2 = str(report_node) assert len(report2) > len(report1) def test_save_to_string_with_report(): bat_path = TEST_DIR.joinpath('battery.xiidm') - reporter = rp.ReportNode() - report1 = str(reporter) + report_node = rp.ReportNode() + report1 = str(report_node) assert len(report1) > 0 bat_path.read_text() - pp.network.load(str(bat_path), reporter=reporter) - report2 = str(reporter) + pp.network.load(str(bat_path), report_node=report_node) + report2 = str(report_node) assert len(report2) >= len(report1) @@ -2137,6 +2411,7 @@ def test_nad_parameters(): assert nad_parameters.radius_factor == 120.0 assert nad_parameters.edge_info_displayed == EdgeInfoType.CURRENT + def test_update_dangling_line(): network = pp.network.create_eurostag_tutorial_example1_network() network.create_dangling_lines(id='dangling_line', voltage_level_id='VLGEN', bus_id='NGEN', p0=100, q0=100, r=0, x=0, g=0, b=0) @@ -2144,5 +2419,84 @@ def test_update_dangling_line(): assert network.get_dangling_lines().loc['dangling_line'].pairing_key == 'XNODE' +def test_update_name(): + n = pp.network.create_eurostag_tutorial_example1_network() + generators = n.get_generators(attributes=['name']) + assert '' == generators.loc['GEN', 'name'] + n.update_generators(id='GEN', name='GEN_NAME') + generators = n.get_generators(attributes=['name']) + assert 'GEN_NAME' == generators.loc['GEN', 'name'] + + +def test_deprecated_operational_limits_is_fictitious(): + network = pp.network.create_eurostag_tutorial_example1_network() + with pytest.warns(DeprecationWarning, match=re.escape("operation limits is_fictitious attribute has been renamed fictitious")): + network.create_operational_limits(pd.DataFrame.from_records(index='element_id', data=[ + {'element_id': 'NHV1_NHV2_1', + 'name': '', + 'side': 'ONE', + 'type': 'CURRENT', + 'value': 400.0, + 'acceptable_duration': -1, + 'is_fictitious': False}, + {'element_id': 'NHV1_NHV2_1', + 'name': '60s', + 'side': 'ONE', + 'type': 'CURRENT', + 'value': 500.0, + 'acceptable_duration': 60, + 'is_fictitious': True}, + ])) + limits = network.get_operational_limits(all_attributes=True) + assert limits.query("element_id == 'NHV1_NHV2_1' and side == 'ONE' and acceptable_duration == 60")['fictitious'].all() + + +def test_deprecated_operational_limits_is_fictitious_kwargs(): + network = pp.network.create_eurostag_tutorial_example1_network() + with pytest.warns(DeprecationWarning, match=re.escape("operation limits is_fictitious attribute has been renamed fictitious")): + network.create_operational_limits(element_id=['NHV1_NHV2_1', 'NHV1_NHV2_1'], + name=['', ''], + side=['ONE', 'ONE'], type=['CURRENT', 'CURRENT'], value=[400.0, 500.0], + acceptable_duration=[-1, 60], is_fictitious=[False, True]) + limits = network.get_operational_limits(all_attributes=True) + assert limits.query("element_id == 'NHV1_NHV2_1' and side == 'ONE' and acceptable_duration == 60")['fictitious'].all() + + +def test_deprecated_operational_limits_element_type(): + # element type should just be ignored and not throw an exception + network = pp.network.create_eurostag_tutorial_example1_network() + with pytest.warns(DeprecationWarning, match=re.escape("useless operation limits element_type attribute has been removed")): + network.create_operational_limits(pd.DataFrame.from_records(index='element_id', data=[ + {'element_id': 'NHV1_NHV2_1', + 'element_type': 'LINE', + 'name': '', + 'side': 'ONE', + 'type': 'CURRENT', + 'value': 400.0, + 'acceptable_duration': -1, + 'fictitious': False}, + ])) + + +def test_deprecated_operational_limits_element_type_kwargs(): + # element type should just be ignored and not throw an exception + network = pp.network.create_eurostag_tutorial_example1_network() + with pytest.warns(DeprecationWarning, match=re.escape("useless operation limits element_type attribute has been removed")): + network.create_operational_limits(element_id=['NHV1_NHV2_1', 'NHV1_NHV2_1'], element_type=['LINE', 'LINE'], name=['', ''], + side=['ONE', 'ONE'], type=['CURRENT', 'CURRENT'], value=[400.0, 500.0], + acceptable_duration=[-1, 60], fictitious=[False, True]) + + +def test_connect_disconnect_with_empty_bus(): + network = pp.network.create_eurostag_tutorial_example1_with_more_generators_network() + network.update_generators(id=['GEN', 'GEN2'], connected=[False, True], bus_breaker_bus_id=['', 'NGEN']) + generators = network.get_generators(attributes=['connected', 'bus_breaker_bus_id']) + expected_generators = pd.DataFrame(index=pd.Series(name='id', data=['GEN', 'GEN2']), + columns=['connected', 'bus_breaker_bus_id'], + data=[[False, 'NGEN'], + [True, 'NGEN']]) + pd.testing.assert_frame_equal(expected_generators, generators, check_dtype=False) + + if __name__ == '__main__': unittest.main() diff --git a/tests/test_network_elements_creation.py b/tests/test_network_elements_creation.py index dd43d90ccc..8a84984895 100644 --- a/tests/test_network_elements_creation.py +++ b/tests/test_network_elements_creation.py @@ -9,6 +9,7 @@ import numpy as np import pandas as pd import pytest +import re import pypowsybl import pypowsybl.network @@ -629,36 +630,36 @@ def test_create_node_breaker_network_and_run_loadflow(): def test_create_limits(): net = pn.create_eurostag_tutorial_example1_network() net.create_operational_limits(pd.DataFrame.from_records(index='element_id', data=[ - {'element_id': 'NHV1_NHV2_1', 'name': 'permanent_limit', 'element_type': 'LINE', 'side': 'ONE', + {'element_id': 'NHV1_NHV2_1', 'name': 'permanent_limit', 'side': 'ONE', 'type': 'APPARENT_POWER', 'value': 600, - 'acceptable_duration': np.inf, 'is_fictitious': False}, - {'element_id': 'NHV1_NHV2_1', 'name': '1\'', 'element_type': 'LINE', 'side': 'ONE', + 'acceptable_duration': np.inf, 'fictitious': False}, + {'element_id': 'NHV1_NHV2_1', 'name': '1\'', 'side': 'ONE', 'type': 'APPARENT_POWER', 'value': 1000, - 'acceptable_duration': 60, 'is_fictitious': False}, - {'element_id': 'NHV1_NHV2_1', 'name': 'permanent_limit', 'element_type': 'LINE', 'side': 'ONE', + 'acceptable_duration': 60, 'fictitious': False}, + {'element_id': 'NHV1_NHV2_1', 'name': 'permanent_limit', 'side': 'ONE', 'type': 'ACTIVE_POWER', 'value': 400, - 'acceptable_duration': np.inf, 'is_fictitious': False}, - {'element_id': 'NHV1_NHV2_1', 'name': '1\'', 'element_type': 'LINE', 'side': 'ONE', + 'acceptable_duration': np.inf, 'fictitious': False}, + {'element_id': 'NHV1_NHV2_1', 'name': '1\'', 'side': 'ONE', 'type': 'ACTIVE_POWER', 'value': 700, - 'acceptable_duration': 60, 'is_fictitious': False} + 'acceptable_duration': 60, 'fictitious': False} ])) expected = pd.DataFrame.from_records( index='element_id', - columns=['element_id', 'element_type', 'side', 'name', 'type', 'value', 'acceptable_duration', 'fictitious'], - data=[['NHV1_NHV2_1', 'LINE', 'ONE', 'permanent_limit', 'CURRENT', 500, -1, False], - ['NHV1_NHV2_1', 'LINE', 'TWO', 'permanent_limit', 'CURRENT', 1100, -1, False], - ['NHV1_NHV2_1', 'LINE', 'ONE', 'permanent_limit', 'ACTIVE_POWER', 400, -1, False], - ['NHV1_NHV2_1', 'LINE', 'ONE', 'permanent_limit', 'APPARENT_POWER', 600, -1, False]]) + columns=['element_id', 'element_type', 'side', 'name', 'type', 'value', 'acceptable_duration', 'fictitious', 'group_name', 'selected'], + data=[['NHV1_NHV2_1', 'LINE', 'ONE', 'permanent_limit', 'CURRENT', 500, -1, False, 'DEFAULT', True], + ['NHV1_NHV2_1', 'LINE', 'ONE', 'permanent_limit', 'ACTIVE_POWER', 400, -1, False, 'DEFAULT', True], + ['NHV1_NHV2_1', 'LINE', 'ONE', 'permanent_limit', 'APPARENT_POWER', 600, -1, False, 'DEFAULT', True], + ['NHV1_NHV2_1', 'LINE', 'TWO', 'permanent_limit', 'CURRENT', 1100, -1, False, 'DEFAULT', True]]) limits = net.get_operational_limits(all_attributes=True).loc['NHV1_NHV2_1'] permanent_limits = limits[limits['name'] == 'permanent_limit'] pd.testing.assert_frame_equal(expected, permanent_limits, check_dtype=False) expected = pd.DataFrame.from_records( index='element_id', - columns=['element_id', 'element_type', 'side', 'name', 'type', 'value', 'acceptable_duration', 'fictitious'], - data=[['NHV1_NHV2_1', 'LINE', 'TWO', '1\'', 'CURRENT', 1500, 60, False], - ['NHV1_NHV2_1', 'LINE', 'ONE', '1\'', 'ACTIVE_POWER', 700, 60, False], - ['NHV1_NHV2_1', 'LINE', 'ONE', '1\'', 'APPARENT_POWER', 1000, 60, False]]) + columns=['element_id', 'element_type', 'side', 'name', 'type', 'value', 'acceptable_duration', 'fictitious', 'group_name', 'selected'], + data=[['NHV1_NHV2_1', 'LINE', 'ONE', '1\'', 'ACTIVE_POWER', 700, 60, False, 'DEFAULT', True], + ['NHV1_NHV2_1', 'LINE', 'ONE', '1\'', 'APPARENT_POWER', 1000, 60, False, 'DEFAULT', True], + ['NHV1_NHV2_1', 'LINE', 'TWO', '1\'', 'CURRENT', 1500, 60, False, 'DEFAULT', True]]) one_minute_limits = limits[limits['name'] == '1\''] pd.testing.assert_frame_equal(expected, one_minute_limits, check_dtype=False) @@ -851,10 +852,11 @@ def test_tie_line_creation(): nominal_v=[225, 225], topology_kind=['BUS_BREAKER', 'BUS_BREAKER']) network.create_buses(id=['BUS_TEST', 'BUS_TEST2'], voltage_level_id=['VLTEST', 'VLTEST2']) - network.create_dangling_lines(id=['DL_TEST', 'DL_TEST2'], voltage_level_id=['VLTEST', 'VLTEST2'], - bus_id=['BUS_TEST', 'BUS_TEST2'], - p0=[100, 100], q0=[101, 101], r=[2, 2], x=[2, 2], g=[1, 1], b=[1, 1], - ucte_xnode_code=['XNODE', 'XNODE']) + with pytest.warns(DeprecationWarning, match=re.escape("ucte_xnode_code is deprecated, use pairing_key")): + network.create_dangling_lines(id=['DL_TEST', 'DL_TEST2'], voltage_level_id=['VLTEST', 'VLTEST2'], + bus_id=['BUS_TEST', 'BUS_TEST2'], + p0=[100, 100], q0=[101, 101], r=[2, 2], x=[2, 2], g=[1, 1], b=[1, 1], + ucte_xnode_code=['XNODE', 'XNODE']) df = pd.DataFrame.from_records( columns=['id', 'dangling_line1_id', 'dangling_line2_id'], data=[('TIE_LINE_TEST', 'DL_TEST', 'DL_TEST2')], @@ -913,7 +915,7 @@ def test_deprecated_ucte_xnode_code_kwargs(): nominal_v=[225, 225], topology_kind=['BUS_BREAKER', 'BUS_BREAKER']) network.create_buses(id=['BUS_TEST', 'BUS_TEST2'], voltage_level_id=['VLTEST', 'VLTEST2']) - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("ucte_xnode_code is deprecated, use pairing_key")): network.create_dangling_lines(id=['DL_TEST', 'DL_TEST2'], voltage_level_id=['VLTEST', 'VLTEST2'], bus_id=['BUS_TEST', 'BUS_TEST2'], p0=[100, 100], q0=[101, 101], r=[2, 2], x=[2, 2], g=[1, 1], b=[1, 1], @@ -931,7 +933,7 @@ def test_deprecated_ucte_xnode_code_dataframe(): nominal_v=[225, 225], topology_kind=['BUS_BREAKER', 'BUS_BREAKER']) network.create_buses(id=['BUS_TEST', 'BUS_TEST2'], voltage_level_id=['VLTEST', 'VLTEST2']) - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("ucte_xnode_code is deprecated, use pairing_key")): network.create_dangling_lines(pd.DataFrame.from_records( columns=['id', 'voltage_level_id', 'bus_id', 'p0', 'q0', 'r', 'x', 'g', 'b', 'ucte_xnode_code'], data=[('DL_TEST', 'VLTEST', 'BUS_TEST', 100, 101, 2, 2, 1, 1, 'XNODE1'), @@ -1023,21 +1025,123 @@ def test_3_windings_transformers_creation(): #Add some limits n.create_operational_limits(pd.DataFrame.from_records(index='element_id', data=[ - {'element_id': 'TWT_TEST', 'name': 'permanent_limit', 'element_type': 'THREE_WINDINGS_TRANSFORMER', + {'element_id': 'TWT_TEST', 'name': 'permanent_limit', 'side': 'ONE', 'type': 'APPARENT_POWER', 'value': 600, - 'acceptable_duration': np.inf, 'is_fictitious': False}, - {'element_id': 'TWT_TEST', 'name': '1\'', 'element_type': 'THREE_WINDINGS_TRANSFORMER', 'side': 'ONE', + 'acceptable_duration': np.inf, 'fictitious': False}, + {'element_id': 'TWT_TEST', 'name': '1\'', 'side': 'ONE', 'type': 'APPARENT_POWER', 'value': 1000, - 'acceptable_duration': 60, 'is_fictitious': False}, - {'element_id': 'TWT_TEST', 'name': 'permanent_limit', 'element_type': 'THREE_WINDINGS_TRANSFORMER', + 'acceptable_duration': 60, 'fictitious': False}, + {'element_id': 'TWT_TEST', 'name': 'permanent_limit', 'side': 'ONE', 'type': 'ACTIVE_POWER', 'value': 400, - 'acceptable_duration': np.inf, 'is_fictitious': False}, - {'element_id': 'TWT_TEST', 'name': '1\'', 'element_type': 'THREE_WINDINGS_TRANSFORMER', 'side': 'ONE', + 'acceptable_duration': np.inf, 'fictitious': False}, + {'element_id': 'TWT_TEST', 'name': '1\'', 'side': 'ONE', 'type': 'ACTIVE_POWER', 'value': 700, - 'acceptable_duration': 60, 'is_fictitious': False} + 'acceptable_duration': 60, 'fictitious': False} ])) operational_limits = n.get_operational_limits().loc['TWT_TEST'] assert operational_limits.shape[0] == 4 + +def test_internal_connections_creation_removal(): + network = pn.create_empty() + network.create_substations(id='S1') + network.create_voltage_levels(id=['VL1', 'VL2'], nominal_v=[380, 380], + topology_kind=['NODE_BREAKER', 'NODE_BREAKER']) + + busbar_sections = pd.DataFrame.from_records(index='voltage_level_id', data=[ + {'voltage_level_id': 'VL1', 'id': 'VL1BBS1', 'node': 0}, + {'voltage_level_id': 'VL1', 'id': 'VL1BBS2', 'node': 1}, + {'voltage_level_id': 'VL2', 'id': 'VL2BBS1', 'node': 0}, + {'voltage_level_id': 'VL2', 'id': 'VL2BBS2', 'node': 1}, + ]) + network.create_busbar_sections(busbar_sections) + network.create_switches(id='VL1.COUPLER', voltage_level_id='VL1', kind='BREAKER', node1=2, node2=3) + network.create_switches(id='VL2.COUPLER', voltage_level_id='VL2', kind='BREAKER', node1=2, node2=3) + + # create internal connections - dataframe + internal_connections = pd.DataFrame.from_records(index='voltage_level_id', data=[ + {'voltage_level_id': 'VL1', 'node1': 0, 'node2': 2}, + {'voltage_level_id': 'VL1', 'node1': 1, 'node2': 3}, + {'voltage_level_id': 'VL2', 'node1': 0, 'node2': 2}, + {'voltage_level_id': 'VL2', 'node1': 1, 'node2': 3}, + ]) + network.create_internal_connections(internal_connections) + expected_icn_initial = pd.DataFrame( + index=pd.Series(name='id', data=[0, 1]), + columns=['node1', 'node2'], + data=[[0, 2], + [1, 3]]) + pd.testing.assert_frame_equal(expected_icn_initial, + network.get_node_breaker_topology('VL1').internal_connections, + check_dtype=False, check_index_type=False) + pd.testing.assert_frame_equal(expected_icn_initial, + network.get_node_breaker_topology('VL2').internal_connections, + check_dtype=False, check_index_type=False) + + # remove internal connections - dataframe + vl1_remove = pd.DataFrame.from_records(index='voltage_level_id', data=[ + {'voltage_level_id': 'VL1', 'node1': 3, 'node2': 1}, # removal is not oriented + ]) + network.remove_internal_connections(vl1_remove) + expected_icn_vl1_after_removal = pd.DataFrame( + index=pd.Series(name='id', data=[0]), + columns=['node1', 'node2'], + data=[[0, 2]]) + pd.testing.assert_frame_equal(expected_icn_vl1_after_removal, + network.get_node_breaker_topology('VL1').internal_connections, + check_dtype=False, + check_index_type=False) + # VL2 unaffected + pd.testing.assert_frame_equal(expected_icn_initial, + network.get_node_breaker_topology('VL2').internal_connections, + check_dtype=False, + check_index_type=False) + + # create internal connections - kwargs + network.create_internal_connections(voltage_level_id='VL2', node1=5, node2=6) + expected_icn_vl2 = pd.DataFrame( + index=pd.Series(name='id', data=[0, 1, 2]), + columns=['node1', 'node2'], + data=[[0, 2], + [1, 3], + [5, 6]]) + pd.testing.assert_frame_equal(expected_icn_vl2, + network.get_node_breaker_topology('VL2').internal_connections, + check_dtype=False, + check_index_type=False) + + # remove internal connections - kwargs + network.remove_internal_connections(voltage_level_id=['VL2', 'VL2'], node1=[0, 5], node2=[2, 6]) + expected_icn_vl2_after_removal = pd.DataFrame( + index=pd.Series(name='id', data=[0]), + columns=['node1', 'node2'], + data=[[1, 3]]) + pd.testing.assert_frame_equal(expected_icn_vl1_after_removal, + network.get_node_breaker_topology('VL1').internal_connections, # unchanged + check_dtype=False, + check_index_type=False) + pd.testing.assert_frame_equal(expected_icn_vl2_after_removal, + network.get_node_breaker_topology('VL2').internal_connections, + check_dtype=False, + check_index_type=False) + + +def test_internal_connections_errors(): + network = pypowsybl.network.create_eurostag_tutorial_example1_network() + with pytest.raises(PyPowsyblError) as exc: + network.create_internal_connections(voltage_level_id='wrongVL', node1=0, node2=1) + assert "Voltage level \'wrongVL\' does not exist." in str(exc) + + with pytest.raises(PyPowsyblError) as exc: + network.remove_internal_connections(voltage_level_id='wrongVL', node1=0, node2=1) + assert "Voltage level \'wrongVL\' does not exist." in str(exc) + + with pytest.raises(PyPowsyblError) as exc: + network.create_internal_connections(voltage_level_id='VLGEN', node1=0, node2=1) + assert "Voltage level \'VLGEN\' is not of Node/Breaker topology kind." in str(exc) + + with pytest.raises(PyPowsyblError) as exc: + network.remove_internal_connections(voltage_level_id='VLGEN', node1=0, node2=1) + assert "Voltage level \'VLGEN\' is not of Node/Breaker topology kind." in str(exc) diff --git a/tests/test_network_extensions.py b/tests/test_network_extensions.py index 1b8e2f933a..0290413835 100644 --- a/tests/test_network_extensions.py +++ b/tests/test_network_extensions.py @@ -25,23 +25,33 @@ def test_extensions(): assert len(generators_extensions) == 1 assert generators_extensions['participate']['GEN'] assert generators_extensions['droop']['GEN'] == pytest.approx(1.1, abs=1e-3) + assert np.isnan(generators_extensions['participation_factor']['GEN']) + assert np.isnan(generators_extensions['max_target_p']['GEN']) + assert np.isnan(generators_extensions['min_target_p']['GEN']) assert n.get_extensions('hvdcOperatorActivePowerRange').empty def test_update_extensions(): n = pn._create_network('eurostag_tutorial_example1_with_apc_extension') n.update_extensions('activePowerControl', pd.DataFrame.from_records(index='id', data=[ - {'id': 'GEN', 'droop': 1.2} + {'id': 'GEN', 'droop': 1.2, 'participation_factor': 1.5, 'max_target_p': 900., 'min_target_p': 200.} ])) generators_extensions = n.get_extensions('activePowerControl') assert len(generators_extensions) == 1 assert generators_extensions['participate']['GEN'] assert generators_extensions['droop']['GEN'] == pytest.approx(1.2, abs=1e-3) - n.update_extensions('activePowerControl', id='GEN', droop=1.4) + assert generators_extensions['participation_factor']['GEN'] == pytest.approx(1.5, abs=1e-3) + assert generators_extensions['max_target_p']['GEN'] == pytest.approx(900., abs=1e-3) + assert generators_extensions['min_target_p']['GEN'] == pytest.approx(200., abs=1e-3) + n.update_extensions('activePowerControl', + id='GEN', droop=1.4, participation_factor=1.8, max_target_p=800., min_target_p=150.) generators_extensions = n.get_extensions('activePowerControl') assert len(generators_extensions) == 1 assert generators_extensions['participate']['GEN'] assert generators_extensions['droop']['GEN'] == pytest.approx(1.4, abs=1e-3) + assert generators_extensions['participation_factor']['GEN'] == pytest.approx(1.8, abs=1e-3) + assert generators_extensions['max_target_p']['GEN'] == pytest.approx(800., abs=1e-3) + assert generators_extensions['min_target_p']['GEN'] == pytest.approx(150., abs=1e-3) def test_remove_extensions(): @@ -55,18 +65,25 @@ def test_remove_extensions(): def test_create_extensions(): n = pn._create_network('eurostag_tutorial_example1') n.create_extensions('activePowerControl', pd.DataFrame.from_records(index='id', data=[ - {'id': 'GEN', 'droop': 1.2, 'participate': True} + {'id': 'GEN', 'droop': 1.2, 'participate': True, 'participation_factor': 1.5, + 'max_target_p': 900., 'min_target_p': 200.} ])) generators_extensions = n.get_extensions('activePowerControl') assert len(generators_extensions) == 1 assert generators_extensions['participate']['GEN'] assert generators_extensions['droop']['GEN'] == pytest.approx(1.2, abs=1e-3) + assert generators_extensions['participation_factor']['GEN'] == pytest.approx(1.5, abs=1e-3) + assert generators_extensions['max_target_p']['GEN'] == pytest.approx(900., abs=1e-3) + assert generators_extensions['min_target_p']['GEN'] == pytest.approx(200., abs=1e-3) n.create_extensions('activePowerControl', id='GEN2', droop=1.3, participate=False) generators_extensions = n.get_extensions('activePowerControl') assert len(generators_extensions) == 2 assert not generators_extensions['participate']['GEN2'] assert generators_extensions['droop']['GEN2'] == pytest.approx(1.3, abs=1e-3) + assert np.isnan(generators_extensions['participation_factor']['GEN2']) + assert np.isnan(generators_extensions['max_target_p']['GEN2']) + assert np.isnan(generators_extensions['min_target_p']['GEN2']) def test_entsoe_area(): @@ -454,8 +471,68 @@ def test_geo_data(): pd.testing.assert_frame_equal(n.get_extensions('linePosition'), line_expected) +def test_cgmes_metadata_extension(): + n = pn.create_eurostag_tutorial_example1_network() + extension_name = 'cgmesMetadataModels' + cgmes_id = 'sshId' + metadata = pd.DataFrame.from_records(index='id', + data=[('sshId', 'STEADY_STATE_HYPOTHESIS', 'SSH description', 1, + 'http://powsybl.org', 'steady-state-hypothesis', + 'ssh-dependency1/ssh-dependency2', '')], + columns=['id', 'cgmes_subset', 'description', 'version', + 'modeling_authority_set', 'profiles', 'dependent_on', 'supersedes']) + + n.create_extensions(extension_name, [metadata]) + + # force num column dtype to be int32 like the one generated by pypowsybl and not int64 + # as a consequence index has to be created after + metadata.reset_index(inplace=True) + metadata = metadata.astype({'id': str, 'cgmes_subset' : str, 'description' : str, 'version' : np.int32, 'modeling_authority_set' : str, + 'profiles' : str, 'dependent_on' : str, 'supersedes' : str}) + metadata.set_index(['id'], inplace=True) + + #test get_extensions + pd.testing.assert_frame_equal(n.get_extensions(extension_name), metadata) + + +def test_reference_priorities(): + network = pn.create_eurostag_tutorial_example1_network() + assert network.get_extensions('referencePriorities').empty + + network.create_extensions('referencePriorities', id='GEN', priority=1) + e = network.get_extensions('referencePriorities') + expected = pd.DataFrame( + index=pd.Series(name='id', data=['GEN']), + columns=['priority'], + data=[[1]]) + pd.testing.assert_frame_equal(expected, e, check_dtype=False) + + network.create_extensions('referencePriorities', id='LOAD', priority=2) + e = network.get_extensions('referencePriorities') + expected = pd.DataFrame( + index=pd.Series(name='id', data=['GEN', 'LOAD']), + columns=['priority'], + data=[[1], [2]]) + pd.testing.assert_frame_equal(expected, e, check_dtype=False) + + network.update_extensions('referencePriorities', id=['GEN', 'LOAD'], priority=[3, 4]) + e = network.get_extensions('referencePriorities') + expected = pd.DataFrame( + index=pd.Series(name='id', data=['GEN', 'LOAD']), + columns=['priority'], + data=[[3], [4]]) + pd.testing.assert_frame_equal(expected, e, check_dtype=False) + + network.remove_extensions('referencePriorities', ['GEN', 'LOAD']) + assert network.get_extensions('referencePriorities').empty + + def test_get_extensions_information(): extensions_information = pypowsybl.network.get_extensions_information() + assert extensions_information.loc['cgmesMetadataModels']['detail'] == 'Provides information about CGMES metadata models' + assert extensions_information.loc['cgmesMetadataModels']['attributes'] == ('index : id (str), cgmes_subset (str), description (str), ' \ + 'version (int), modeling_authority_set (str), profiles (str), ' \ + 'dependent_on (str), supersedes (str) ') assert extensions_information.loc['measurements']['detail'] == 'Provides measurement about a specific equipment' assert extensions_information.loc['measurements']['attributes'] == 'index : element_id (str),id (str), type (str), ' \ 'standard_deviation (float), value (float), valid (bool)' @@ -476,7 +553,7 @@ def test_get_extensions_information(): assert extensions_information.loc['hvdcOperatorActivePowerRange']['detail'] == '' assert extensions_information.loc['hvdcOperatorActivePowerRange']['attributes'] == 'index : id (str), opr_from_cs1_to_cs2 (float), opr_from_cs2_to_cs1 (float)' assert extensions_information.loc['activePowerControl']['detail'] == 'Provides information about the participation of generators to balancing' - assert extensions_information.loc['activePowerControl']['attributes'] == 'index : id (str), participate (bool), droop (float)' + assert extensions_information.loc['activePowerControl']['attributes'] == 'index : id (str), participate (bool), droop (float), participation_factor (float), max_target_p (float), min_target_p (float)' assert extensions_information.loc['entsoeCategory']['detail'] == 'Provides Entsoe category code for a generator' assert extensions_information.loc['entsoeCategory']['attributes'] == 'index : id (str), code (int)' assert extensions_information.loc['entsoeArea']['detail'] == 'Provides Entsoe geographical code for a substation' @@ -495,3 +572,5 @@ def test_get_extensions_information(): assert extensions_information.loc['secondaryVoltageControl']['attributes'] == '[dataframe "zones"] index : name (str), target_v (float), bus_ids (str) / [dataframe "units"] index : unit_id (str), participate (bool), zone_name (str)' assert extensions_information.loc['substationPosition']['attributes'] == 'index : id (str), latitude (float), longitude (float)' assert extensions_information.loc['linePosition']['attributes'] == 'index : id (str), num (int), latitude (float), longitude (float)' + assert extensions_information.loc['referencePriorities']['detail'] == 'Defines the angle reference generator, busbar section or load of a power flow calculation, i.e. which bus will be used with a zero-voltage angle.' + assert extensions_information.loc['referencePriorities']['attributes'] == 'index : id (str), priority (int)' diff --git a/tests/test_network_modification.py b/tests/test_network_modification.py index 94762f3067..b466c157b3 100644 --- a/tests/test_network_modification.py +++ b/tests/test_network_modification.py @@ -3,6 +3,7 @@ import pypowsybl as pp import pandas as pd import pathlib +import re from pypowsybl import PyPowsyblError import pypowsybl.report as rp @@ -28,13 +29,13 @@ def test_voltage_level_topology_creation_deprecated_report(): df = pd.DataFrame.from_records(index="id", data=[ {'id': 'VL1', 'aligned_buses_or_busbar_count': 3, 'switch_kinds': 'BREAKER, DISCONNECTOR'} ]) - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_voltage_level_topology(network, df, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) + report2 = str(report_node) + assert len(report2) > len(report1) def test_voltage_level_topology_creation_with_no_switch_kind(): @@ -187,20 +188,20 @@ def test_add_load_bay_node_breaker_deprecated_reporter(): n = pp.network.create_four_substations_node_breaker_network_with_extensions() df = pd.DataFrame(index=["new_load"], columns=["id", "p0", "q0", "bus_or_busbar_section_id", "position_order"], data=[["new_load", 10.0, 3.0, "S1VL1_BBS", 0]]) - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_load_bay(network=n, df=df, raise_exception=True, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - load = n.get_loads().loc["new_load"] - assert load.p0 == 10.0 - assert load.q0 == 3.0 - position = n.get_extensions('position').loc["new_load"] - assert position.order == 0 - assert position.feeder_name == 'new_load' - assert position.direction == 'BOTTOM' + report2 = str(report_node) + assert len(report2) > len(report1) + load = n.get_loads().loc["new_load"] + assert load.p0 == 10.0 + assert load.q0 == 3.0 + position = n.get_extensions('position').loc["new_load"] + assert position.order == 0 + assert position.feeder_name == 'new_load' + assert position.direction == 'BOTTOM' def test_add_load_bay_from_kwargs_node_breaker(): @@ -245,25 +246,25 @@ def test_add_generator_bay(): def test_add_generator_bay_deprecated_report(): n = pp.network.create_four_substations_node_breaker_network_with_extensions() - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_generator_bay(n, pd.DataFrame.from_records( data=[('new_gen', 4999, -9999.99, True, 100, 150, 300, 'S1VL1_BBS', 15, 'TOP')], columns=['id', 'max_p', 'min_p', 'voltage_regulator_on', 'target_p', 'target_q', 'target_v', 'bus_or_busbar_section_id', 'position_order', 'direction'], index='id'), reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - generator = n.get_generators().loc['new_gen'] - assert generator.target_p == 100.0 - assert generator.target_q == 150.0 - assert generator.voltage_level_id == 'S1VL1' - position = n.get_extensions('position').loc["new_gen"] - assert position.order == 15 - assert position.feeder_name == 'new_gen' - assert position.direction == 'TOP' + report2 = str(report_node) + assert len(report2) > len(report1) + generator = n.get_generators().loc['new_gen'] + assert generator.target_p == 100.0 + assert generator.target_q == 150.0 + assert generator.voltage_level_id == 'S1VL1' + position = n.get_extensions('position').loc["new_gen"] + assert position.order == 15 + assert position.feeder_name == 'new_gen' + assert position.direction == 'TOP' def test_add_generator_bay_bus_breaker(): @@ -306,23 +307,23 @@ def test_add_battery_bay_deprecated_report(): columns=['id', 'bus_or_busbar_section_id', 'max_p', 'min_p', 'target_p', 'target_q', 'position_order'], data=[('new_battery', 'S1VL1_BBS', 100, 10, 90, 20, 15)], index='id') - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_battery_bay(n, df, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - battery = n.get_batteries().loc['new_battery'] - assert battery.voltage_level_id == 'S1VL1' - assert battery.max_p == 100 - assert battery.min_p == 10 - assert battery.target_p == 90 - assert battery.target_q == 20 - position = n.get_extensions('position').loc["new_battery"] - assert position.order == 15 - assert position.feeder_name == 'new_battery' - assert position.direction == 'BOTTOM' + report2 = str(report_node) + assert len(report2) > len(report1) + battery = n.get_batteries().loc['new_battery'] + assert battery.voltage_level_id == 'S1VL1' + assert battery.max_p == 100 + assert battery.min_p == 10 + assert battery.target_p == 90 + assert battery.target_q == 20 + position = n.get_extensions('position').loc["new_battery"] + assert position.order == 15 + assert position.feeder_name == 'new_battery' + assert position.direction == 'BOTTOM' def test_add_battery_bay_bus_breaker(): @@ -385,25 +386,25 @@ def test_add_dangling_line_bay_deprecated_reporter(): 'position_order': 15, 'bus_or_busbar_section_id': 'S1VL1_BBS' }]) - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_dangling_line_bay(n, df, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - dangling_line = n.get_dangling_lines().loc['new_dangling_line'] - assert dangling_line.voltage_level_id == 'S1VL1' - assert dangling_line.r == 2 - assert dangling_line.x == 2 - assert dangling_line.g == 1 - assert dangling_line.b == 1 - assert dangling_line.p0 == 100 - assert dangling_line.q0 == 101 - position = n.get_extensions('position').loc["new_dangling_line"] - assert position.order == 15 - assert position.feeder_name == 'new_dangling_line' - assert position.direction == 'BOTTOM' + report2 = str(report_node) + assert len(report2) > len(report1) + dangling_line = n.get_dangling_lines().loc['new_dangling_line'] + assert dangling_line.voltage_level_id == 'S1VL1' + assert dangling_line.r == 2 + assert dangling_line.x == 2 + assert dangling_line.g == 1 + assert dangling_line.b == 1 + assert dangling_line.p0 == 100 + assert dangling_line.q0 == 101 + position = n.get_extensions('position').loc["new_dangling_line"] + assert position.order == 15 + assert position.feeder_name == 'new_dangling_line' + assert position.direction == 'BOTTOM' def test_add_dangling_line_bay_bus_breaker(): @@ -639,24 +640,24 @@ def test_add_svc_bay_deprecated_report(): 'target_v': 400, 'b_min': 0, 'b_max': 2}]) - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_static_var_compensator_bay(n, df, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - svc = n.get_static_var_compensators().loc['svc_test'] - assert svc.voltage_level_id == 'S1VL1' - assert svc.target_q == 200 - assert svc.regulation_mode == 'REACTIVE_POWER' - assert svc.target_v == 400 - assert svc.b_min == 0 - assert svc.b_max == 2 - position = n.get_extensions('position').loc['svc_test'] - assert position.order == 15 - assert position.feeder_name == 'svc_test' - assert position.direction == 'BOTTOM' + report2 = str(report_node) + assert len(report2) > len(report1) + svc = n.get_static_var_compensators().loc['svc_test'] + assert svc.voltage_level_id == 'S1VL1' + assert svc.target_q == 200 + assert svc.regulation_mode == 'REACTIVE_POWER' + assert svc.target_v == 400 + assert svc.b_min == 0 + assert svc.b_max == 2 + position = n.get_extensions('position').loc['svc_test'] + assert position.order == 15 + assert position.feeder_name == 'svc_test' + assert position.direction == 'BOTTOM' def test_add_svc_bay_bus_breaker(): @@ -714,21 +715,21 @@ def test_add_lcc_bay_deprecated_report(): 'position_order': 15, 'loss_factor': 0.1, 'power_factor': 0.2}]) - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_lcc_converter_station_bay(n, df, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - lcc = n.get_lcc_converter_stations().loc['lcc_test'] - assert lcc.voltage_level_id == 'S1VL1' - assert lcc.loss_factor == pytest.approx(0.1, abs=1e-6) - assert lcc.power_factor == pytest.approx(0.2, abs=1e-6) - position = n.get_extensions('position').loc['lcc_test'] - assert position.order == 15 - assert position.feeder_name == 'lcc_test' - assert position.direction == 'BOTTOM' + report2 = str(report_node) + assert len(report2) > len(report1) + lcc = n.get_lcc_converter_stations().loc['lcc_test'] + assert lcc.voltage_level_id == 'S1VL1' + assert lcc.loss_factor == pytest.approx(0.1, abs=1e-6) + assert lcc.power_factor == pytest.approx(0.2, abs=1e-6) + position = n.get_extensions('position').loc['lcc_test'] + assert position.order == 15 + assert position.feeder_name == 'lcc_test' + assert position.direction == 'BOTTOM' def test_add_lcc_bay_node_breaker(): @@ -787,23 +788,23 @@ def test_add_vsc_bay_deprecated_report(): 'voltage_regulator_on': True, 'loss_factor': 1.0, 'target_v': 400}]) - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_vsc_converter_station_bay(n, df, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - vsc = n.get_vsc_converter_stations().loc['vsc_test'] - assert vsc.voltage_level_id == 'S1VL1' - assert vsc.target_q == 200 - assert vsc.voltage_regulator_on == True - assert vsc.loss_factor == 1 - assert vsc.target_v == 400 - position = n.get_extensions('position').loc['vsc_test'] - assert position.order == 15 - assert position.feeder_name == 'vsc_test' - assert position.direction == 'BOTTOM' + report2 = str(report_node) + assert len(report2) > len(report1) + vsc = n.get_vsc_converter_stations().loc['vsc_test'] + assert vsc.voltage_level_id == 'S1VL1' + assert vsc.target_q == 200 + assert vsc.voltage_regulator_on == True + assert vsc.loss_factor == 1 + assert vsc.target_v == 400 + position = n.get_extensions('position').loc['vsc_test'] + assert position.order == 15 + assert position.feeder_name == 'vsc_test' + assert position.direction == 'BOTTOM' def test_add_vsc_bay_bus_breaker(): @@ -852,22 +853,22 @@ def test_create_branch_feeder_bays_twt_bus_breaker_deprecated_reporter(): columns=['id', 'bus_or_busbar_section_id_1', 'bus_or_busbar_section_id_2', 'r', 'x', 'g', 'b', 'rated_u1', 'rated_u2', 'rated_s'], data=[['new_twt', 'NGEN', 'NHV1', 5.0, 50.0, 2.0, 4.0, 225.0, 400.0, 1.0]]) - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_2_windings_transformer_bays(n, df, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - retrieved_new_twt = n.get_2_windings_transformers().loc['new_twt'] - assert retrieved_new_twt["r"] == 5.0 - assert retrieved_new_twt["x"] == 50.0 - assert retrieved_new_twt["g"] == 2.0 - assert retrieved_new_twt["b"] == 4.0 - assert retrieved_new_twt["rated_u1"] == 225.0 - assert retrieved_new_twt["rated_u2"] == 400.0 - assert retrieved_new_twt["rated_s"] == 1.0 - assert n.get_extensions('position').size == 0 + report2 = str(report_node) + assert len(report2) > len(report1) + retrieved_new_twt = n.get_2_windings_transformers().loc['new_twt'] + assert retrieved_new_twt["r"] == 5.0 + assert retrieved_new_twt["x"] == 50.0 + assert retrieved_new_twt["g"] == 2.0 + assert retrieved_new_twt["b"] == 4.0 + assert retrieved_new_twt["rated_u1"] == 225.0 + assert retrieved_new_twt["rated_u2"] == 400.0 + assert retrieved_new_twt["rated_s"] == 1.0 + assert n.get_extensions('position').size == 0 def test_create_branch_feeder_bays_line_bus_breaker(): @@ -893,23 +894,23 @@ def test_create_branch_feeder_bays_line_bus_breaker_deprecated_report(): df = pd.DataFrame(index=['new_line'], columns=['id', 'bus_or_busbar_section_id_1', 'bus_or_busbar_section_id_2', 'r', 'x', 'g1', 'g2', 'b1', 'b2'], data=[['new_line', 'NHV1', 'NHV2', 5.0, 50.0, 20.0, 30.0, 40.0, 50.0]]) - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_line_bays(n, df, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - retrieved_newline = n.get_lines().loc['new_line'] - assert retrieved_newline["r"] == 5.0 - assert retrieved_newline["x"] == 50.0 - assert retrieved_newline["g1"] == 20.0 - assert retrieved_newline["g2"] == 30.0 - assert retrieved_newline["b1"] == 40.0 - assert retrieved_newline["b2"] == 50.0 - assert retrieved_newline["connected1"] - assert retrieved_newline["connected2"] - assert n.get_extensions('position').size == 0 + report2 = str(report_node) + assert len(report2) > len(report1) + retrieved_newline = n.get_lines().loc['new_line'] + assert retrieved_newline["r"] == 5.0 + assert retrieved_newline["x"] == 50.0 + assert retrieved_newline["g1"] == 20.0 + assert retrieved_newline["g2"] == 30.0 + assert retrieved_newline["b1"] == 40.0 + assert retrieved_newline["b2"] == 50.0 + assert retrieved_newline["connected1"] + assert retrieved_newline["connected2"] + assert n.get_extensions('position').size == 0 def test_create_coupling_device(): @@ -955,19 +956,19 @@ def test_create_coupling_device_deprecated_report(): coupling_device = pd.DataFrame.from_records(index='bus_or_busbar_section_id_1', data=[ {'bus_or_busbar_section_id_1': 'BBS1', 'bus_or_busbar_section_id_2': 'BBS2'}, ]) - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_coupling_device(n, coupling_device, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - switches = n.get_switches() - assert len(switches.index) == 5 - assert len(switches[switches["kind"] == "DISCONNECTOR"].index) == 4 - assert len(switches[switches["kind"] == "BREAKER"].index) == 1 - assert len(switches[switches["open"] == True].index) == 2 - assert len(switches[switches["open"] == False].index) == 3 + report2 = str(report_node) + assert len(report2) > len(report1) + switches = n.get_switches() + assert len(switches.index) == 5 + assert len(switches[switches["kind"] == "DISCONNECTOR"].index) == 4 + assert len(switches[switches["kind"] == "BREAKER"].index) == 1 + assert len(switches[switches["open"] == True].index) == 2 + assert len(switches[switches["open"] == False].index) == 3 def test_create_coupling_device_kwargs(): @@ -1017,23 +1018,23 @@ def test_remove_feeder_bay_deprecated_report(): 'position_order_2', 'direction_1', 'direction_2', 'r', 'x', 'g1', 'g2', 'b1', 'b2'], data=[['new_line', 'S1VL2_BBS1', 'S2VL1_BBS', 115, 121, 'TOP', 'TOP', 5.0, 50.0, 20.0, 30.0, 40.0, 50.0]]) - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.create_line_bays(n, df, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) - assert 'new_line' in n.get_lines().index - assert 'new_line1_BREAKER' in n.get_switches().index - assert 'new_line1_DISCONNECTOR_25_0' in n.get_switches().index - assert 'new_line2_BREAKER' in n.get_switches().index - assert 'new_line2_DISCONNECTOR_8_0' in n.get_switches().index - pp.network.remove_feeder_bays(n, 'new_line') - assert 'new_line1_BREAKER' not in n.get_switches().index - assert 'new_line1_DISCONNECTOR' not in n.get_switches().index - assert 'new_line2_BREAKER' not in n.get_switches().index - assert 'new_line2_DISCONNECTOR' not in n.get_switches().index + report2 = str(report_node) + assert len(report2) > len(report1) + assert 'new_line' in n.get_lines().index + assert 'new_line1_BREAKER' in n.get_switches().index + assert 'new_line1_DISCONNECTOR_25_0' in n.get_switches().index + assert 'new_line2_BREAKER' in n.get_switches().index + assert 'new_line2_DISCONNECTOR_8_0' in n.get_switches().index + pp.network.remove_feeder_bays(n, 'new_line') + assert 'new_line1_BREAKER' not in n.get_switches().index + assert 'new_line1_DISCONNECTOR' not in n.get_switches().index + assert 'new_line2_BREAKER' not in n.get_switches().index + assert 'new_line2_DISCONNECTOR' not in n.get_switches().index def test_remove_feeder_bay(): @@ -1345,15 +1346,15 @@ def test_remove_voltage_level_deprecated_report(): assert 'VLTEST' in n.get_voltage_levels().index - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.remove_voltage_levels(n, 'VLTEST', reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) + report2 = str(report_node) + assert len(report2) > len(report1) - assert 'VLTEST' not in n.get_voltage_levels().index + assert 'VLTEST' not in n.get_voltage_levels().index def test_remove_hvdc(): @@ -1378,13 +1379,13 @@ def test_remove_hvdc_deprecated_report(): assert 'SHUNT' not in n.get_shunt_compensators().index assert n.get_lcc_converter_stations().empty - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - with pytest.deprecated_call(): + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 pp.network.remove_hvdc_lines(n, 'HVDC1', reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) + report2 = str(report_node) + assert len(report2) > len(report1) def test_exception_create_element_with_bay(): diff --git a/tests/test_pandapower.py b/tests/test_pandapower.py new file mode 100644 index 0000000000..7fa5658f34 --- /dev/null +++ b/tests/test_pandapower.py @@ -0,0 +1,82 @@ +# Copyright (c) 2024, RTE (http://www.rte-france.com) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# +import pathlib + +import pandapower as pdp +import pytest + +import pypowsybl as pp +import logging + +EPS_V = 0.001 + +TEST_DIR = pathlib.Path(__file__).parent +DATA_DIR = TEST_DIR.parent / 'data' + +@pytest.fixture(autouse=True) +def setup(): + logging.basicConfig() + logging.getLogger('powsybl').setLevel(logging.DEBUG) + + +def run_and_compare(pdp_n, expected_bus_count: int): + pdp.runpp(pdp_n, numba=True, enforce_q_lims=False, distributed_slack=True, trafo_model="pi") + n = pp.network.convert_from_pandapower(pdp_n) + assert len(n.get_buses()) == expected_bus_count + param = pp.loadflow.Parameters(voltage_init_mode=pp.loadflow.VoltageInitMode.UNIFORM_VALUES, + transformer_voltage_control_on=False, + use_reactive_limits=False, + shunt_compensator_voltage_control_on=False, + phase_shifter_regulation_on=False, + distributed_slack=True) + results = pp.loadflow.run_ac(n, param) + assert pp.loadflow.ComponentStatus.CONVERGED == results[0].status + pdp_v = list(pdp_n.res_bus['vm_pu'] * pdp_n.bus['vn_kv']) + buses = n.get_bus_breaker_view_buses() + v = list(buses['v_mag']) + for index, (pdp_v_val, v_val) in enumerate(zip(pdp_v, v)): + assert pdp_v_val == pytest.approx(v_val, abs=EPS_V, rel=EPS_V), f"Voltage mismatch at index {index}: {pdp_v_val} != {v_val}" + + +def test_pandapower_case5(): + run_and_compare(pdp.networks.case5(), 5) + +def test_pandapower_case4gs(): + run_and_compare(pdp.networks.case4gs(), 4) + +def test_pandapower_case6ww(): + run_and_compare(pdp.networks.case6ww(), 6) + +def test_pandapower_case9(): + run_and_compare(pdp.networks.case9(), 9) + +def test_pandapower_case14(): + run_and_compare(pdp.networks.case14(), 14) + +def test_pandapower_case30(): + run_and_compare(pdp.networks.case30(), 30) + +def test_pandapower_case_ieee30(): + run_and_compare(pdp.networks.case_ieee30(), 30) + +def test_pandapower_case33bw(): + run_and_compare(pdp.networks.case33bw(), 33) + +def test_pandapower_case39(): + run_and_compare(pdp.networks.case39(), 39) + +def test_pandapower_case57(): + run_and_compare(pdp.networks.case57(), 57) + +def test_pandapower_panda_four_load_branch(): + run_and_compare(pdp.networks.panda_four_load_branch(), 6) + +def test_pandapower_four_loads_with_branches_out(): + run_and_compare(pdp.networks.four_loads_with_branches_out(), 10) + +def test_educ_case14_storage(): + run_and_compare(pdp.from_json(DATA_DIR / 'educ_case14_storage.json'), 14) diff --git a/tests/test_per_unit.py b/tests/test_per_unit.py index 2cdbbbb0c0..c08685b330 100644 --- a/tests/test_per_unit.py +++ b/tests/test_per_unit.py @@ -8,6 +8,7 @@ import pypowsybl as pp import pandas as pd from numpy import nan +import re import util import pytest @@ -15,7 +16,8 @@ def test_bus_per_unit(): n = pp.network.create_eurostag_tutorial_example1_network() pp.loadflow.run_ac(n) - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) buses = n.get_buses() expected = pd.DataFrame(index=pd.Series(name='id', data=['VLGEN_0', 'VLHV1_0', 'VLHV2_0', 'VLLOAD_0']), columns=['name', 'v_mag', 'v_angle', 'connected_component', 'synchronous_component', @@ -40,14 +42,15 @@ def test_bus_per_unit(): def test_generator_per_unit(): n = pp.network.create_eurostag_tutorial_example1_network() pp.loadflow.run_ac(n) - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame.from_records( index='id', columns=['id', 'name', 'energy_source', 'target_p', 'min_p', 'max_p', 'min_q', 'max_q', 'rated_s', 'reactive_limits_kind', 'target_v', 'target_q', 'voltage_regulator_on', 'regulated_element_id', 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], - data=[['GEN', '', 'OTHER', 6.07, -100, 49.99, -100, 100, None, 'MIN_MAX', 1.02, 3.01, True, 'GEN', -3.03, + data=[['GEN', '', 'OTHER', 6.07, -100, 49.99, -100, 100, nan, 'MIN_MAX', 1.02, 3.01, True, 'GEN', -3.03, -1.12641, 3.16461, 'VLGEN', 'VLGEN_0', True], ['GEN2', '', 'OTHER', 6.07, -100, 49.99, -1.79769e+306, 1.79769e+306, None, 'MIN_MAX', 1.02, 3.01, True, 'GEN2', -3.03, -1.13, 3.16, 'VLGEN', 'VLGEN_0', True]]) @@ -62,7 +65,7 @@ def test_generator_per_unit(): 'target_v', 'target_q', 'voltage_regulator_on', 'regulated_element_id', 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], - data=[['GEN', '', 'OTHER', 6.07, -100, 49.99, -100, 100, None, 'MIN_MAX', 1.1, 3.02, False, 'GEN', -3.03, + data=[['GEN', '', 'OTHER', 6.07, -100, 49.99, -100, 100, nan, 'MIN_MAX', 1.1, 3.02, False, 'GEN', -3.03, -1.12641, nan, 'VLGEN', '', False], ['GEN2', '', 'OTHER', 6.07, -100, 49.99, -1.79769e+306, 1.79769e+306, None, 'MIN_MAX', 1.02, 3.01, True, 'GEN2', -3.03, -1.13, 3.16, 'VLGEN', 'VLGEN_0', True]]) @@ -72,7 +75,8 @@ def test_generator_per_unit(): def test_loads_per_unit(): n = pp.network.create_eurostag_tutorial_example1_network() pp.loadflow.run_ac(n) - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame(index=pd.Series(name='id', data=['LOAD']), columns=['name', 'type', 'p0', 'q0', 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], @@ -89,7 +93,8 @@ def test_loads_per_unit(): def test_busbar_per_unit(): n = pp.network.create_four_substations_node_breaker_network() pp.loadflow.run_ac(n) - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame(index=pd.Series(name='id', data=['S1VL1_BBS', 'S1VL2_BBS1', 'S1VL2_BBS2', 'S2VL1_BBS', 'S3VL1_BBS', 'S4VL1_BBS']), @@ -105,7 +110,8 @@ def test_busbar_per_unit(): def test_hvdc_per_unit(): n = pp.network.create_four_substations_node_breaker_network() - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame.from_records( index='id', columns=['id', 'name', 'converters_mode', 'target_p', 'max_p', 'nominal_v', 'r', @@ -126,7 +132,8 @@ def test_hvdc_per_unit(): def test_lines_per_unit(): n = pp.network.create_four_substations_node_breaker_network() - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) lines = n.get_lines() expected = pd.DataFrame(index=pd.Series(name='id', data=['LINE_S2S3', 'LINE_S3S4']), columns=['name', 'r', 'x', 'g1', 'b1', 'g2', 'b2', 'p1', 'q1', 'i1', 'p2', 'q2', 'i2', @@ -153,7 +160,8 @@ def test_lines_per_unit(): def test_two_windings_transformers_per_unit(): n = pp.network.create_four_substations_node_breaker_network() - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame(index=pd.Series(name='id', data=['TWT']), columns=['name', 'r', 'x', 'g', 'b', 'rated_u1', 'rated_u2', 'rated_s', 'p1', 'q1', 'i1', 'p2', @@ -181,7 +189,8 @@ def test_two_windings_transformers_per_unit(): def test_shunt_compensators_per_unit(): n = pp.network.create_four_substations_node_breaker_network() - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame(index=pd.Series(name='id', data=['SHUNT']), columns=['name', 'g', 'b', 'model_type', 'max_section_count', 'section_count', 'voltage_regulation_on', 'target_v', 'target_deadband', 'regulating_bus_id', @@ -195,7 +204,8 @@ def test_shunt_compensators_per_unit(): def test_dangling_lines_per_unit(): n = util.create_dangling_lines_network() pp.loadflow.run_ac(n) - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame(index=pd.Series(name='id', data=['DL']), columns=['name', 'r', 'x', 'g', 'b', 'p0', 'q0', 'p', 'q', 'i', 'voltage_level_id', @@ -217,7 +227,8 @@ def test_dangling_lines_per_unit(): def test_lcc_converter_stations_per_unit(): n = pp.network.create_four_substations_node_breaker_network() pp.loadflow.run_ac(n) - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame(index=pd.Series(name='id', data=['LCC1', 'LCC2']), columns=['name', 'power_factor', 'loss_factor', 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], @@ -237,7 +248,8 @@ def test_lcc_converter_stations_per_unit(): def test_vsc_converter_stations_per_unit(): n = pp.network.create_four_substations_node_breaker_network() - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame.from_records( index='id', columns=['id', 'name', 'loss_factor', 'min_q', 'max_q', 'reactive_limits_kind', 'target_v', 'target_q', 'voltage_regulator_on', @@ -262,7 +274,8 @@ def test_vsc_converter_stations_per_unit(): def test_get_static_var_compensators_per_unit(): n = pp.network.create_four_substations_node_breaker_network() pp.loadflow.run_ac(n) - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame.from_records( index='id', columns=['id', 'name', 'b_min', 'b_max', 'target_v', 'target_q', 'regulation_mode', 'regulated_element_id', @@ -284,7 +297,8 @@ def test_get_static_var_compensators_per_unit(): def test_voltage_level_per_unit(): n = pp.network.create_four_substations_node_breaker_network() - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame(index=pd.Series(name='id', data=['S1VL1', 'S1VL2', 'S2VL1', 'S3VL1', 'S4VL1']), columns=['name', 'substation_id', 'nominal_v', 'high_voltage_limit', 'low_voltage_limit'], data=[['', 'S1', 225, 1.07, 0.98], ['', 'S1', 400, 1.1, 0.98], ['', 'S2', 400, 1.1, 0.98], @@ -294,7 +308,8 @@ def test_voltage_level_per_unit(): def test_reactive_capability_curve_points_per_unit(): n = pp.network.create_four_substations_node_breaker_network() - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) reactive_capability_curve_points = n.get_reactive_capability_curve_points() pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('GH1', 0)], pd.Series(data={'p': 0, 'min_q': -7.69, 'max_q': 8.6}, @@ -338,7 +353,8 @@ def test_reactive_capability_curve_points_per_unit(): def test_three_windings_transformer_per_unit(): n = util.create_three_windings_transformer_network() - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame( index=pd.Series(name='id', data=['3WT']), columns=['name', 'rated_u0', 'r1', 'x1', 'g1', 'b1', 'rated_u1', 'rated_s1', 'ratio_tap_position1', @@ -384,7 +400,8 @@ def test_three_windings_transformer_per_unit(): def test_batteries(): n = util.create_battery_network() - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame(index=pd.Series(name='id', data=['BAT', 'BAT2']), columns=['name', 'max_p', 'min_p', 'min_q', 'max_q', 'reactive_limits_kind', 'target_p', 'target_q', 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], @@ -404,18 +421,20 @@ def test_batteries(): def test_ratio_tap_changers_per_unit(): n = pp.network.create_eurostag_tutorial_example1_network() - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) expected = pd.DataFrame(index=pd.Series(name='id', data=['NHV2_NLOAD']), columns=['tap', 'low_tap', 'high_tap', 'step_count', 'on_load', 'regulating', 'target_v', 'target_deadband', 'regulating_bus_id', 'rho', 'alpha'], - data=[[1, 0, 2, 3, True, True, 158.0, 0.0, 'VLLOAD_0', 1.00, nan]]) + data=[[1, 0, 2, 3, True, True, 1.053, 0.0, 'VLLOAD_0', 1.00, nan]]) pd.testing.assert_frame_equal(expected, n.get_ratio_tap_changers(), check_dtype=False, atol=1e-2) def test_lines_not_same_nominal_voltage_per_unit(): n = pp.network.create_ieee14() - n = per_unit_view(n, 100) + with pytest.warns(DeprecationWarning, match=re.escape("Per-unit view is deprecated and slow (make a deep copy of the network), use per unit mode of the network instead")): + n = per_unit_view(n, 100) lines = n.get_lines() assert lines.loc['L7-8-1']['r'] == 0 assert lines.loc['L7-8-1']['x'] == pytest.approx(0.17615, rel=1e-5) @@ -429,12 +448,12 @@ def test_ratio_tap_changer_steps_per_unit(): n = pp.network.create_eurostag_tutorial_example1_network() n.per_unit = True steps = n.get_ratio_tap_changer_steps() - assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(2.15, rel=1e-1) - assert steps.loc['NHV2_NLOAD', 1]['rho'] == pytest.approx(2.54, rel=1e-1) - assert steps.loc['NHV2_NLOAD', 2]['rho'] == pytest.approx(2.92, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(0.850566, rel=1e-6) + assert steps.loc['NHV2_NLOAD', 1]['rho'] == pytest.approx(1.000666, rel=1e-6) + assert steps.loc['NHV2_NLOAD', 2]['rho'] == pytest.approx(1.150766, rel=1e-6) n.update_ratio_tap_changer_steps(id='NHV2_NLOAD', position=0, r=1, x=3, g=4, b=2) steps = n.get_ratio_tap_changer_steps() - assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(2.15, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(0.850566, rel=1e-6) assert steps.loc['NHV2_NLOAD', 0]['r'] == pytest.approx(1, rel=1e-1) assert steps.loc['NHV2_NLOAD', 0]['x'] == pytest.approx(3, rel=1e-1) assert steps.loc['NHV2_NLOAD', 0]['g'] == pytest.approx(4, rel=1e-1) diff --git a/tests/test_security_analysis.py b/tests/test_security_analysis.py index a4486bfd06..f3c78f36fc 100644 --- a/tests/test_security_analysis.py +++ b/tests/test_security_analysis.py @@ -10,6 +10,7 @@ import pandas as pd import pypowsybl.report as rp from pypowsybl._pypowsybl import ConditionType +import re @pytest.fixture(autouse=True) @@ -78,13 +79,13 @@ def test_variant(): def test_monitored_elements(): - n = pp.network.create_eurostag_tutorial_example1_network() + n = pp.network.create_eurostag_tutorial_example1_with_more_generators_network() sa = pp.security.create_analysis() sa.add_single_element_contingency('NHV1_NHV2_1', 'NHV1_NHV2_1') - sa.add_single_element_contingency('NGEN_NHV1', 'NGEN_NHV1') + sa.add_single_element_contingency('GEN', 'GEN') sa.add_monitored_elements(voltage_level_ids=['VLHV2']) - sa.add_postcontingency_monitored_elements(branch_ids=['NHV1_NHV2_2'], contingency_ids=['NHV1_NHV2_1', 'NGEN_NHV1']) - sa.add_postcontingency_monitored_elements(branch_ids=['NHV1_NHV2_1'], contingency_ids='NGEN_NHV1') + sa.add_postcontingency_monitored_elements(branch_ids=['NHV1_NHV2_2'], contingency_ids=['NHV1_NHV2_1', 'GEN']) + sa.add_postcontingency_monitored_elements(branch_ids=['NHV1_NHV2_1'], contingency_ids='GEN') sa.add_precontingency_monitored_elements(branch_ids=['NHV1_NHV2_2']) sa_result = sa.run_ac(n) @@ -100,8 +101,8 @@ def test_monitored_elements(): assert branch_results.columns.tolist() == ['p1', 'q1', 'i1', 'p2', 'q2', 'i2', 'flow_transfer'] assert len(branch_results) == 4 assert branch_results.loc['', '', 'NHV1_NHV2_2']['p1'] == pytest.approx(302.44, abs=1e-2) - assert branch_results.loc['NGEN_NHV1', '', 'NHV1_NHV2_1']['p1'] == pytest.approx(301.05, abs=1e-2) - assert branch_results.loc['NGEN_NHV1', '', 'NHV1_NHV2_2']['p1'] == pytest.approx(301.05, abs=1e-2) + assert branch_results.loc['GEN', '', 'NHV1_NHV2_1']['p1'] == pytest.approx(302.44, abs=1e-2) + assert branch_results.loc['GEN', '', 'NHV1_NHV2_2']['p1'] == pytest.approx(302.44, abs=1e-2) assert branch_results.loc['NHV1_NHV2_1', '', 'NHV1_NHV2_2']['p1'] == pytest.approx(610.56, abs=1e-2) @@ -162,15 +163,16 @@ def test_ac_security_analysis_with_report(): assert len(report2) >= len(report1) def test_ac_security_analysis_with_deprecated_report(): - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - n = pp.network.create_eurostag_tutorial_example1_network() - sa = pp.security.create_analysis() - sa.add_single_element_contingency('NHV1_NHV2_1', 'First contingency') - sa.run_ac(n, reporter=report_node) - report2 = str(report_node) - assert len(report2) >= len(report1) + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 + n = pp.network.create_eurostag_tutorial_example1_network() + sa = pp.security.create_analysis() + sa.add_single_element_contingency('NHV1_NHV2_1', 'First contingency') + sa.run_ac(n, reporter=report_node) + report2 = str(report_node) + assert len(report2) >= len(report1) def test_dc_analysis_with_report(): report_node = rp.ReportNode() @@ -201,10 +203,8 @@ def test_loadflow_parameters(): def test_security_analysis_parameters(): network = pp.network.create_eurostag_tutorial_example1_network() - network.create_operational_limits(pd.DataFrame.from_records(index='element_id', data=[ - {'element_id': 'NHV1_NHV2_1', 'name': 'permanent_limit', 'element_type': 'LINE', 'side': 'ONE', - 'type': 'CURRENT', 'value': 400, - 'acceptable_duration': np.inf, 'is_fictitious': False}])) + network.create_operational_limits(element_id='NHV1_NHV2_1', name='permanent_limit', side='ONE', type='CURRENT', value=400.0, + acceptable_duration=-1, fictitious=False) sa = pp.security.create_analysis() sa.add_single_element_contingency('', 'First contingency') sa.add_single_element_contingency('NHV1_NHV2_2', 'First contingency') @@ -241,8 +241,8 @@ def test_security_analysis_parameters(): def test_provider_parameters_names(): - assert pp.security.get_provider_parameters_names() == ['createResultExtension', 'contingencyPropagation','threadCount'] - assert pp.security.get_provider_parameters_names('OpenLoadFlow') == ['createResultExtension', 'contingencyPropagation','threadCount'] + assert pp.security.get_provider_parameters_names() == ['createResultExtension', 'contingencyPropagation', 'threadCount', 'dcFastMode'] + assert pp.security.get_provider_parameters_names('OpenLoadFlow') == ['createResultExtension', 'contingencyPropagation', 'threadCount', 'dcFastMode'] with pytest.raises(pp.PyPowsyblError, match='No security analysis provider for name \'unknown\''): pp.security.get_provider_parameters_names('unknown') diff --git a/tests/test_sensitivity_analysis.py b/tests/test_sensitivity_analysis.py index 92692ec749..cb21da8e75 100644 --- a/tests/test_sensitivity_analysis.py +++ b/tests/test_sensitivity_analysis.py @@ -8,6 +8,7 @@ import pytest import pypowsybl as pp import pandas as pd +import re from pypowsybl import PyPowsyblError import pypowsybl.report as rp from pypowsybl.sensitivity import SensitivityFunctionType, SensitivityVariableType, ContingencyContextType @@ -31,7 +32,7 @@ def test_config(): n.update_generators(generators) sa = pp.sensitivity.create_dc_analysis() sa.add_single_element_contingency('L1-2-1') - sa.set_branch_flow_factor_matrix(['L1-5-1', 'L2-3-1'], ['B1-G', 'B2-G', 'B3-G']) + sa.add_branch_flow_factor_matrix(['L1-5-1', 'L2-3-1'], ['B1-G', 'B2-G', 'B3-G']) with pytest.raises(PyPowsyblError, match='No sensitivity analysis provider for name \'provider\''): sa.run(n) r = sa.run(n, provider='OpenLoadFlow') @@ -233,7 +234,7 @@ def test_provider_names(): def test_no_output_matrices_available(): network = pp.network.create_eurostag_tutorial_example1_network() analysis = pp.sensitivity.create_ac_analysis() - analysis.set_branch_flow_factor_matrix(network.get_lines().index.to_list(), + analysis.add_branch_flow_factor_matrix(network.get_lines().index.to_list(), network.get_generators().index.to_list()) result = analysis.run(network) df = result.get_sensitivity_matrix('default') @@ -250,7 +251,7 @@ def test_provider_parameters(): provider_parameters={'maxNewtonRaphsonIterations': '1'}) n = pp.network.create_eurostag_tutorial_example1_network() analysis = pp.sensitivity.create_ac_analysis() - analysis.set_branch_flow_factor_matrix(['NHV1_NHV2_1'], ['GEN']) + analysis.add_branch_flow_factor_matrix(['NHV1_NHV2_1'], ['GEN']) with pytest.raises(pp.PyPowsyblError, match='Load flow ended with status MAX_ITERATION_REACHED'): analysis.run(n, parameters) # does not throw @@ -270,21 +271,22 @@ def test_voltage_sensitivities_with_report(): assert len(report2) > len(report1) def test_voltage_sensitivities_with_deprecated_report(): - report_node = rp.Reporter() - report1 = str(report_node) - assert len(report1) > 0 - n = pp.network.create_eurostag_tutorial_example1_network() - sa = pp.sensitivity.create_ac_analysis() - sa.add_bus_voltage_factor_matrix(['VLGEN_0'], ['GEN']) - sa.run(n, reporter=report_node) - report2 = str(report_node) - assert len(report2) > len(report1) + with pytest.warns(DeprecationWarning, match=re.escape("Use of deprecated attribute reporter. Use report_node instead.")): + report_node = rp.Reporter() + report1 = str(report_node) + assert len(report1) > 0 + n = pp.network.create_eurostag_tutorial_example1_network() + sa = pp.sensitivity.create_ac_analysis() + sa.add_bus_voltage_factor_matrix(['VLGEN_0'], ['GEN']) + sa.run(n, reporter=report_node) + report2 = str(report_node) + assert len(report2) > len(report1) def test_sensitivity_parameters(): n = pp.network.create_eurostag_tutorial_example1_network() analysis = pp.sensitivity.create_ac_analysis() - analysis.set_branch_flow_factor_matrix(['NHV1_NHV2_1'], ['GEN']) + analysis.add_branch_flow_factor_matrix(['NHV1_NHV2_1'], ['GEN']) # 1. distributing on generators parameters = pp.sensitivity.Parameters() @@ -311,7 +313,7 @@ def test_provider_parameters_names(): def test_hvdc(): network = pp.network.create_four_substations_node_breaker_network() analysis = pp.sensitivity.create_dc_analysis() - analysis.set_branch_flow_factor_matrix(["LINE_S2S3"], ["HVDC1"]) + analysis.add_branch_flow_factor_matrix(["LINE_S2S3"], ["HVDC1"]) results = analysis.run(network) assert {'default': ['HVDC1']} == results.function_data_frame_index assert {'default': ['LINE_S2S3']} == results.functions_ids