diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a09c56d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..52d1351 --- /dev/null +++ b/LICENSE @@ -0,0 +1,374 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + 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/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..bb780e3 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,8 @@ +[mypy] +disallow_untyped_defs = True + +[mypy-grid2op.*] +ignore_missing_imports = True + +[mypy-pandapower.*] +ignore_missing_imports = True diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..b3715b3 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,776 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "certifi" +version = "2024.8.30" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "deepdiff" +version = "8.0.1" +description = "Deep Difference and Search of any Python object/data. Recreate objects by adding adding deltas to each other." +optional = false +python-versions = ">=3.8" +files = [ + {file = "deepdiff-8.0.1-py3-none-any.whl", hash = "sha256:42e99004ce603f9a53934c634a57b04ad5900e0d8ed0abb15e635767489cbc05"}, + {file = "deepdiff-8.0.1.tar.gz", hash = "sha256:245599a4586ab59bb599ca3517a9c42f3318ff600ded5e80a3432693c8ec3c4b"}, +] + +[package.dependencies] +orderly-set = "5.2.2" + +[package.extras] +cli = ["click (==8.1.7)", "pyyaml (==6.0.1)"] +optimize = ["orjson"] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "Grid2Op" +version = "1.10.4.dev1" +description = "An gymnasium compatible environment to model sequential decision making for powersystems" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +networkx = ">=2.4" +numpy = "*" +packaging = "*" +pandapower = ">=2.2.2" +pandas = ">=1.0.3" +requests = ">=2.23.0" +scipy = ">=1.4.1" +tqdm = ">=4.45.0" +typing_extensions = "*" + +[package.extras] +api = ["flask", "flask_wtf", "ujson"] +chronix2grid = ["ChroniX2Grid (>=1.2.0.post1)"] +docs = ["autodocsumm (>=0.1.13)", "gym (>=0.17.2)", "gymnasium", "numpydoc (>=0.9.2)", "sphinx (>=2.4.4,<7.0.0)", "sphinx-rtd-theme (>=0.4.3)", "sphinxcontrib-trio (>=1.1.0)"] +gym = ["gym (>=0.17.2)"] +gymnasium = ["gymnasium"] +optional = ["gymnasium", "imageio (>=2.8.0)", "jupyter-client (>=6.1.0)", "jyquickhelper (>=0.3.128)", "lightsim2grid", "matplotlib (>=3.2.1)", "nbformat (>=5.0.4)", "numba (>=0.48.0)", "plotly (>=4.5.4)", "psutil (>=5.7.0)", "pygifsicle (>=1.0.1)", "seaborn (>=0.10.0)"] +plot = ["imageio"] +test = ["gym (>=0.26)", "gymnasium", "gymnasium", "gymnasium", "imageio", "imageio (>=2.8.0)", "jinja2", "jupyter-client (>=6.1.0)", "jyquickhelper (>=0.3.128)", "lightsim2grid", "lightsim2grid", "matplotlib (>=3.2.1)", "nbconvert", "nbformat (>=5.0.4)", "numba", "numba (>=0.48.0)", "plotly (>=4.5.4)", "psutil (>=5.7.0)", "pygifsicle (>=1.0.1)", "seaborn (>=0.10.0)"] + +[package.source] +type = "git" +url = "https://github.com/rte-france/Grid2Op.git" +reference = "dev_1.10.4" +resolved_reference = "72be14eabb3650a5431432dd065398be0ab415fd" + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "mypy" +version = "1.11.2" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, + {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, + {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, + {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, + {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, + {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, + {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, + {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, + {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, + {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, + {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, + {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, + {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, + {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, + {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, + {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, + {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, + {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "networkx" +version = "3.2.1" +description = "Python package for creating and manipulating graphs and networks" +optional = false +python-versions = ">=3.9" +files = [ + {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, + {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, +] + +[package.extras] +default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] +developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] +doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] +extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] + +[[package]] +name = "numpy" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "orderly-set" +version = "5.2.2" +description = "Orderly set" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orderly_set-5.2.2-py3-none-any.whl", hash = "sha256:f7a37c95a38c01cdfe41c3ffb62925a318a2286ea0a41790c057fc802aec54da"}, + {file = "orderly_set-5.2.2.tar.gz", hash = "sha256:52a18b86aaf3f5d5a498bbdb27bf3253a4e5c57ab38e5b7a56fa00115cd28448"}, +] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pandapower" +version = "2.14.11" +description = "An easy to use open source tool for power system modeling, analysis and optimization with a high degree of automation." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pandapower-2.14.11.zip", hash = "sha256:d193499f5ef1100d12b104c9e25cd025bb69d78a6c95b1ab5628b289c0852a8d"}, +] + +[package.dependencies] +deepdiff = "*" +networkx = ">=2.5" +numpy = "<2.0" +packaging = "*" +pandas = ">=1.0" +scipy = "<1.14" +tqdm = "*" + +[package.extras] +all = ["cryptography", "geojson", "geopandas (<1.0)", "igraph", "lightsim2grid (>=0.8.1,<0.9.0)", "matplotlib (<3.9)", "matpowercaseframes", "numba (>=0.25)", "numpydoc", "openpyxl", "ortools", "plotly (>=3.1.1)", "power-grid-model-io", "psycopg2", "pytest (>=8.1,<9.0)", "pytest-xdist", "sphinx", "sphinx_rtd_theme", "xlsxwriter"] +converter = ["matpowercaseframes"] +docs = ["numpydoc", "sphinx", "sphinx_rtd_theme"] +fileio = ["cryptography", "geopandas (<1.0)", "openpyxl", "psycopg2", "xlsxwriter"] +performance = ["lightsim2grid (>=0.8.1,<0.9.0)", "numba", "ortools"] +pgm = ["power-grid-model-io"] +plotting = ["geojson", "geopandas (<1.0)", "igraph", "matplotlib (<3.9)", "plotly"] +test = ["pytest", "pytest-xdist"] + +[[package]] +name = "pandas" +version = "2.2.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "pandas-stubs" +version = "2.2.2.240807" +description = "Type annotations for pandas" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas_stubs-2.2.2.240807-py3-none-any.whl", hash = "sha256:893919ad82be4275f0d07bb47a95d08bae580d3fdea308a7acfcb3f02e76186e"}, + {file = "pandas_stubs-2.2.2.240807.tar.gz", hash = "sha256:64a559725a57a449f46225fbafc422520b7410bff9252b661a225b5559192a93"}, +] + +[package.dependencies] +numpy = ">=1.23.5" +types-pytz = ">=2022.1.1" + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "prettytable" +version = "3.11.0" +description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" +optional = false +python-versions = ">=3.8" +files = [ + {file = "prettytable-3.11.0-py3-none-any.whl", hash = "sha256:aa17083feb6c71da11a68b2c213b04675c4af4ce9c541762632ca3f2cb3546dd"}, + {file = "prettytable-3.11.0.tar.gz", hash = "sha256:7e23ca1e68bbfd06ba8de98bf553bf3493264c96d5e8a615c0471025deeba722"}, +] + +[package.dependencies] +wcwidth = "*" + +[package.extras] +tests = ["pytest", "pytest-cov", "pytest-lazy-fixtures"] + +[[package]] +name = "pypowsybl" +version = "1.8.0.dev1" +description = "A PowSyBl Python API" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +networkx = "*" +pandas = {version = ">=2.2.2", markers = "python_version >= \"3.9\""} +prettytable = ">=2.0.0" + +[package.extras] +extra = ["pandapower (>=2.4.11)"] + +[package.source] +type = "git" +url = "https://github.com/powsybl/pypowsybl.git" +reference = "pandapower_converter" +resolved_reference = "cca89c313443c1bbc578d692a616f8c9ae7f4e7c" + +[[package]] +name = "pytest" +version = "8.3.3" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2024.2" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "scipy" +version = "1.13.1" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, + {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, + {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, + {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, + {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, + {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, + {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, + {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, + {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, + {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, + {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, +] + +[package.dependencies] +numpy = ">=1.22.4,<2.3" + +[package.extras] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] +test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "tomli" +version = "2.0.2" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, +] + +[[package]] +name = "tqdm" +version = "4.66.5" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, + {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "types-pytz" +version = "2024.2.0.20241003" +description = "Typing stubs for pytz" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-pytz-2024.2.0.20241003.tar.gz", hash = "sha256:575dc38f385a922a212bac00a7d6d2e16e141132a3c955078f4a4fd13ed6cb44"}, + {file = "types_pytz-2024.2.0.20241003-py3-none-any.whl", hash = "sha256:3e22df1336c0c6ad1d29163c8fda82736909eb977281cb823c57f8bae07118b7"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "tzdata" +version = "2024.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "6621601fa7d40a47f69638888f16223128bb732cf8bf1d6a31955c8661594409" diff --git a/pypowsybl2grid/__init__.py b/pypowsybl2grid/__init__.py new file mode 100644 index 0000000..df799bf --- /dev/null +++ b/pypowsybl2grid/__init__.py @@ -0,0 +1 @@ +from .pypowsybl_backend import PyPowSyBlBackend diff --git a/pypowsybl2grid/fast_network_cache.py b/pypowsybl2grid/fast_network_cache.py new file mode 100644 index 0000000..a58daef --- /dev/null +++ b/pypowsybl2grid/fast_network_cache.py @@ -0,0 +1,333 @@ +import logging +from typing import Dict, Any, List, Tuple, Optional + +import pandas as pd +import pypowsybl as pp +from pandas import DataFrame + +from pypowsybl2grid.network_cache import NetworkCache, NetworkCacheFactory, DEFAULT_LF_PARAMETERS + +logger = logging.getLogger(__name__) + + +class FastNetworkCache(NetworkCache): + BUS_STATE_ATTRIBUTES = ['v_mag'] + INJECTION_STATE_ATTRIBUTES = ['p', 'q'] + BRANCH_STATE_ATTRIBUTES = ['p1', 'q1', 'i1', 'p2', 'q2', 'i2'] + BUS_TOPO_ATTRIBUTES = ['synchronous_component'] + MERGE_BUS_TOPO_ATTRIBUTES = ['synchronous_component', 'num'] + INJECTION_TOPO_ATTRIBUTES = ['bus_breaker_bus_id', 'connected'] + BRANCH_TOPO_ATTRIBUTES = ['bus_breaker_bus1_id', 'connected1', 'bus_breaker_bus2_id', 'connected2'] + + def __init__(self, network: pp.network.Network, lf_parameters: pp.loadflow.Parameters): + super().__init__(network, lf_parameters) + self._fetch_switches() + self._fetch_voltage_levels() + self._fetch_buses() + self._fetch_loads() + self._fetch_generators() + self._fetch_shunts() + self._fetch_batteries() + self._fetch_branches() + self._fetch_branches_limits() + + def _fetch_switches(self) -> None: + self._switches = self._network.get_switches(attributes=NetworkCache.SWITCH_ATTRIBUTES) + + def _fetch_voltage_levels(self) -> None: + self._voltage_levels = self._network.get_voltage_levels(attributes=NetworkCache.VOLTAGE_LEVEL_ATTRIBUTES) + self._voltage_levels['num'] = range(len(self._voltage_levels)) # add numbering + + def _number_buses(self, buses: DataFrame) -> DataFrame: + numbered_buses = buses.merge(self._voltage_levels.rename(columns=lambda x: x + '_voltage_level'), + left_on='voltage_level_id', right_index=True, how='outer') + numbered_buses['local_num'] = numbered_buses.groupby('voltage_level_id').cumcount() + numbered_buses['num'] = numbered_buses['num_voltage_level'] + numbered_buses['local_num'] * len(self._voltage_levels) + return numbered_buses + + def _fetch_buses(self) -> None: + self._buses = self._network.get_bus_breaker_view_buses(attributes=NetworkCache.BUS_ATTRIBUTES) + # !!!! there is a precise way to create buses global numbering (see grid2op methods to convert from local to global num) + # global_num = substation_num + local_num * bus_per_substation count + # So given 2 voltage levels with 2 buses each, local and global number should be: + # + # voltage_level_id bus_id local_num global_num + # VL1 B1 0 0 + # VL1 B2 1 2 + # VL2 B3 0 1 + # VL2 B4 1 3 + self._buses = self._number_buses(self._buses) + self._buses_dict = self._buses['num'].to_dict() + self._buses_dict = {v: k for k, v in self._buses_dict.items()} + + def _fetch_injections(self, injections: DataFrame) -> DataFrame: + injections['num'] = range(len(injections)) # add numbering + buses_min = self._buses[['v_mag', 'synchronous_component', 'local_num', 'num']] + return injections.merge( + buses_min.rename(columns=lambda x: x + '_bus'), right_index=True, + left_on='bus_breaker_bus_id', how='left') + + def _fetch_loads(self) -> None: + self._loads = self._fetch_injections(self._network.get_loads(attributes=NetworkCache.INJECTION_ATTRIBUTES)) + + def _fetch_generators(self) -> None: + self._generators = self._fetch_injections( + self._network.get_generators(attributes=NetworkCache.INJECTION_ATTRIBUTES)) + + def _fetch_shunts(self) -> None: + self._shunts = self._fetch_injections( + self._network.get_shunt_compensators(attributes=NetworkCache.INJECTION_ATTRIBUTES)) + + def _fetch_batteries(self) -> None: + self._batteries = self._fetch_injections( + self._network.get_batteries(attributes=NetworkCache.INJECTION_ATTRIBUTES)) + + def _fetch_branches(self) -> None: + lines = self._network.get_lines(attributes=NetworkCache.BRANCH_ATTRIBUTES) + transformers = self._network.get_2_windings_transformers(attributes=NetworkCache.BRANCH_ATTRIBUTES) + lines['num'] = range(len(lines)) # add numbering + transformers['num'] = range(len(lines), len(lines) + len(transformers)) # numbering starting from last line num + # FIXME support 3 windings transformers + branches = pd.concat([lines, transformers], axis=0) + buses_min = self._buses[['v_mag', 'synchronous_component', 'num']] + self._branches = ( + branches.merge(buses_min.rename(columns=lambda x: x + '_bus1'), right_index=True, + left_on='bus_breaker_bus1_id', + how='left') + .merge(buses_min.rename(columns=lambda x: x + '_bus2'), right_index=True, left_on='bus_breaker_bus2_id', + how='left')) + + def _fetch_branches_limits(self) -> None: + operational_limits = self._network.get_operational_limits( + attributes=['element_type', 'type', 'value', 'acceptable_duration']) + # FIXME also get other limit type + current_limits = operational_limits[(operational_limits['type'] == 'CURRENT') & ( + operational_limits['acceptable_duration'] == -1)] # only keep permanent limit + current_limits = current_limits.groupby('element_id').agg( + {'value': 'max'}).reset_index() # get side 1 and 2 max one + self._branches_limits = self._branches.merge(current_limits, left_index=True, right_on='element_id', + how='outer') + self._branches_limits = self._branches_limits.fillna(888888) # replace missing limits by a very high one + + @staticmethod + def _update(initial_df: DataFrame, update_df: DataFrame) -> None: + # is this really efficient ? + # update and combine_first does not work as it ignore nan and none of the updated dataframe + for col in update_df.columns: + initial_df.loc[update_df.index, col] = update_df[col] + + @staticmethod + def _fetch_injections_state(injections: DataFrame, buses_state: DataFrame, injections_state: DataFrame) -> None: + # update columns coming from bus state dataframe join + injections_merged_with_buses_state = injections[['bus_breaker_bus_id']].merge( + buses_state.rename(columns=lambda x: x + '_bus'), right_index=True, + left_on='bus_breaker_bus_id', how='left') + FastNetworkCache._update(injections, injections_merged_with_buses_state) + + # update injection state columns + FastNetworkCache._update(injections, injections_state) + + def _fetch_full_state(self) -> None: + # update buses state columns + buses_state = self._network.get_bus_breaker_view_buses(attributes=FastNetworkCache.BUS_STATE_ATTRIBUTES) + self._buses.update(buses_state) + + # update injections state + self._fetch_injections_state(self._loads, buses_state, + self._network.get_loads(attributes=FastNetworkCache.INJECTION_STATE_ATTRIBUTES)) + self._fetch_injections_state(self._generators, buses_state, self._network.get_generators( + attributes=FastNetworkCache.INJECTION_STATE_ATTRIBUTES)) + self._fetch_injections_state(self._shunts, buses_state, self._network.get_shunt_compensators( + attributes=FastNetworkCache.INJECTION_STATE_ATTRIBUTES)) + self._fetch_injections_state(self._batteries, buses_state, + self._network.get_batteries( + attributes=FastNetworkCache.INJECTION_STATE_ATTRIBUTES)) + + # update columns coming from bus state dataframe joins + branches_merged_with_buses_state = self._branches[['bus_breaker_bus1_id', 'bus_breaker_bus2_id']].merge( + buses_state.rename(columns=lambda x: x + '_bus1'), right_index=True, + left_on='bus_breaker_bus1_id', how='left').merge( + buses_state.rename(columns=lambda x: x + '_bus2'), right_index=True, + left_on='bus_breaker_bus2_id', how='left') + FastNetworkCache._update(self._branches, branches_merged_with_buses_state) + + # update branches state columns + # FIXME support 3 windings transformers + branches_state = self._network.get_branches(attributes=FastNetworkCache.BRANCH_STATE_ATTRIBUTES) + FastNetworkCache._update(self._branches, branches_state) + + def reset_retained_switches(self) -> None: + logger.info("Reset all retained switches") + self._network.update_switches(id=self._switches.index, retained=[False] * len(self._switches)) + + def get_voltage_levels(self) -> pd.DataFrame: + return self._voltage_levels + + def get_buses(self) -> Tuple[pd.DataFrame, Dict[int, str]]: + return self._buses, self._buses_dict + + def get_loads(self) -> pd.DataFrame: + return self._loads + + def get_generators(self) -> pd.DataFrame: + return self._generators + + def get_shunts(self) -> pd.DataFrame: + return self._shunts + + def get_batteries(self) -> pd.DataFrame: + return self._batteries + + def get_branches(self) -> pd.DataFrame: + return self._branches + + def get_branches_with_limits(self) -> pd.DataFrame: + return self._branches_limits + + def get_switches(self) -> pd.DataFrame: + switches = self._network.get_switches(attributes=NetworkCache.SWITCH_ATTRIBUTES) + return switches[switches['retained']] + + def run_dc_pf(self) -> List[pp.loadflow.ComponentResult]: + result = super().run_dc_pf() + self._fetch_full_state() + return result + + def run_ac_pf(self) -> List[pp.loadflow.ComponentResult]: + result = super().run_ac_pf() + self._fetch_full_state() + return result + + def create_buses(self, df: Optional[DataFrame] = None, **kwargs: Dict[str, Any]) -> None: + self._network.create_buses(df, **kwargs) + self._fetch_buses() + + def _fetch_injection_topo(self, injections: DataFrame, injections_topo: DataFrame, buses_topo: DataFrame) -> None: + FastNetworkCache._update(injections, injections_topo) + + # update columns coming from bus topo dataframe join + injections_merged_with_buses_topo = injections[['bus_breaker_bus_id']].merge( + buses_topo.rename(columns=lambda x: x + '_bus'), right_index=True, + left_on='bus_breaker_bus_id', how='left') + FastNetworkCache._update(injections, injections_merged_with_buses_topo) + + def _fetch_load_topo(self, iidm_id: str) -> None: + self._fetch_bus_topo() + buses_topo = self._buses[FastNetworkCache.MERGE_BUS_TOPO_ATTRIBUTES] + self._fetch_injection_topo(self._loads, self._network.get_loads(id=iidm_id, + attributes=FastNetworkCache.INJECTION_TOPO_ATTRIBUTES), + buses_topo) + + def disconnect_load(self, iidm_id: str) -> None: + self._network.update_loads(id=iidm_id, connected=False) + self._fetch_load_topo(iidm_id) + + def connected_load(self, iidm_id: str, new_bus_id: str) -> None: + self._network.update_loads(id=iidm_id, bus_breaker_bus_id=new_bus_id, connected=True) + self._fetch_load_topo(iidm_id) + + def _fetch_generator_topo(self, iidm_id: str) -> None: + self._fetch_bus_topo() + buses_topo = self._buses[FastNetworkCache.MERGE_BUS_TOPO_ATTRIBUTES] + self._fetch_injection_topo(self._generators, self._network.get_generators(id=iidm_id, + attributes=FastNetworkCache.INJECTION_TOPO_ATTRIBUTES), + buses_topo) + + def disconnect_generator(self, iidm_id: str) -> None: + self._network.update_generators(id=iidm_id, connected=False) + self._fetch_generator_topo(iidm_id) + + def connected_generator(self, iidm_id: str, new_bus_id: str) -> None: + self._network.update_generators(id=iidm_id, bus_breaker_bus_id=new_bus_id, connected=True) + self._fetch_generator_topo(iidm_id) + + def _fetch_shunt_topo(self, iidm_id: str) -> None: + self._fetch_bus_topo() + buses_topo = self._buses[FastNetworkCache.MERGE_BUS_TOPO_ATTRIBUTES] + self._fetch_injection_topo(self._shunts, self._network.get_shunt_compensators(id=iidm_id, + attributes=FastNetworkCache.INJECTION_TOPO_ATTRIBUTES), + buses_topo) + + def disconnect_shunt(self, iidm_id: str) -> None: + self._network.update_shunt_compensators(id=iidm_id, connected=False) + self._fetch_shunt_topo(iidm_id) + + def connected_shunt(self, iidm_id: str, new_bus_id: str) -> None: + self._network.update_shunt_compensators(id=iidm_id, bus_breaker_bus_id=new_bus_id, connected=True) + self._fetch_shunt_topo(iidm_id) + + def _fetch_bus_topo(self) -> None: + buses_topo_update = self._network.get_bus_breaker_view_buses(attributes=FastNetworkCache.BUS_TOPO_ATTRIBUTES) + FastNetworkCache._update(self._buses, buses_topo_update) + + def _fetch_branch_topo(self) -> None: + # update buses because of potential synchronous component update + self._fetch_bus_topo() + buses_topo = self._buses[FastNetworkCache.MERGE_BUS_TOPO_ATTRIBUTES] + + # we need to update connectivity of all branches and injections + # (potentially indirectly lost by the branch topo change) + branches_topo = self._network.get_branches(attributes=FastNetworkCache.BRANCH_TOPO_ATTRIBUTES) + branches_merged_with_buses_topo = branches_topo.merge( + buses_topo.rename(columns=lambda x: x + '_bus1'), right_index=True, + left_on='bus_breaker_bus1_id', how='left') + branches_merged_with_buses_topo = branches_merged_with_buses_topo.merge( + buses_topo.rename(columns=lambda x: x + '_bus2'), right_index=True, + left_on='bus_breaker_bus2_id', how='left') + FastNetworkCache._update(self._branches, branches_merged_with_buses_topo) + + self._fetch_injection_topo(self._loads, + self._network.get_loads(attributes=FastNetworkCache.INJECTION_TOPO_ATTRIBUTES), + buses_topo) + self._fetch_injection_topo(self._generators, + self._network.get_generators(attributes=FastNetworkCache.INJECTION_TOPO_ATTRIBUTES), + buses_topo) + self._fetch_injection_topo(self._shunts, + self._network.get_shunt_compensators(attributes=FastNetworkCache.INJECTION_TOPO_ATTRIBUTES), + buses_topo) + + def disconnect_branch_side1(self, iidm_id: str) -> None: + self._network.update_branches(id=iidm_id, connected1=False) + self._fetch_branch_topo() + + def connect_branch_side1(self, iidm_id: str, new_bus_id: str) -> None: + self._network.update_branches(id=iidm_id, bus_breaker_bus1_id=new_bus_id, connected1=True) + self._fetch_branch_topo() + + def disconnect_branch_side2(self, iidm_id: str) -> None: + self._network.update_branches(id=iidm_id, connected2=False) + self._fetch_branch_topo() + + def connect_branch_side2(self, iidm_id: str, new_bus_id: str) -> None: + self._network.update_branches(id=iidm_id, bus_breaker_bus2_id=new_bus_id, connected2=True) + self._fetch_branch_topo() + + def update_load_p(self, iidm_id: str, new_p: float) -> None: + self._network.update_loads(id=iidm_id, p0=new_p) + # not need to update until LF ran again + + def update_load_q(self, iidm_id: str, new_q: float) -> None: + self._network.update_loads(id=iidm_id, q0=new_q) + # not need to update until LF ran again + + def update_generator_p(self, iidm_id: str, new_p: float) -> None: + self._network.update_generators(id=iidm_id, target_p=new_p) + # not need to update until LF ran again + + def update_generator_v(self, iidm_id: str, new_v: float) -> None: + self._network.update_generators(id=iidm_id, target_v=new_v) + # not need to update until LF ran again + + def update_shunt_p(self, iidm_id: str, new_p: float) -> None: + # FIXME how to deal with discrete shunts? + pass + + def update_shunt_q(self, iidm_id: str, new_q: float) -> None: + # FIXME how to deal with discrete shunts? + pass + + +class FastNetworkCacheFactory(NetworkCacheFactory): + + def create_network_cache(self, network: pp.network.Network, lf_parameters: pp.loadflow.Parameters = DEFAULT_LF_PARAMETERS) -> NetworkCache: + return FastNetworkCache(network, lf_parameters) diff --git a/pypowsybl2grid/network_cache.py b/pypowsybl2grid/network_cache.py new file mode 100644 index 0000000..f759358 --- /dev/null +++ b/pypowsybl2grid/network_cache.py @@ -0,0 +1,147 @@ +import logging +from abc import ABC, abstractmethod +from typing import Dict, Any, Tuple, List, Optional + +import pandas as pd +import pypowsybl as pp +from pandas import DataFrame + +logger = logging.getLogger(__name__) + +DEFAULT_LF_PARAMETERS = pp.loadflow.Parameters(voltage_init_mode=pp.loadflow.VoltageInitMode.DC_VALUES) + +class NetworkCache(ABC): + VOLTAGE_LEVEL_ATTRIBUTES = ['name', 'topology_kind'] + BUS_ATTRIBUTES = ['v_mag', 'synchronous_component', 'voltage_level_id'] + INJECTION_ATTRIBUTES = ['name', 'voltage_level_id', 'bus_breaker_bus_id', 'connected', 'p', 'q'] + BRANCH_ATTRIBUTES = ['name', 'voltage_level1_id', 'voltage_level2_id', 'bus_breaker_bus1_id', 'bus_breaker_bus2_id', + 'connected1', 'connected2', 'p1', 'q1', 'i1', 'p2', 'q2', 'i2'] + SWITCH_ATTRIBUTES = ['open', 'retained'] + + def __init__(self, network: pp.network.Network, lf_parameters: pp.loadflow.Parameters): + self._network = network + self._lf_parameters = lf_parameters + + def get_id(self) -> str: + return self._network.id + + @abstractmethod + def reset_retained_switches(self) -> None: + pass + + @abstractmethod + def get_voltage_levels(self) -> pd.DataFrame: + pass + + @abstractmethod + def get_buses(self) -> Tuple[pd.DataFrame, Dict[int, str]]: + pass + + @abstractmethod + def get_loads(self) -> pd.DataFrame: + pass + + @abstractmethod + def get_generators(self) -> pd.DataFrame: + pass + + @abstractmethod + def get_shunts(self) -> pd.DataFrame: + pass + + @abstractmethod + def get_batteries(self) -> pd.DataFrame: + pass + + @abstractmethod + def get_branches(self) -> pd.DataFrame: + pass + + @abstractmethod + def get_branches_with_limits(self) -> pd.DataFrame: + pass + + @abstractmethod + def get_switches(self) -> pd.DataFrame: + pass + + def run_dc_pf(self) -> List[pp.loadflow.ComponentResult]: + return pp.loadflow.run_dc(self._network) + + def run_ac_pf(self) -> List[pp.loadflow.ComponentResult]: + return pp.loadflow.run_ac(self._network, self._lf_parameters) + + @abstractmethod + def create_buses(self, df: Optional[DataFrame] = None, **kwargs: Dict[str, Any]) -> None: + pass + + @abstractmethod + def disconnect_load(self, iidm_id: str) -> None: + pass + + @abstractmethod + def connected_load(self, iidm_id: str, new_bus_id: str) -> None: + pass + + @abstractmethod + def disconnect_generator(self, iidm_id: str) -> None: + pass + + @abstractmethod + def connected_generator(self, iidm_id: str, new_bus_id: str) -> None: + pass + + @abstractmethod + def disconnect_shunt(self, iidm_id: str) -> None: + pass + + @abstractmethod + def connected_shunt(self, iidm_id: str, new_bus_id: str) -> None: + pass + + @abstractmethod + def disconnect_branch_side1(self, iidm_id: str) -> None: + pass + + @abstractmethod + def connect_branch_side1(self, iidm_id: str, new_bus_id: str) -> None: + pass + + @abstractmethod + def disconnect_branch_side2(self, iidm_id: str) -> None: + pass + + @abstractmethod + def connect_branch_side2(self, iidm_id: str, new_bus_id: str) -> None: + pass + + @abstractmethod + def update_load_p(self, iidm_id: str, new_p: float) -> None: + pass + + @abstractmethod + def update_load_q(self, iidm_id: str, new_q: float) -> None: + pass + + @abstractmethod + def update_generator_p(self, iidm_id: str, new_p: float) -> None: + pass + + @abstractmethod + def update_generator_v(self, iidm_id: str, new_v: float) -> None: + pass + + @abstractmethod + def update_shunt_p(self, iidm_id: str, new_p: float) -> None: + pass + + @abstractmethod + def update_shunt_q(self, iidm_id: str, new_q: float) -> None: + pass + + +class NetworkCacheFactory(ABC): + + @abstractmethod + def create_network_cache(self, network: pp.network.Network, lf_parameters: pp.loadflow.Parameters = DEFAULT_LF_PARAMETERS) -> NetworkCache: + pass diff --git a/pypowsybl2grid/pypowsybl_backend.py b/pypowsybl2grid/pypowsybl_backend.py new file mode 100644 index 0000000..c30cf32 --- /dev/null +++ b/pypowsybl2grid/pypowsybl_backend.py @@ -0,0 +1,319 @@ +import logging +import os +from typing import Optional, Tuple, Union + +import grid2op +import numpy as np +import pandapower as pdp +import pandas as pd +import pypowsybl as pp +from grid2op.Backend import Backend +from grid2op.Exceptions import DivergingPowerflow +from pandas import DataFrame +from pypowsybl import PyPowsyblError + +from pypowsybl2grid.fast_network_cache import FastNetworkCache +from pypowsybl2grid.network_cache import DEFAULT_LF_PARAMETERS + +logger = logging.getLogger(__name__) + +class PyPowSyBlBackend(Backend): + + def __init__(self, + detailed_infos_for_cascading_failures=False, + can_be_copied: bool = True, + check_isolated_and_disconnected_injections = True, + lf_parameters: pp.loadflow.Parameters = DEFAULT_LF_PARAMETERS): + Backend.__init__(self, + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, + can_be_copied=can_be_copied) + self._check_isolated_and_disconnected_injections = check_isolated_and_disconnected_injections + self._detailed_infos_for_cascading_failures = detailed_infos_for_cascading_failures + self._lf_parameters = lf_parameters + self.shunts_data_available = True + self.supported_grid_format = ("json", "xiidm", "txt") # FIXME dynamically get supported extensions + + @staticmethod + def create_name(df: DataFrame) -> np.ndarray: + return np.where(df['name'].eq(''), df.index.to_series(), df['name']) + + def load_grid(self, + path: Union[os.PathLike, str], + filename: Optional[Union[os.PathLike, str]] = None) -> None: + # load network + full_path = self.make_complete_path(path, filename) + + if full_path.endswith('.json'): + n_pdp = pdp.from_json(full_path) + n = pp.network.convert_from_pandapower(n_pdp) + else: + n = pp.network.load(full_path) + self._network = FastNetworkCache(n, self._lf_parameters) + + # remove all retained switches + # TODO provide a way to define retained switches + self._network.reset_retained_switches() + + # substations mapped to IIDM voltage levels + voltage_levels = self._network.get_voltage_levels() + self.n_sub = len(voltage_levels) + self.name_sub = self.create_name(voltage_levels) + + self.can_handle_more_than_2_busbar() + + # only one value for n_busbar_per_sub is allowed => use maximum one across all voltage levels + buses, _ = self._network.get_buses() + max_bus_count = int(buses['local_num'].max()) + 1 + if max_bus_count == 1: + # this is a synthetic (like ieee) network + + # also check all voltage levels have a bus/breaker topo. + # it would be suspect to have a real node/breaker network with only 1 possible bus for all its voltage levels + if not (voltage_levels['topology_kind'] == 'BUS_BREAKER').all(): + raise PyPowsyblError("pandapower is not installed") + + # we create other busbars for all voltage levels + for i in range(self.n_busbar_per_sub - 1): + self._network.create_buses(id=voltage_levels.index + '_extra_busbar_' + str(i + 1), + voltage_level_id=voltage_levels.index) + else: + # TODO + # we have a real network so we should not create extra busbars but we should probably + # - as grid2op only allow to have same number of busbar section for all substations we need to set the n_busnbar_per_substation + # to max voltage level one and handle an error when environnement ask the back to connect to a not existing busbar + # - for a node/breaker network this is even more complex as we whould be able to map a topology to a set of switch + # to action and also handle not existing configuration + self.n_busbar_per_sub = max_bus_count + + logger.info(f"{self.n_busbar_per_sub} busbars per substation") + + # loads + loads = self._network.get_loads() + self.n_load = len(loads) + self.name_load = self.create_name(loads) + self.load_to_subid = np.zeros(self.n_load, dtype=int) + for _, row in loads.iterrows(): + self.load_to_subid[row.num] = voltage_levels.loc[row.voltage_level_id, "num"] + + # generators + generators = self._network.get_generators() + self.n_gen = len(generators) + self.name_gen = self.create_name(generators) + self.gen_to_subid = np.zeros(self.n_gen, dtype=int) + for _, row in generators.iterrows(): + self.gen_to_subid[row.num] = voltage_levels.loc[row.voltage_level_id, "num"] + + # shunts + shunts = self._network.get_shunts() + self.n_shunt = len(shunts) + self.name_shunt = self.create_name(shunts) + self.shunt_to_subid = np.zeros(self.n_shunt, dtype=int) + for _, row in shunts.iterrows(): + self.shunt_to_subid[row.num] = voltage_levels.loc[row.voltage_level_id, "num"] + + # batteries + self.set_no_storage() + # FIXME implement batteries + # batteries = self._network.get_batteries() + # self.n_storage = len(batteries) + # self.name_storage = np.array(batteries.index) + # self.storage_type = np.full(self.n_storage, fill_value="???") + # self.storage_to_subid = np.zeros(self.n_storage, dtype=int) + # for index, row in batteries.iterrows(): + # self.storage_to_subid[row.num] = voltage_levels.loc[row.voltage_level_id, "num"] + + # lines and transformers + branches = self._network.get_branches() + self.n_line = len(branches) + self.name_line = self.create_name(branches) + self.line_or_to_subid = np.zeros(self.n_line, dtype=int) + self.line_ex_to_subid = np.zeros(self.n_line, dtype=int) + for _, row in branches.iterrows(): + self.line_or_to_subid[row.num] = voltage_levels.loc[row.voltage_level1_id, "num"] + self.line_ex_to_subid[row.num] = voltage_levels.loc[row.voltage_level2_id, "num"] + + self._compute_pos_big_topo() + + # thermal limits + self.thermal_limit_a = np.zeros(self.n_line, dtype=int) + for _, row in self._network.get_branches_with_limits().iterrows(): + self.thermal_limit_a[row.num] = row.value + + switches = self._network.get_switches() + logger.info(f"Network '{self._network.get_id()}' loaded with {len(switches)} retained switches: {len(buses)} buses, {len(branches)} branches, {len(generators)} generators, {len(loads)} loads, {len(shunts)} shunts") + + def apply_action(self, backend_action: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: + # the following few lines are highly recommended + if backend_action is None: + return + + # active and reactive power of loads + loads = self._network.get_loads() + for load_id, new_p in backend_action.load_p: + iidm_id = loads.iloc[load_id].name + self._network.update_load_p(iidm_id, new_p) + + for load_id, new_q in backend_action.load_q: + iidm_id = loads.iloc[load_id].name + self._network.update_load_q(iidm_id, new_q) + + # active power and voltage target of generators + generators = self._network.get_generators() + for gen_id, new_p in backend_action.prod_p: + iidm_id = generators.iloc[gen_id].name + self._network.update_generator_p(iidm_id, new_p) + + for gen_id, new_v in backend_action.prod_v: + iidm_id = generators.iloc[gen_id].name + self._network.update_generator_v(iidm_id, new_v) + + # active and reactive power of shunts + shunts = self._network.get_shunts() + for shunt_id, new_p in backend_action.shunt_p: + iidm_id = shunts.iloc[shunt_id].name + self._network.update_shunt_p(iidm_id, new_p) + + for shunt_id, new_q in backend_action.shunt_q: + iidm_id = shunts.iloc[shunt_id].name + self._network.update_shunt_q(iidm_id, new_q) + + # loads bus connection + _, buses_dict = self._network.get_buses() + loads_bus = backend_action.get_loads_bus_global() + for load_id, new_bus in loads_bus: + iidm_id = loads.iloc[load_id].name + if new_bus == -1: + self._network.disconnect_load(iidm_id) + else: + new_bus_id = buses_dict[new_bus] + self._network.connected_load(iidm_id, new_bus_id) + + # generators bus connection + generators_bus = backend_action.get_gens_bus_global() + for gen_id, new_bus in generators_bus: + iidm_id = generators.iloc[gen_id].name + if new_bus == -1: + self._network.disconnect_generator(iidm_id) + else: + new_bus_id = buses_dict[new_bus] + self._network.connected_generator(iidm_id, new_bus_id) + + # shunts bus connection + shunts_bus = backend_action.get_shunts_bus_global() + for shunt_id, new_bus in shunts_bus: + iidm_id = shunts.iloc[shunt_id].name + if new_bus == -1: + self._network.disconnect_shunt(iidm_id) + else: + new_bus_id = buses_dict[new_bus] + self._network.connected_shunt(iidm_id, new_bus_id) + + # lines origin bus connection + branches = self._network.get_branches() + lines_or_bus = backend_action.get_lines_or_bus_global() + for line_id, new_bus in lines_or_bus: + iidm_id = branches.iloc[line_id].name + if new_bus == -1: + self._network.disconnect_branch_side1(iidm_id) + else: + new_bus_id = buses_dict[new_bus] + self._network.connect_branch_side1(iidm_id, new_bus_id) + + # lines extremity bus connection + lines_ex_bus = backend_action.get_lines_ex_bus_global() + for line_id, new_bus in lines_ex_bus: + iidm_id = branches.iloc[line_id].name + if new_bus == -1: + self._network.disconnect_branch_side2(iidm_id) + else: + new_bus_id = buses_dict[new_bus] + self._network.connect_branch_side2(iidm_id, new_bus_id) + + def _check_isolated_injections(self) -> bool: + loads = self._network.get_loads() + if (loads['synchronous_component_bus'] > 0).any(): + return True + if (~loads['connected']).any(): + return True + generators = self._network.get_generators() + if (generators['synchronous_component_bus'] > 0).any(): + return True + if (~generators['connected']).any(): + return True + shunts = self._network.get_shunts() + if (shunts['synchronous_component_bus'] > 0).any(): + return True + return False + + @staticmethod + def _is_converged(result: pp.loadflow.ComponentResult) -> bool: + return result.status == pp.loadflow.ComponentStatus.CONVERGED or result.status == pp.loadflow.ComponentStatus.NO_CALCULATION + + def runpf(self, is_dc: bool = False) -> Tuple[bool, Union[Exception, None]]: + if self._check_isolated_and_disconnected_injections and self._check_isolated_injections(): + converged = False + else: + if is_dc: + results = self._network.run_dc_pf() + else: + results = self._network.run_ac_pf() + converged = self._is_converged(results[0]) + + return converged, None if converged else DivergingPowerflow() # FIXME this unusual as an API to require passing an exception as a return type + + def _update_topo_vect(self, res, df: pd.DataFrame, pos_topo_vect, bus_breaker_bus_id_attr: str, + connected_attr: str, num_bus_attr: str) -> None: + for _, row in df.iterrows(): + my_pos_topo_vect = pos_topo_vect[row['num']] + if row[bus_breaker_bus_id_attr] and row[connected_attr]: + local_bus = self.global_bus_to_local_int(row[num_bus_attr], my_pos_topo_vect) + else: + local_bus = -1 + res[my_pos_topo_vect] = local_bus + + def get_topo_vect(self) -> np.ndarray: + res = np.full(self.dim_topo, fill_value=-2, dtype=int) + self._update_topo_vect(res, self._network.get_loads(), self.load_pos_topo_vect, 'bus_breaker_bus_id', + 'connected', 'num_bus') + self._update_topo_vect(res, self._network.get_generators(), self.gen_pos_topo_vect, 'bus_breaker_bus_id', + 'connected', 'num_bus') + # FIXME why no shunt_pos_topo_vect ? + branches = self._network.get_branches() + self._update_topo_vect(res, branches, self.line_or_pos_topo_vect, 'bus_breaker_bus1_id', 'connected1', + 'num_bus1') + self._update_topo_vect(res, branches, self.line_ex_pos_topo_vect, 'bus_breaker_bus2_id', 'connected2', + 'num_bus2') + return res + + def _injections_info(self, df: pd.DataFrame, sign: float) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + p = np.nan_to_num(np.where(df['connected'], df['p'], 0)) * sign + q = np.nan_to_num(np.where(df['connected'], df['q'], 0)) * sign + v = np.nan_to_num(np.array(np.where(df['connected'], df['v_mag_bus'], 0))) + bus = np.array(np.where(df['connected'], df['local_num_bus'] + 1, -1)) # local bus number should start at 1... + return p, q, v, bus + + def generators_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + p, q, v, _ = self._injections_info(self._network.get_generators(), -1.0) # load convention expected + return p, q, v + + def loads_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + p, q, v, _ = self._injections_info(self._network.get_loads(), 1.0) + return p, q, v + + def shunt_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + return self._injections_info(self._network.get_shunts(), 1.0) + + def _lines_info(self, p_attr: str, q_attr: str, a_attr: str, v_attr: str, connected_attr: str) -> Tuple[ + np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + branches = self._network.get_branches() + p = np.nan_to_num(np.where(branches[connected_attr], branches[p_attr], 0)) + q = np.nan_to_num(np.where(branches[connected_attr], branches[q_attr], 0)) + a = np.nan_to_num(np.where(branches[connected_attr], branches[a_attr], 0)) + v = np.nan_to_num(np.where(branches[connected_attr], branches[v_attr], 0)) + return p, q, v, a + + def lines_or_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + return self._lines_info('p1', 'q1', 'i1', 'v_mag_bus1', 'connected1') + + def lines_ex_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + return self._lines_info('p2', 'q2', 'i2', 'v_mag_bus2', 'connected2') diff --git a/pypowsybl2grid/simple_network_cache.py b/pypowsybl2grid/simple_network_cache.py new file mode 100644 index 0000000..09f7226 --- /dev/null +++ b/pypowsybl2grid/simple_network_cache.py @@ -0,0 +1,156 @@ +import logging +from typing import Dict, Any, Tuple, Optional + +import pandas as pd +import pypowsybl as pp +from pandas import DataFrame + +from pypowsybl2grid.network_cache import NetworkCache, NetworkCacheFactory, DEFAULT_LF_PARAMETERS + +logger = logging.getLogger(__name__) + +class SimpleNetworkCache(NetworkCache): + + def __init__(self, network: pp.network.Network, lf_parameters: pp.loadflow.Parameters): + super().__init__(network, lf_parameters) + + def reset_retained_switches(self) -> None: + logger.info("Reset all retained switches") + switches = self._network.get_switches(attributes=NetworkCache.SWITCH_ATTRIBUTES) + self._network.update_switches(id=switches.index, retained=[False] * len(switches)) + + def get_voltage_levels(self) -> pd.DataFrame: + voltage_levels = self._network.get_voltage_levels(attributes=NetworkCache.VOLTAGE_LEVEL_ATTRIBUTES) + voltage_levels['num'] = range(len(voltage_levels)) # add numbering + return voltage_levels + + def get_buses(self) -> Tuple[pd.DataFrame, Dict[int, str]]: + buses = self._network.get_bus_breaker_view_buses(attributes=NetworkCache.BUS_ATTRIBUTES) + # !!!! there is a precise way to create buses global numbering (see grid2op methods to convert from local to global num) + # global_num = substation_num + local_num * bus_per_substation count + # So given 2 voltage levels with 2 buses each, local and global number should be: + # + # voltage_level_id bus_id local_num global_num + # VL1 B1 0 0 + # VL1 B2 1 2 + # VL2 B3 0 1 + # VL2 B4 1 3 + voltage_levels = self.get_voltage_levels() + buses = buses.merge(voltage_levels.rename(columns=lambda x: x + '_voltage_level'), left_on='voltage_level_id', right_index=True, how='outer') + buses['local_num'] = buses.groupby('voltage_level_id').cumcount() + buses['num'] = buses['num_voltage_level'] + buses['local_num'] * len(voltage_levels) + buses_dict = buses['num'].to_dict() + buses_dict = {v: k for k, v in buses_dict.items()} + return buses, buses_dict + + def get_injections(self, injections: DataFrame) -> pd.DataFrame: + injections['num'] = range(len(injections)) # add numbering + buses = self.get_buses()[0] + buses_min = buses[['v_mag', 'synchronous_component', 'local_num', 'num']] + injections_merged_with_buses = injections.merge( + buses_min.rename(columns=lambda x: x + '_bus'), right_index=True, + left_on='bus_breaker_bus_id', how='left') + return injections_merged_with_buses + + def get_loads(self) -> pd.DataFrame: + return self.get_injections(self._network.get_loads(attributes=NetworkCache.INJECTION_ATTRIBUTES)) + + def get_generators(self) -> pd.DataFrame: + return self.get_injections(self._network.get_generators(attributes=NetworkCache.INJECTION_ATTRIBUTES)) + + def get_shunts(self) -> pd.DataFrame: + return self.get_injections(self._network.get_shunt_compensators(attributes=NetworkCache.INJECTION_ATTRIBUTES)) + + def get_batteries(self) -> pd.DataFrame: + return self.get_injections(self._network.get_batteries(attributes=NetworkCache.INJECTION_ATTRIBUTES)) + + def get_branches(self) -> pd.DataFrame: + lines = self._network.get_lines(attributes=NetworkCache.BRANCH_ATTRIBUTES) + transformers = self._network.get_2_windings_transformers(attributes=NetworkCache.BRANCH_ATTRIBUTES) + lines['num'] = range(len(lines)) # add numbering + transformers['num'] = range(len(lines), len(lines) + len(transformers)) # numbering starting from last line num + # FIXME support 3 windings transformers + branches = pd.concat([lines, transformers], axis=0) + buses = self.get_buses()[0] + buses_min = buses[['v_mag', 'synchronous_component', 'num']] + branches_merged_with_buses = ( + branches.merge(buses_min.rename(columns=lambda x: x + '_bus1'), right_index=True, left_on='bus_breaker_bus1_id', + how='left') + .merge(buses_min.rename(columns=lambda x: x + '_bus2'), right_index=True, left_on='bus_breaker_bus2_id', + how='left')) + return branches_merged_with_buses + + def get_branches_with_limits(self) -> pd.DataFrame: + operational_limits = self._network.get_operational_limits( + attributes=['element_type', 'type', 'value', 'acceptable_duration']) + # FIXME also get other limit type + current_limits = operational_limits[(operational_limits['type'] == 'CURRENT') & ( + operational_limits['acceptable_duration'] == -1)] # only keep permanent limit + current_limits = current_limits.groupby('element_id').agg( + {'value': 'max'}).reset_index() # get side 1 and 2 max one + branches = self.get_branches() + branches_with_limits_a = branches.merge(current_limits, left_index=True, right_on='element_id', how='outer') + branches_with_limits_a = branches_with_limits_a.fillna(888888) # replace missing limits by a very high one + return branches_with_limits_a + + def get_switches(self) -> pd.DataFrame: + switches = self._network.get_switches(attributes=NetworkCache.SWITCH_ATTRIBUTES) + return switches[switches['retained']] + + def create_buses(self, df: Optional[DataFrame] = None, **kwargs: Dict[str, Any]) -> None: + self._network.create_buses(df, **kwargs) + + def disconnect_load(self, iidm_id: str) -> None: + self._network.update_loads(id=iidm_id, connected=False) + + def connected_load(self, iidm_id: str, new_bus_id: str) -> None: + self._network.update_loads(id=iidm_id, bus_breaker_bus_id=new_bus_id, connected=True) + + def disconnect_generator(self, iidm_id: str) -> None: + self._network.update_generators(id=iidm_id, connected=False) + + def connected_generator(self, iidm_id: str, new_bus_id: str) -> None: + self._network.update_generators(id=iidm_id, bus_breaker_bus_id=new_bus_id, connected=True) + + def disconnect_shunt(self, iidm_id: str) -> None: + self._network.update_shunt_compensators(id=iidm_id, connected=False) + + def connected_shunt(self, iidm_id: str, new_bus_id: str) -> None: + self._network.update_shunt_compensators(id=iidm_id, bus_breaker_bus_id=new_bus_id, connected=True) + + def disconnect_branch_side1(self, iidm_id: str) -> None: + self._network.update_branches(id=iidm_id, connected1=False) + + def connect_branch_side1(self, iidm_id: str, new_bus_id: str) -> None: + self._network.update_branches(id=iidm_id, bus_breaker_bus1_id=new_bus_id, connected1=True) + + def disconnect_branch_side2(self, iidm_id: str) -> None: + self._network.update_branches(id=iidm_id, connected2=False) + + def connect_branch_side2(self, iidm_id: str, new_bus_id: str) -> None: + self._network.update_branches(id=iidm_id, bus_breaker_bus2_id=new_bus_id, connected2=True) + + def update_load_p(self, iidm_id: str, new_p: float) -> None: + self._network.update_loads(id=iidm_id, p0=new_p) + + def update_load_q(self, iidm_id: str, new_q: float) -> None: + self._network.update_loads(id=iidm_id, q0=new_q) + + def update_generator_p(self, iidm_id: str, new_p: float) -> None: + self._network.update_generators(id=iidm_id, target_p=new_p) + + def update_generator_v(self, iidm_id: str, new_v: float) -> None: + self._network.update_generators(id=iidm_id, target_v=new_v) + + def update_shunt_p(self, iidm_id: str, new_p: float) -> None: + # FIXME how to deal with discrete shunts? + pass + + def update_shunt_q(self, iidm_id: str, new_q: float) -> None: + # FIXME how to deal with discrete shunts? + pass + +class SimpleNetworkCacheFactory(NetworkCacheFactory): + + def create_network_cache(self, network: pp.network.Network, lf_parameters: pp.loadflow.Parameters = DEFAULT_LF_PARAMETERS) -> NetworkCache: + return SimpleNetworkCache(network, lf_parameters) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..23decbb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,25 @@ +[tool.poetry] +name = "pypowsybl2grid" +version = "0.1.0" +description = "An integration between Grid2op and PyPowSybl" +authors = ["Geoffroy Jamgotchian "] +readme = "README.md" +license = "MPL-2.0" + +[tool.poetry.dependencies] +python = "^3.9" +numpy = "1.26.4" +pandas = "^2.2.2" +pandas-stubs = "^2.2.2" +#pypowsybl = "^1.8.0" +pypowsybl = { git = "https://github.com/powsybl/pypowsybl.git", branch = "pandapower_converter" } +#grid2op = "^1.10.4 +grid2op = { git = "https://github.com/rte-france/Grid2Op.git", branch = "dev_1.10.4" } + +[tool.poetry.group.test.dependencies] +pytest = "^8.3.3" +mypy = "^1.11.2" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_network_cache.py b/tests/test_network_cache.py new file mode 100644 index 0000000..78e8fbb --- /dev/null +++ b/tests/test_network_cache.py @@ -0,0 +1,154 @@ +import pandas as pd +import pypowsybl as pp +import pytest +from numpy import nan + +from pypowsybl2grid.fast_network_cache import FastNetworkCacheFactory +from pypowsybl2grid.network_cache import NetworkCacheFactory +from pypowsybl2grid.simple_network_cache import SimpleNetworkCacheFactory + + +@pytest.fixture(autouse=True) +def setup(): + pd.options.display.max_columns = None + pd.options.display.expand_frame_repr = False + + +def test_simple_network_cache(): + run_network_cache_test(SimpleNetworkCacheFactory()) + + +def test_fast_network_cache(): + run_network_cache_test(FastNetworkCacheFactory()) + + +def run_network_cache_test(network_cache_factory: NetworkCacheFactory): + n = pp.network.create_eurostag_tutorial_example1_network() + cache = network_cache_factory.create_network_cache(n) + cache.create_buses(id='VLGEN_extra_busbar_1', voltage_level_id="VLGEN") + cache.run_ac_pf() + buses, buses_dict = cache.get_buses() + expected_buses = pd.DataFrame(index=pd.Series(name='id', data=['NGEN', 'VLGEN_extra_busbar_1', 'NHV1', 'NHV2', 'NLOAD']), + columns=['v_mag', 'synchronous_component', 'voltage_level_id', 'name_voltage_level', 'topology_kind_voltage_level', 'num_voltage_level', 'local_num', 'num'], + data=[[24.500000, 0, 'VLGEN', '', 'BUS_BREAKER', 0, 0, 0], + [nan, -99999, 'VLGEN', '', 'BUS_BREAKER', 0, 1, 4], + [402.142826, 0, 'VLHV1', '', 'BUS_BREAKER', 1, 0, 1], + [389.952653, 0, 'VLHV2', '', 'BUS_BREAKER', 2, 0, 2], + [147.578618, 0, 'VLLOAD','', 'BUS_BREAKER', 3, 0, 3]]) + pd.testing.assert_frame_equal(expected_buses, buses, check_dtype=False) + + assert {0: 'NGEN', 1: 'NHV1', 2: 'NHV2', 3: 'NLOAD', 4: 'VLGEN_extra_busbar_1'} == buses_dict + + voltage_levels = cache.get_voltage_levels() + expected_voltage_levels = pd.DataFrame(index=pd.Series(name='id', data=['VLGEN', 'VLHV1', 'VLHV2', 'VLLOAD']), + columns=['name', 'topology_kind', 'num'], + data=[['', 'BUS_BREAKER', 0], + ['', 'BUS_BREAKER', 1], + ['', 'BUS_BREAKER', 2], + ['', 'BUS_BREAKER', 3]]) + pd.testing.assert_frame_equal(expected_voltage_levels, voltage_levels, check_dtype=False) + + loads = cache.get_loads() + expected_loads = pd.DataFrame(index=pd.Series(name='id', data=['LOAD']), + columns=['name', 'voltage_level_id', 'bus_breaker_bus_id', 'connected', 'p', 'q', 'num', + 'v_mag_bus', 'synchronous_component_bus', 'local_num_bus', 'num_bus'], + data=[['', 'VLLOAD', 'NLOAD', True, 600.0, 200.0, 0, 147.578618, 0, 0, 3]]) + pd.testing.assert_frame_equal(expected_loads, loads, check_dtype=False) + + generators = cache.get_generators() + expected_generators = pd.DataFrame(index=pd.Series(name='id', data=['GEN', 'GEN2']), + columns=['name', 'voltage_level_id', 'bus_breaker_bus_id', 'connected', 'p', 'q', 'num', + 'v_mag_bus', 'synchronous_component_bus', 'local_num_bus', 'num_bus'], + data=[['', 'VLGEN', 'NGEN', True, -302.780515, -112.64135, 0, 24.5, 0, 0, 0], + ['', 'VLGEN', 'NGEN', True, -302.780515, -112.64135, 1, 24.5, 0, 0, 0]]) + pd.testing.assert_frame_equal(expected_generators, generators, check_dtype=False) + + shunts = cache.get_shunts() + assert len(shunts) == 0 + + branches = cache.get_branches() + expected_branches = pd.DataFrame(index=pd.Series(name='id', data=['NHV1_NHV2_1', 'NHV1_NHV2_2', 'NGEN_NHV1', 'NHV2_NLOAD']), + columns=['name', 'voltage_level1_id', 'voltage_level2_id', 'bus_breaker_bus1_id', + 'bus_breaker_bus2_id', 'connected1', 'connected2', 'p1', 'q1', 'i1', + 'p2', 'q2', 'i2', 'num', 'v_mag_bus1', 'synchronous_component_bus1', 'num_bus1', 'v_mag_bus2', 'synchronous_component_bus2', 'num_bus2'], + data=[['', 'VLHV1', 'VLHV2', 'NHV1', 'NHV2', True, True, 302.444049, 98.740275, 456.768978, -300.433895, -137.188493, 488.992798, 0, 402.142826, 0, 1, 389.952653, 0, 2], + ['', 'VLHV1', 'VLHV2', 'NHV1', 'NHV2', True, True, 302.444049, 98.740275, 456.768978, -300.433895, -137.188493, 488.992798, 1, 402.142826, 0, 1, 389.952653, 0, 2], + ['', 'VLGEN', 'VLHV1', 'NGEN', 'NHV1', True, True, 605.561014, 225.282699, 15225.756113, -604.893567, -197.480432, 913.545367, 2, 24.5, 0, 0, 402.142826, 0, 1], + ['', 'VLHV2', 'VLLOAD', 'NHV2', 'NLOAD', True, True, 600.867790, 274.376987, 977.985596, -600.0, -200.0, 2474.263394, 3, 389.952653, 0, 2, 147.578618, 0, 3]]) + pd.testing.assert_frame_equal(expected_branches, branches, check_dtype=False) + + # test load modification + cache.update_load_p('LOAD', 700.0) + cache.update_load_q('LOAD', 300.0) + cache.run_ac_pf() + loads = cache.get_loads() + expected_loads = pd.DataFrame(index=pd.Series(name='id', data=['LOAD']), + columns=['name', 'voltage_level_id', 'bus_breaker_bus_id', 'connected', 'p', 'q', + 'num', + 'v_mag_bus', 'synchronous_component_bus', 'local_num_bus', 'num_bus'], + data=[['', 'VLLOAD', 'NLOAD', True, 700.0, 300.0, 0, 138.52392450257688, 0, 0, 3]]) + pd.testing.assert_frame_equal(expected_loads, loads, check_dtype=False) + + # test generator modification + cache.update_generator_p('GEN', 310.0) + cache.update_generator_v('GEN', 25.0) + cache.run_ac_pf() + generators = cache.get_generators() + expected_generators = pd.DataFrame(index=pd.Series(name='id', data=['GEN', 'GEN2']), + columns=['name', 'voltage_level_id', 'bus_breaker_bus_id', 'connected', 'p', 'q', + 'num', 'v_mag_bus', 'synchronous_component_bus', 'local_num_bus', 'num_bus'], + data=[['', 'VLGEN', 'NGEN', True, -206.07521276657846, -202.2736774358548, 0, 25.0, 0, 0, 0], + ['', 'VLGEN', 'NGEN', True, -503.07521276657843, -202.2736774358548, 1, 25.0, 0, 0, 0]]) + pd.testing.assert_frame_equal(expected_generators, generators, check_dtype=False) + + # load disconnection + cache.disconnect_load('LOAD') + cache.run_ac_pf() + loads = cache.get_loads() + expected_loads = pd.DataFrame(index=pd.Series(name='id', data=['LOAD']), + columns=['name', 'voltage_level_id', 'bus_breaker_bus_id', 'connected', 'p', 'q','num', + 'v_mag_bus', 'synchronous_component_bus', 'local_num_bus', 'num_bus'], + data=[['', 'VLLOAD', 'NLOAD', False, nan, nan, 0, 167.18649525262273, 0, 0, 3]]) + pd.testing.assert_frame_equal(expected_loads, loads, check_dtype=False) + + # load reconnection + cache.connected_load('LOAD', 'NLOAD') + cache.run_ac_pf() + loads = cache.get_loads() + expected_loads = pd.DataFrame(index=pd.Series(name='id', data=['LOAD']), + columns=['name', 'voltage_level_id', 'bus_breaker_bus_id', 'connected', 'p', 'q', + 'num', 'v_mag_bus', 'synchronous_component_bus', 'local_num_bus', 'num_bus'], + data=[['', 'VLLOAD', 'NLOAD', True, 700.0, 300.0, 0, 142.91752783756047, 0, 0, 3]]) + pd.testing.assert_frame_equal(expected_loads, loads, check_dtype=False) + + # line disconnection + cache.disconnect_branch_side1('NHV1_NHV2_1') + cache.disconnect_branch_side2('NHV1_NHV2_1') + cache.run_ac_pf() + branches = cache.get_branches() + expected_branches = pd.DataFrame( + index=pd.Series(name='id', data=['NHV1_NHV2_1', 'NHV1_NHV2_2', 'NGEN_NHV1', 'NHV2_NLOAD']), + columns=['name', 'voltage_level1_id', 'voltage_level2_id', 'bus_breaker_bus1_id', + 'bus_breaker_bus2_id', 'connected1', 'connected2', 'p1', 'q1', 'i1', + 'p2', 'q2', 'i2', 'num', 'v_mag_bus1', 'synchronous_component_bus1', 'num_bus1', 'v_mag_bus2', 'synchronous_component_bus2', 'num_bus2'], + data=[['', 'VLHV1', 'VLHV2', 'NHV1', 'NHV2', False, False, nan, nan, nan, nan, nan, nan, 0, 399.7154229049848, 0, 1, 348.5241869126856, 0, 2], + ['', 'VLHV1', 'VLHV2', 'NHV1', 'NHV2', True, True, 718.336375, 576.329007, 1330.233688, -701.725437, -447.888303, 1379.050354, 1, 399.7154229049848, 0, 1, 348.5241869126856, 0, 2], + ['', 'VLGEN', 'VLHV1', 'NGEN', 'NHV1', True, True, 720.38758, 635.341154, 22182.47534, -718.970877, -576.32886, 1330.94852, 2, 25.0, 0, 0, 399.7154229049848, 0, 1], + ['', 'VLHV2', 'VLLOAD', 'NHV2', 'NLOAD', True, True, 701.725392, 447.888302, 1379.05029, -699.999911, -299.999984, 3488.940596, 3, 348.5241869126856, 0, 2, 126.02588154999955, 0, 3]]) + pd.testing.assert_frame_equal(expected_branches, branches, check_dtype=False) + + # line reconnection + cache.connect_branch_side1('NHV1_NHV2_1', 'NHV1') + cache.connect_branch_side2('NHV1_NHV2_1', 'NHV2') + cache.run_ac_pf() + branches = cache.get_branches() + expected_branches = pd.DataFrame( + index=pd.Series(name='id', data=['NHV1_NHV2_1', 'NHV1_NHV2_2', 'NGEN_NHV1', 'NHV2_NLOAD']), + columns=['name', 'voltage_level1_id', 'voltage_level2_id', 'bus_breaker_bus1_id', + 'bus_breaker_bus2_id', 'connected1', 'connected2', 'p1', 'q1', 'i1', + 'p2', 'q2', 'i2', 'num', 'v_mag_bus1', 'synchronous_component_bus1', 'num_bus1', 'v_mag_bus2', 'synchronous_component_bus2', 'num_bus2'], + data=[['', 'VLHV1', 'VLHV2', 'NHV1', 'NHV2', True, True, 353.774582, 180.95677, 565.271318, -350.670841, -207.497956, 608.029136, 0, 405.85974058065284, 0, 1, 386.90318309661694, 0, 2], + ['', 'VLHV1', 'VLHV2', 'NHV1', 'NHV2', True, True, 353.774582, 180.95677, 565.271318, -350.670841, -207.497956, 608.029136, 1, 405.85974058065284, 0, 1, 386.90318309661694, 0, 2], + ['', 'VLGEN', 'VLHV1', 'NGEN', 'NHV1', True, True, 709.150389, 404.547355, 18854.570955, -708.126879, -361.913383, 1131.274257, 2, 25.0, 0, 0, 405.85974058065284, 0, 1], + ['', 'VLHV2', 'VLLOAD', 'NHV2', 'NLOAD', True, True, 701.341670, 414.995911, 1216.058256, -699.999960, -299.999993, 3076.577445, 3, 386.90318309661694, 0, 2, 142.91752783756047, 0, 3]]) + pd.testing.assert_frame_equal(expected_branches, branches, check_dtype=False) diff --git a/tests/test_pypowsybl_backend.py b/tests/test_pypowsybl_backend.py new file mode 100644 index 0000000..5165f3e --- /dev/null +++ b/tests/test_pypowsybl_backend.py @@ -0,0 +1,28 @@ +import logging +import unittest + +import pypowsybl as pp +import pytest +from grid2op.dtypes import dt_float +from grid2op.tests.aaa_test_backend_interface import AAATestBackendAPI + +from pypowsybl2grid.pypowsybl_backend import PyPowSyBlBackend + +# needed config to make some grid2op test passing +TEST_LOADFLOW_PARAMETERS = pp.loadflow.Parameters(distributed_slack=True, + use_reactive_limits=False, + provider_parameters={"slackBusPMaxMismatch": "1e-2", + "newtonRaphsonConvEpsPerEq": "1e-7", + "useActiveLimits": "false"}) + +@pytest.fixture(autouse=True) +def setup(): + logging.basicConfig() + logging.getLogger('powsybl').setLevel(logging.ERROR) + + +class TestBackendPyPowSyBl(AAATestBackendAPI, unittest.TestCase): + + def make_backend(self, detailed_infos_for_cascading_failures=False): + self.tol_one = dt_float(1e-3) + return PyPowSyBlBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, lf_parameters=TEST_LOADFLOW_PARAMETERS)