[
  {
    "path": ".github/workflows/build_and_test.yml",
    "content": "name: Build\n\non:\n  push:\n    branches: [ master, develop ]\n  pull_request:\n    branches: [ master, develop ]\n  workflow_dispatch:\n  \njobs:\n  build-code:\n    runs-on: ubuntu-latest\n    strategy:\n      max-parallel: 5\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up Python 3.8\n      uses: actions/setup-python@v2\n      with:\n        python-version: 3.8\n    - name: Add conda to system path\n      run: |\n        # $CONDA is an environment variable pointing to the root of the miniconda directory\n        echo $CONDA/bin >> $GITHUB_PATH\n    - name: Install dependencies\n      run: |\n        conda install -c conda-forge pyfmi\n        pip install -r requirements.txt\n        pip install .[all]\n        pip install -r requirements_dev.txt\n        conda install pytest\n    - name: Test with pytest\n      run: pytest\n  build-doc:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v2\n    - name: Build Sphinx documentation\n      uses: ammaraskar/sphinx-action@master\n      with:\n        pre-build-command: \"python -m pip install -r requirements_dev.txt\"\n        docs-folder: \"docs/\"\n    # Publish built docs to gh-pages branch.\n    # ===============================\n    - name: Commit documentation changes\n      run: |\n        git clone https://github.com/upb-lea/openmodelica-microgrid-gym.git --branch gh-pages --single-branch gh-pages\n        cp -r docs/_build/html/* gh-pages/\n        cd gh-pages\n        touch .nojekyll\n        git config --local user.email \"action@github.com\"\n        git config --local user.name \"GitHub Action\"\n        git add .\n        git commit -m \"Update documentation\" -a || true\n        # The above command will fail if no changes were present, so we ignore\n        # that.\n    - name: Push changes\n      uses: ad-m/github-push-action@master\n      with:\n        branch: gh-pages\n        directory: gh-pages\n        github_token: ${{ secrets.GITHUB_TOKEN }}\n    # ===============================\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\n__pycache__/\n.mypy_cache/\n*.fmu.txt\n\n.coverage\n\n# release\n/openmodelica_microgrid_gym.egg-info/\n/dist/\n/build/\n/.eggs/\n\n/docs/_build/\n\n/fmu/binaries/win64/\n/fmu/sources/\n/fmu/comb\n/fmu/*.bat\n/fmu/*.log\n/fmu/*_FMU.makefile\n/fmu/combi\n/fmu/combined\n/fmu/*.def\n/fmu/*.makefile\n/fmu/*_FMU.libs\n/fmu/*_FMU.log\n/fmu/*_init.xml\n/fmu/modelDescription.xml\n/fmu/OMCpp*.cpp\n/fmu/OMCpp*.h\n/fmu/OMCpp*AlgLoopMain.cpp\n/fmu/OMCpp*CalcHelperMain.cpp\n/fmu/OMCpp*CalcHelperMain.o\n/fmu/OMCpp*FactoryExport.cpp\n/fmu/OMCpp*FMU.cpp\n/fmu/OMCpp*FMU.h\n/fmu/OMCpp*Functions.cpp\n/fmu/OMCpp*Functions.h\n/fmu/OMCpp*Initialize.cpp\n/fmu/OMCpp*Initialize.h\n/fmu/OMCpp*InitializeAlgVars.cpp\n/fmu/OMCpp*InitializeParameter.cpp\n/fmu/OMCpp*Jacobian.cpp\n/fmu/OMCpp*Jacobian.h\n/fmu/OMCpp*Main.cpp\n/fmu/OMCpp*Mixed.cpp\n/fmu/OMCpp*Mixed.h\n/fmu/OMCpp*StateSelection.cpp\n/fmu/OMCpp*StateSelection.h\n/fmu/OMCpp*Types.h\n/fmu/OMCpp*WriteOutput.cpp\n/fmu/OMCpp*WriteOutput.h\n"
  },
  {
    "path": "AUTHORS.rst",
    "content": "=======\nCredits\n=======\n\nDevelopment Lead\n----------------\n\n* LEA - Uni Paderborn <upblea@mail.upb.de>\n\nContributors\n------------\n\nNone yet. Why not be the first?\n"
  },
  {
    "path": "CONTRIBUTING.rst",
    "content": ".. highlight:: shell\n\n============\nContributing\n============\n\nContributions are welcome, and they are greatly appreciated! Every little bit\nhelps, and credit will always be given.\n\nYou can contribute in many ways:\n\nTypes of Contributions\n----------------------\n\nReport Bugs\n~~~~~~~~~~~\n\nReport bugs at https://github.com/upb-lea/openmodelica_microgrid_gym/issues.\n\nIf you are reporting a bug, please include:\n\n* Your operating system name and version.\n* Any details about your local setup that might be helpful in troubleshooting.\n* Detailed steps to reproduce the bug.\n\nFix Bugs\n~~~~~~~~\n\nLook through the GitHub issues for bugs. Anything tagged with \"bug\" and \"help\nwanted\" is open to whoever wants to implement it.\n\nImplement Features\n~~~~~~~~~~~~~~~~~~\n\nLook through the GitHub issues for features. Anything tagged with \"enhancement\"\nand \"help wanted\" is open to whoever wants to implement it.\n\nWrite Documentation\n~~~~~~~~~~~~~~~~~~~\n\nOpenModelica Microgrid Gym could always use more documentation, whether as part of the\nofficial OpenModelica Microgrid Gym docs, in docstrings, or even on the web in blog posts,\narticles, and such.\n\nSubmit Feedback\n~~~~~~~~~~~~~~~\n\nThe best way to send feedback is to file an issue at https://github.com/upb-lea/openmodelica_microgrid_gym/issues.\n\nIf you are proposing a feature:\n\n* Explain in detail how it would work.\n* Keep the scope as narrow as possible, to make it easier to implement.\n* Remember that this is a volunteer-driven project, and that contributions\n  are welcome :)\n\nGet Started!\n------------\n\nReady to contribute? Here's how to set up `openmodelica_microgrid_gym` for local development.\n\n1. Fork the `openmodelica_microgrid_gym` repo on GitHub.\n2. Clone your fork locally::\n\n    $ git clone git@github.com:your_name_here/openmodelica_microgrid_gym.git\n\n3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development::\n\n    $ mkvirtualenv openmodelica_microgrid_gym\n    $ cd openmodelica_microgrid_gym/\n    $ python setup.py develop\n\n4. Create a branch for local development::\n\n    $ git checkout -b name-of-your-bugfix-or-feature\n\n   Now you can make your changes locally.\n\n5. When you're done making changes, check that your changes pass pytest::\n\n    $ pytest\n\n6. Commit your changes and push your branch to GitHub::\n\n    $ git add .\n    $ git commit -m \"Your detailed description of your changes.\"\n    $ git push origin name-of-your-bugfix-or-feature\n\n7. Submit a pull request through the GitHub website.\n\nPull Request Guidelines\n-----------------------\n\nBefore you submit a pull request, check that it meets these guidelines:\n\n1. The pull request should include tests.\n2. If the pull request adds functionality, the docs should be updated. Put\n   your new functionality into a function with a docstring, and add the\n   feature to the list in README.rst.\n3. The pull request should work for Python 3.5, 3.6, 3.7 and 3.8, and for PyPy. Check\n   https://travis-ci.com/upb-lea/openmodelica_microgrid_gym/pull_requests\n   and make sure that the tests pass for all supported Python versions.\n\nTips\n----\n\nTo run a subset of tests::\n\n    $ pytest tests.test_openmodelica_microgrid_gym\n\n\nDeploying\n---------\n\nA reminder for the maintainers on how to deploy.\nMake sure all your changes are committed (including an entry in HISTORY.rst).\nThen run::\n\n    $ bump2version patch # possible: major / minor / patch\n    $ git push\n    $ git push --tags\n\nTravis will then deploy to PyPI if tests pass.\n"
  },
  {
    "path": "HISTORY.rst",
    "content": "=======\nHistory\n=======\n\nNext\n-------\n\n0.4.0 (2021-04-07)\n------------------\nChanges\n^^^^^^^\n* ModelicaEnv:\n    - Introduced action clipping\n    - model_params: None values are not passed to the OpenModelica env to allow initialization\n    - model_params: negative time values are introduced for initialization (fix)\n    - Introduced abort reward in env if episode is terminated\n    - Introduced obs_output to define a subset of history given as observation to the agent\n\nFix\n^^^\n* omg.net.MasterInverter:\n    - default values used to overwrite passed values\n\nAdd\n^^^\n* Random Process wrapper\n* ObsTempl test\n* reset test for initialized env\n\n\n\n\n0.3.0 (2020-12-18)\n------------------\n\nAPI\n^^^\n* ModelicaEnv:\n    - Uses Network\n    - __init__:\n      - removed: timestep, model_output, model_input\n      - added: network\n    - Delay buffer\n* Network and Components:\n    - Specify class structure using config file corresponding to fmu (see net-folder)\n    - added noise\n* SafeoptAgent:\n    - __init__: Performance parameters and calculation\n* aux_ctl.Contoller:\n    - __init__: timestep and undersampling changed\n    - added output clipping\n* Plotmanager\n\n\nExamples\n^^^^^^^^\n* updated to changed API\n\nExperiments\n^^^^^^^^^^^\n* model validation:\n    - experiment files\n    - experiment environment managing testbench connection via SSH\n\nDependencies\n^^^^^^^^^^^^\n* Decreased Language Level to Python 3.7\n\n\n\n\n\n0.2.0 (2020-05-27)\n------------------\n\n\nAPI\n^^^\n* ModelicaEnv:\n   - reward function parameter\n   - vis_cols now also supports Plotting templates\n\n* EmptyHistory and descendant: update(), append()\n* Agent: added properties\n* StaticControlAgent and descendant: small changes in constructor params, specifically obs_template, added properties\n* SafeOptAgent: added properties\n* Runner: plotting can be disabled\n\nExamples\n^^^^^^^^\n* added example for plotting\n\nPerformance\n^^^^^^^^^^^\n* 6.6× speedup\n\nDependencies\n^^^^^^^^^^^^\n* Increased Language Level to Python 3.8\n\n\n\n0.1.3 (2020-05-13)\n------------------\n\n* best parameter set output after termination of SafeOpt agent (`#7`_)\n* proper action and observation space (`#14`_)\n* resolved problem related to environment :code:`model_params` (`#21`_)\n\n|\n\n* documentation improvements (more examples, installation)\n\n.. _`#7`: https://github.com/upb-lea/openmodelica-microgrid-gym/issues/7\n.. _`#14`: https://github.com/upb-lea/openmodelica-microgrid-gym/issues/14\n.. _`#21`: https://github.com/upb-lea/openmodelica-microgrid-gym/issues/21\n\n\n0.1.2 (2020-05-04)\n------------------\n\n* corrected pip install requirements\n\n\n0.1.1 (2020-04-22)\n------------------\n\n* First release on PyPI.\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include AUTHORS.rst\ninclude CONTRIBUTING.rst\ninclude HISTORY.rst\ninclude LICENSE\ninclude README.rst\n\nrecursive-include tests *\nrecursive-exclude * __pycache__\nrecursive-exclude * *.py[co]\n\nrecursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: clean clean-test clean-pyc clean-build docs help\n.DEFAULT_GOAL := help\n\ndefine BROWSER_PYSCRIPT\nimport os, webbrowser, sys\n\nfrom urllib.request import pathname2url\n\nwebbrowser.open(\"file://\" + pathname2url(os.path.abspath(sys.argv[1])))\nendef\nexport BROWSER_PYSCRIPT\n\ndefine PRINT_HELP_PYSCRIPT\nimport re, sys\n\nfor line in sys.stdin:\n\tmatch = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line)\n\tif match:\n\t\ttarget, help = match.groups()\n\t\tprint(\"%-20s %s\" % (target, help))\nendef\nexport PRINT_HELP_PYSCRIPT\n\nBROWSER := python -c \"$$BROWSER_PYSCRIPT\"\n\nhelp:\n\t@python -c \"$$PRINT_HELP_PYSCRIPT\" < $(MAKEFILE_LIST)\n\nclean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts\n\nclean-build: ## remove build artifacts\n\trm -fr build/\n\trm -fr dist/\n\trm -fr .eggs/\n\tfind . -name '*.egg-info' -exec rm -fr {} +\n\tfind . -name '*.egg' -exec rm -f {} +\n\nclean-pyc: ## remove Python file artifacts\n\tfind . -name '*.pyc' -exec rm -f {} +\n\tfind . -name '*.pyo' -exec rm -f {} +\n\tfind . -name '*~' -exec rm -f {} +\n\tfind . -name '__pycache__' -exec rm -fr {} +\n\nclean-test: ## remove test and coverage artifacts\n\trm -fr .tox/\n\trm -f .coverage\n\trm -fr htmlcov/\n\trm -fr .pytest_cache\n\nlint: ## check style with flake8\n\tflake8 openmodelica_microgrid_gym tests\n\ntest: ## run tests quickly with the default Python\n\tpytest\n\ntest-all: ## run tests on every Python version with tox\n\ttox\n\ncoverage: ## check code coverage quickly with the default Python\n\tcoverage run --source openmodelica_microgrid_gym -m pytest\n\tcoverage report -m\n\tcoverage html\n\t$(BROWSER) htmlcov/index.html\n\ndocs: ## generate Sphinx HTML documentation, including API docs\n\trm -f docs/openmodelica_microgrid_gym.rst\n\trm -f docs/modules.rst\n\tsphinx-apidoc -o docs/ openmodelica_microgrid_gym\n\t$(MAKE) -C docs clean\n\t$(MAKE) -C docs html\n\t$(BROWSER) docs/_build/html/index.html\n\nservedocs: docs ## compile the docs watching for changes\n\twatchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D .\n\nrelease: dist ## package and upload a release\n\ttwine upload dist/*\n\ndist: clean ## builds source and wheel package\n\tpython setup.py sdist\n\tpython setup.py bdist_wheel\n\tls -l dist\n\ninstall: clean ## install the package to the active Python's site-packages\n\tpython setup.py install\n"
  },
  {
    "path": "README.rst",
    "content": "==========================\nOpenModelica Microgrid Gym\n==========================\n\n| |build| |cov| |nbsp| |nbsp| |python| |pypi| |download| |nbsp| |nbsp| |license|\n| |doc| |whitepaper| |joss|\n\n.. |nbsp|   unicode:: U+00A0 .. NO-BREAK SPACE\n\n.. |build| image:: https://github.com/upb-lea/openmodelica-microgrid-gym/actions/workflows/build_and_test.yml/badge.svg\n    :target: https://github.com/upb-lea/openmodelica-microgrid-gym/actions/workflows/build_and_test.yml\n\n.. |cov| image:: https://codecov.io/gh/upb-lea/openmodelica-microgrid-gym/branch/master/graph/badge.svg\n    :target: https://codecov.io/gh/upb-lea/openmodelica-microgrid-gym\n\n.. |license| image:: https://img.shields.io/github/license/upb-lea/openmodelica-microgrid-gym\n    :target: LICENSE\n\n.. |python| image:: https://img.shields.io/pypi/pyversions/openmodelica-microgrid-gym\n    :target: https://pypi.python.org/pypi/openmodelica_microgrid_gym\n\n.. |pypi| image:: https://img.shields.io/pypi/v/openmodelica_microgrid_gym\n    :target: https://pypi.python.org/pypi/openmodelica_microgrid_gym\n\n.. |download| image:: https://img.shields.io/pypi/dw/openmodelica-microgrid-gym\n    :target: https://pypistats.org/packages/openmodelica-microgrid-gym\n\n.. |doc| image:: https://img.shields.io/badge/doc-success-success\n    :target: https://upb-lea.github.io/openmodelica-microgrid-gym\n\n.. |whitepaper| image:: https://img.shields.io/badge/arXiv-whitepaper-informational\n    :target: https://arxiv.org/pdf/2005.04869.pdf\n    \n.. |joss| image:: https://joss.theoj.org/papers/10.21105/joss.02435/status.svg\n   :target: https://doi.org/10.21105/joss.02435\n\n\n\n.. figure:: https://github.com/upb-lea/openmodelica-microgrid-gym/raw/develop/docs/pictures/omg_flow.png\n\n**The OpenModelica Microgrid Gym (OMG) package is a software toolbox for the\nsimulation and control optimization of microgrids based on energy conversion by power electronic converters.**\n\nThe main characteristics of the toolbox are the plug-and-play grid design and simulation in OpenModelica as well as\nthe ready-to-go approach of intuitive reinfrocement learning (RL) approaches through a Python interface.\n\nThe OMG toolbox is built upon the `OpenAI Gym`_ environment definition framework.\nTherefore, the toolbox is specifically designed for running reinforcement\nlearning algorithms to train agents controlling power electronic converters in microgrids. Nevertheless, also arbritary classical control approaches can be combined and tested using the OMG interface.\n\n.. _OpenAI Gym: https://gym.openai.com/\n\n* Free software: GNU General Public License v3\n* Documentation: https://upb-lea.github.io/openmodelica-microgrid-gym\n\n\nVideo Tutorial\n--------------\n\nFollowing is a short YouTube video introduction, to get a fist impression how to use OMG.\n\n\n\n- https://www.youtube.com/watch?v=rwBNFvCi_dY\n\nInstallation\n------------\n\n\nInstall Python Environment\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThis is the short installation guide for Windows and Linux. OpenModelica_ is hardly supported for Mac, they suggest to install in a Linux VM. For this reason, running OMG in a Linux VM is strongly recommended for Mac users!\n\nSince it is not possible to install PyFMI_, a package which is necessary for the communication between the python interface and the environment, via pip, we recommend to install this package in advance in a conda environment.\nAs of now, only Windows and Linux are supported officially.\n\n- If conda is NOT installed on your PC, install miniconda_ for python 3.8\n- Create a new conda environment (e.g. in PyCharm)\n- Install PyFMI from the conda-forge channel in the terminal::\n\n    $ conda install -c conda-forge pyfmi\n\n\n- Install OpenModelica MicrogridGym from PyPI (recommended)::\n\n    $ pip install openmodelica_microgrid_gym\n\n.. _OpenModelica: https://openmodelica.org/download/download-mac\n.. _miniconda: https://conda.io/en/latest/miniconda.html\n.. _PyFMI: https://github.com/modelon-community/PyFMI\n\nInstallation of OpenModelica\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nOMG was create by using OMEdit_ v1.16\n\nIn case of installation issues you can resort to their pre-built `virtual machine`_.\n\n.. _OMEdit: https://openmodelica.org/download/download-windows\n.. _virtual machine: https://openmodelica.org/download/virtual-machine\n\nGetting started\n---------------\n\nThe environment is initialized and run like any other OpenAI Gym\n\n.. code-block:: python\n\n    import gym\n\n    if __name__ == '__main__':\n        env = gym.make('openmodelica_microgrid_gym:ModelicaEnv-v1',\n                   max_episode_steps=None,\n                   net='../net/net.yaml',\n                   model_path='../omg_grid/grid.network.fmu')\n\n        env.reset()\n        for _ in range(1000):\n            env.render()\n            env.step(env.action_space.sample())  # take a random action\n        env.close()\n\n\n\n\nOMG uses the `FMI standard`_ for the exchange of the model between OpenModelica and Python.\n\n.. _FMI standard: https://fmi-standard.org/\n\nAn example network consisting out of two inverters, three filters and an inductive load.\n\n.. figure:: https://github.com/upb-lea/openmodelica-microgrid-gym/raw/master/docs/pictures/omedit.jpg\n\nYou can either use one of the provided FMUs (Windows and Linux, 64-bit, both included in the grid.network.fmu) or create your own by running::\n\n    openmodelica_microgrid_gym\\fmu> omc create_fmu.mos\n\nWindows users might need to open the terminal out of OpenModelica by clicking 'tools' => 'OpenModelica Command Prompt' to make sure that the command 'omc' gets recognized.\n\nRunning the ``staticctrl.py`` starts a simulation with a manually tuned cascaded PIPI controller\n\n.. figure:: https://github.com/upb-lea/openmodelica-microgrid-gym/raw/master/docs/pictures/control.jpg\n    :scale: 70%\n    :align: center\n\nA save Bayesian approach of a reinforcement learning agent is provided under examples/berkamkamp.py.\n\n.. figure:: https://github.com/upb-lea/openmodelica-microgrid-gym/raw/master/docs/pictures/kp_kp_J.png\n    :figwidth: 60%\n    :align: center\n\nUsing pytest\n^^^^^^^^^^^^\n\nOMG provides a big range of tests to ensure correct working toolbox after changes are done.\nOn some windows machines, the tests can only be started from the terminal via 'pytest'.\n\nThe standard test OS for the development is Linux. In some cases, we have noticed that the test_modelica.py on windows PCs might throw an error.\nSince on Linux everything works fine, it seems to be a numerical issue connected with the FMUs.\n\n\nCitation & white paper\n----------------------\n\nPlease find a white paper on the OMG toolbox including an exemplary usage scenario here:\n\n- https://arxiv.org/abs/2005.04869\n\nPlease use the following BibTeX entry for citing us::\n\n    @article{OMG-code2020,\n        title = {OMG: A Scalable and Flexible Simulation and Testing Environment Toolbox for Intelligent Microgrid Control},\n        author = {Stefan Heid and Daniel Weber and Henrik Bode and Eyke Hüllermeier and Oliver Wallscheid},\n        year = {2020},\n        doi = {10.21105/joss.02435},\n        url = {https://doi.org/10.21105/joss.02435},\n        publisher = {The Open Journal},\n        volume = {5},\n        number = {54},\n        pages = {2435},\n        journal = {Journal of Open Source Software}\n    }\n\n    @article{OMG-whitepaper2020,\n        title={Towards a Scalable and Flexible Simulation and\n               Testing Environment Toolbox for Intelligent Microgrid Control},\n        author={Henrik Bode and Stefan Heid and Daniel Weber and Eyke Hüllermeier and Oliver Wallscheid},\n        year={2020},\n        eprint={http://arxiv.org/abs/2005.04869},\n        archivePrefix={arXiv},\n        primaryClass={eess.SY}\n    }\n\n\nContributing\n------------\n\nPlease refer to the `contribution guide`_.\n\n.. _`contribution guide`: https://github.com/upb-lea/openmodelica-microgrid-gym/blob/master/CONTRIBUTING.rst\n\n\nCredits\n-------\n\nThis package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template.\n\n.. _Cookiecutter: https://github.com/audreyr/cookiecutter\n.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage\n"
  },
  {
    "path": "docs/JOSS/paper.bib",
    "content": "@Misc{Berkenkamp2020,\n  author = {Felix Berkenkamp},\n  title  = {{SafeOpt: Safe Bayesian Optimization}},\n  year   = {2020},\n  url    = {https://github.com/befelix/SafeOpt},\n}\n@misc{OpenAI2016,\n  Author = {Greg Brockman and Vicki Cheung and Ludwig Pettersson and Jonas Schneider and John Schulman and Jie Tang and Wojciech Zaremba},\n  Title = {OpenAI Gym},\n  Year = {2016},\n  url    = {https://arxiv.org/abs/1606.01540},\n  Eprint = {arXiv:1606.01540},\n}\n@Misc{OSMC2020,\n  author = {{Open Source Modelica Consortium (OSMC)}},\n  title  = {{OpenModelica}},\n  year   = {2020},\n  url    = {https://github.com/OpenModelica/OpenModelica},\n}\n\n\n@inproceedings{Fritzson2018,\n  author = {Peter Fritzson and Adrian Pop and Adeel Asghar and Bernhard Bachmann and Willi Braun and Robert Braun and Lena Buffoni and Francesco Casella and Rodrigo Castro and Alejandro Danós and Rüdiger Franke and Mahder Gebremedhin and Bernt Lie and Alachew Mengist and Kannan Moudgalya and Lennart Ochel and Arunkumar Palanisamy and Wladimir Schamai and Martin Sjölund and Bernhard Thiele and Volker Waurich and Per Östlund},\n  title = {{The OpenModelica Integrated Modeling, Simulation and Optimization Environment}},\n  year    = {2018},\n  crossref = {modelicaUS2018},\n  doi = {10.3384/ecp18154206}\n}\n\n@Misc{FMI2020,\n  author = {{Modelica Association}},\n  title  = {{Functional Mock-up Interface}},\n  year   = {2020},\n  url    = {https://fmi-standard.org/},\n}\n\n\n@Misc{PyFMI2020,\n  author = {{Modelon AB}},\n  title  = {{PyFMI}},\n  year   = {2020},\n  url    = {https://github.com/modelon-community/PyFMI},\n}\n\n\n@Article{Kroposki2008,\n  author  = {B. {Kroposki} and R. {Lasseter} and T. {Ise} and S. {Morozumi} and S. {Papathanassiou} and N. {Hatziargyriou}},\n  title   = {{Making Microgrids Work}},\n  doi = {10.1109/MPE.2008.918718},\n  journal = {IEEE Power and Energy Magazine},\n  year    = {2008},\n  volume  = {6},\n  number  = {3},\n  pages   = {40-53},\n}\n@Article{Lund2017,\n  author   = {Henrik Lund and Poul Alberg {\\O}stergaard and David Connolly and Brian Vad Mathiesen},\n  title    = {{Smart Energy and Smart Energy Systems}},\n  doi = {10.1016/j.energy.2017.05.123},\n  journal  = {Energy},\n  year     = {2017},\n  volume   = {137},\n  pages    = {556 - 565},\n  abstract = {In recent years, the terms “Smart Energy” and “Smart Energy Systems” have been used to express an approach that reaches broader than the term “Smart grid”. Where Smart Grids focus primarily on the electricity sector, Smart Energy Systems take an integrated holistic focus on the inclusion of more sectors (electricity, heating, cooling, industry, buildings and transportation) and allows for the identification of more achievable and affordable solutions to the transformation into future renewable and sustainable energy solutions. This paper first makes a review of the scientific literature within the field. Thereafter it discusses the term Smart Energy Systems with regard to the issues of definition, identification of solutions, modelling, and integration of storage. The conclusion is that the Smart Energy System concept represents a scientific shift in paradigms away from single-sector thinking to a coherent energy systems understanding on how to benefit from the integration of all sectors and infrastructures.},\n}\n@article{Garcia2015,\n  title={A Comprehensive Survey on Safe Reinforcement Learning},\n  author={Garc{\\i}a, Javier and Fern{\\'a}ndez, Fernando},\n  journal={Journal of Machine Learning Research},\n  volume={16},\n  number={1},\n  pages={1437--1480},\n  year={2015}\n}\n@Article{Brown2018,\n  author  = {T. Brown and J. H\\\"orsch and D. Schlachtberger},\n  title   = {{PyPSA: Python for Power System Analysis}},\n  doi     = {10.5334/jors.188},\n  journal = {Journal of Open Research Software},\n  year    = {2018},\n  volume  = {6},\n  eprint  = {1707.09913},\n  issue   = {1},\n}\n@Article{Thurner2018,\n  author  = {L. {Thurner} and A. {Scheidler} and F. {Sch\\\"afer} and J. {Menke} and J. {Dollichon} and F. {Meier} and S. {Meinecke} and M. {Braun}},\n  title   = {{Pandapower: An Open-Source Python Tool for Convenient Modeling, Analysis, and Optimization of Electric Power Systems}},\n  doi = {10.1109/TPWRS.2018.2829021},\n  journal = {IEEE Transactions on Power Systems},\n  year    = {2018},\n  volume  = {33},\n  number  = {6},\n  pages   = {6510-6521},\n}\n@Article{Guerrero2013,\n  author  = {J. M. {Guerrero} and M. {Chandorkar} and T. {Lee} and P. C. {Loh}},\n  title   = {{Advanced Control Architectures for Intelligent Microgrids, Part I: Decentralized and Hierarchical Control}},\n  journal = {IEEE Transactions on Industrial Electronics},\n  doi     = {10.1109/TIE.2012.2194969},\n  year    = {2013},\n  volume  = {60},\n  number  = {4},\n  pages   = {1254-1262},\n}\n@Article{Guerrero2013a,\n  author  = {J. M. {Guerrero} and P. C. {Loh} and T. {Lee} and M. {Chandorkar}},\n  title   = {{Advanced Control Architectures for Intelligent Microgrids, Part II: Power Quality, Energy Storage, and AC/DC Microgrids}},\n  journal = {IEEE Transactions on Industrial Electronics},\n  doi     = {10.1109/TIE.2012.2196889},\n  year    = {2013},\n  volume  = {60},\n  number  = {4},\n  pages   = {1263-1270},\n}\n@Misc{stable-baselines3,\n  author       = {Raffin, Antonin and Hill, Ashley and Ernestus, Maximilian and Gleave, Adam and Kanervisto, Anssi and Dormann, Noah},\n  howpublished = {\\url{https://github.com/DLR-RM/stable-baselines3}},\n  title        = {Stable Baselines3},\n  year         = {2019},\n  journal      = {GitHub repository},\n  publisher    = {GitHub},\n}\n@Misc{TFAgents,\n  author       = {Sergio Guadarrama and Anoop Korattikara and Oscar Ramirez and Pablo Castro and Ethan Holly and Sam Fishman and Ke Wang and Ekaterina Gonina and Neal Wu and Efi Kokiopoulou and Luciano Sbaiz and Jamie Smith and Gábor Bartók and Jesse Berent and Chris Harris and Vincent Vanhoucke and Eugene Brevdo},\n  howpublished = {\\url{https://github.com/tensorflow/agents}},\n  note         = {[Online; accessed 25-June-2019]},\n  title        = {{TF-Agents}: A library for Reinforcement Learning in TensorFlow},\n  year         = {2018},\n  journal      = {GitHub repository},\n  url          = {https://github.com/tensorflow/agents},\n}\n@Misc{plappert2016kerasrl,\n  author       = {Matthias Plappert},\n  howpublished = {\\url{https://github.com/keras-rl/keras-rl}},\n  title        = {keras-rl},\n  year         = {2016},\n  journal      = {GitHub repository},\n  publisher    = {GitHub},\n  url          = {https://github.com/keras-rl/keras-rl},\n}\n"
  },
  {
    "path": "docs/JOSS/paper.md",
    "content": "---\ntitle: 'OMG: A Scalable and Flexible Simulation and Testing Environment Toolbox for Intelligent Microgrid Control'\ntags:\n  - Python\n  - OpenModelica\n  - Microgrids\n  - Reinforcement Learning\n  - Energy Systems\n  - Simulation\n  - Testing\n  - Control\nauthors:\n  - name: Stefan Heid\n    affiliation: 1\n  - name: Daniel Weber\n    affiliation: 2\n  - name: Henrik Bode\n    affiliation: 2\n  - name: Eyke Hüllermeier\n    affiliation: 1\n  - name: Oliver Wallscheid\n    orcid: 0000-0001-9362-8777\n    affiliation: 2\naffiliations:\n - name: Chair of Intelligent Systems and Machine Learning, Paderborn University, Paderborn, Germany\n   index: 1\n - name: Chair of Power Electronics and Electrical Drives, Paderborn University, Paderborn, Germany\n   index: 2\ndate: 25 May 2020\nbibliography: paper.bib\n---\n\n# Summary\n\nThe OpenModelica Microgrid Gym (OMG) toolbox provides a transient simulation framework for local energy grids based on power electronic converters. OpenModelica is used as the backend, allowing users to set up arbitrary electric grid designs via its well-known graphical user interface in a plug-and-play fashion [@Fritzson2018]. Simulations can be configured using a python interface, making it easy to integrate software modules for the realization and testing of closed control loops. In addition, the OpenAI Gym interface is provided to connect data-driven reinforcement learning algorithms for investigating intelligent microgrid control approaches [@OpenAI2016]. \n\n![\\label{fig:omg}](omg.png)\n_Fig. 1:  Overview of the interconnections between the different parts of the  OMG  toolbox.  The  OpenModelica and  OpenAIGym logos are the property of their respective owners._\n\n\n# Background on microgrids and their control\n\nMicro- and smart-grids (MSG) play an important role for integrating renewable energy sources in conventional electricity grids and for providing power supply in remote areas [@Lund2017]. \nDue to their high efficiency and flexibility, power electronic converters are largely used to drive modern MSG.\nPower electronics describes the application of solid-state electronics to the control and conversion of electric power, which is largely performed with semiconductor switching devices such as diodes or power transistors.\nThis includes energy conversion in terms of voltage and current amplitude, frequency and phase angle, as well as the number of phases between two or more electrical energy systems to be connected.\n\n\nControlling MSGs is a challenging task due to the high requirements on energy availability, safety, and voltage quality. \nThis is particularly demanding due to the wide range of different MSG topologies depending on their field of application like industrial campuses, residential areas or remote off-grid electrification [@Kroposki2008].\nThis results in high demand for comprehensive testing of new control concepts during their development phase and comparisons with the state of the art to ensure their feasibility.\nThis applies in particular to data-driven control approaches such as reinforcement learning (RL), the stability and operating behavior of which cannot be evaluated a priori [@Garcia2015].\n\n\n# State of field \n``OMG`` is a Python-based package for the modeling and simulation of microgrids based on power electronic energy conversion.\nThe OpenModelica [@Fritzson2018] library enables the user to define their microgrid (i.e. a local electricity grid containing arbitrary sources, storages and loads) in a flexible and scalable way or to use certain predefined example grids. \nDue to the component-oriented modeling framework based on OpenModelica, dynamic processes on small time scales are in focus, which allows for accurate control and test investigations during transients and steady-state.\nThis is an essential difference to already available open-source solutions for the simulation of electrical energy networks, which, in contrast, generally depict large-scale transmission networks with abstracted models in the (quasi)-stationary state (e.g. PyPSA [@Brown2018] or Pandapower [@Thurner2018]). In addition to the pure modeling and simulation of microgrids, basic building blocks for setting up a hierarchical control framework on the inner and primary level [@Guerrero2013] are provided with ``OMG``. \n\n\n# Interfaces for control and reinforcement learning  \nThe API is designed to provide a user-friendly interface to connect a modeled microgrid (the simulation environment) with a wide range of control methods such as classical linear feedback control or model predictive control techniques (cf. Fig. 1). Moreover, the standardized OpenAI Gym interface [@OpenAI2016] is also available for training data-driven control approaches like RL. \nThis enables users who want to integrate contemporary open-source Python-based RL toolboxes such as ``Stable Baselines3`` [@stable-baselines3], ``TF-Agents`` [@TFAgents] or ``keras-rl`` [@plappert2016kerasrl].\nMany auxiliary functionalities for the essential operation of microgrids are shipped with OMG such as coordinate transformations for basic controller classes, monitoring wrappers, and phase-locked loops for frequency and phase angle extraction. \nFollowing this structure, nearly every control approach, including data-driven RL, can be implemented and tested with ``OMG`` in a relatively short amount of time. \nTo highlight the challenges of data-driven control approaches in safety-critical environments, application examples using safe Bayesian optimization [@Berkenkamp2020] for automated controller design are provided in the toolbox. \n\n\n# Intended use and targeted audience\n``OMG`` is designed to be used by students, academics, and industrial researchers in the field of control and energy engineering and data science. The primary objective of the toolbox is to facilitate entry for new users into the modeling, control, and testing of microgrids and to provide a platform on which different control methods (including RL) can be compared under defined conditions (benchmarks).\n\n\n\n# Features\n\nThe ``OMG`` toolbox provides the following key features:\n\n\n* A library for the scalable and flexible design of local electricity grids in OpenModelica.\nUsers can select between a wide range of different grid components and connect them in a plug-and-play approach.\n\n* Dynamic simulation of local electricity grids on component level including single and multi-phase systems as well as AC and DC operation. \n\n* Easy exchange of models between computing platforms and simulation of the models by using the FMI 2.0 standard [@FMI2020] with C++ code inside and PyFMI [@PyFMI2020] for access in Python. Appropriate numeric solvers for the underlying system of ordinary differential equations can be easily chosen within the usual Python packages (e.g. SciPy) due to the usage of co-simulation. \n\n* Calculation, evaluation and monitoring of every single time step covering states, action and auxiliary quantities provides an interface for manual or automated inspection. The latter is particularly useful for the automatic training of data-driven control approaches such as reinforcement learning.\n\n* Large variety of predefined and parameterizable controllers (droop, voltage, current in multi- and singlephase) available, easy implementation of user-defined control structures possible.\n\n* Monitoring tools to follow the live performance of the RL agent and to map the overall grid behaviour depending of each selected parameter set\n\n* Interesting use cases applying safe data-driven learning to highlight the requirement of safety in a delicate control environment are available.\n\n# Examples\n\nDetailed examples are shown in the OMG whitepaper (https://arxiv.org/pdf/2005.04869.pdf) including the implementation and evaluation of \na safe Bayesian controller [@Berkenkamp2020].\nThe SafeOpt learning algorithm is applied to an automatic controller tuning problem with safety-relevant state constraints in different microgrid topologies (e.g. different number of inverters, load characteristics). \nFurthermore, the provided evaluation tools enable users to compare the performance of different RL algorithms against each other and against manually tuned inverters. \n\n\n\n# Availability and installation\n\n``OMG`` is supported and tested on Linux and Windows. Mac users are asked to run this toolbox on a Linux VM. \nThe package should be installed in a conda environment. ``PyFMI`` can be installed via `conda install -c conda-forge pyfmi`, the ``OMG`` package by `pip` Python package manager using \n`pip install openmodelica_microgrid_gym` command. The source code, guide and \ndatasets are available on the GitHub repository (https://github.com/upb-lea/openmodelica-microgrid-gym). \n\n# Individual contributions of the authors\n\nFollowing are shown the main fields of each individual contributor of OMG: \n\n* S. Heid: Main software architecture, software module integration, unit tests \n\n* D. Weber: Application examples, control-related auxiliary features (e.g. basic expert controllers), unit tests\n\n* H. Bode: Design of the specific OpenModelica library created for OMG, grid modelling and simulation, data transfer between OpenModelica and Python, unit tests\n\n* O. Wallscheid: Concept design and idea generation, testing and technical feedback, administrative project management\n\n* E. Hüllermeier: Administrative project management, concept-oriented feedback\n\n\n\n# Acknowledgements\n\nThe authors kindly acknowledge the valuable contributions and advice regarding grid and controller design by Jarren Lange. The authors would also like to acknowledge the funding and support of this work by the Paderborn University research grant. \n\n# References\n\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the environment for the first two.\nSPHINXOPTS    ?=\nSPHINXBUILD   ?= sphinx-build\nSOURCEDIR     = .\nBUILDDIR      = _build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n"
  },
  {
    "path": "docs/api/omg.agents.agent.rst",
    "content": "omg.agents.agent\n==================================\n\n.. automodule:: openmodelica_microgrid_gym.agents.agent\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.agents.episodic.rst",
    "content": "omg.agents.episodic\n===================================================\n\n.. automodule:: openmodelica_microgrid_gym.agents.episodic\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.agents.rst",
    "content": "omg.agents\n=============================\n\nSubmodules\n----------\n\n.. toctree::\n\n   omg.agents.agent\n   omg.agents.episodic\n   omg.agents.safeopt\n   omg.agents.staticctrl\n   omg.agents.util\n\nModule contents\n---------------\n\n.. automodule:: openmodelica_microgrid_gym.agents\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.agents.safeopt.rst",
    "content": "omg.agents.safeopt\n====================================\n\n.. automodule:: openmodelica_microgrid_gym.agents.safeopt\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.agents.staticctrl.rst",
    "content": "omg.agents.staticctrl\n=======================================\n\n.. automodule:: openmodelica_microgrid_gym.agents.staticctrl\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.agents.util.rst",
    "content": "omg.agents.util\n=================================\n\n.. automodule:: openmodelica_microgrid_gym.agents.util\n   :members:\n   :special-members:\n   :exclude-members: __dict__,__module__,__repr__,__weakref__\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.aux_ctl.base.rst",
    "content": "omg.aux_ctl.base\n======================================\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl.base\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.aux_ctl.droop_controllers.rst",
    "content": "omg.aux\\_ctl.droop\\_controllers\n===============================================================\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl.droop_controllers\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.aux_ctl.filter.rst",
    "content": "omg.aux_ctl.filter\n========================================\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl.filter\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.aux_ctl.inverter_controllers.rst",
    "content": "omg.aux_ctl.inverter\\_controllers\n================================================\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl.inverter_controllers\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.aux_ctl.observers.rst",
    "content": "omg.aux\\_ctl.observers\n======================================================\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl.observers\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.aux_ctl.params.rst",
    "content": "omg.aux_ctl.params\n========================================\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl.params\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.aux_ctl.pi_controllers.rst",
    "content": "omg.aux_ctl.pi_controllers\n====================================\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl.pi_controllers\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.aux_ctl.rst",
    "content": "omg.aux_ctl\n==================================\n\nSubmodules\n----------\n\n.. toctree::\n\n   omg.aux_ctl.base\n   omg.aux_ctl.filter\n   omg.aux_ctl.params\n   omg.aux_ctl.observers\n   omg.aux_ctl.droop_controllers\n   omg.aux_ctl.pi_controllers\n   omg.aux_ctl.inverter_controllers\n\nModule contents\n---------------\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.env.modelica.rst",
    "content": "omg.env.modelica\n==================================\n\n.. automodule:: openmodelica_microgrid_gym.env.modelica\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.env.plot.rst",
    "content": "omg.env.plot\n==================================\n\n.. automodule:: openmodelica_microgrid_gym.env.plot\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.env.plotmanager.rst",
    "content": "omg.env.plotmanager\n===================================================\n\n.. automodule:: openmodelica_microgrid_gym.env.plotmanager\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.env.pyfmi.rst",
    "content": "omg.env.pyfmi\n=============================================\n\n.. automodule:: openmodelica_microgrid_gym.env.pyfmi\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.env.rst",
    "content": "omg.env\n==========================\n\nSubmodules\n----------\n\n.. toctree::\n\n   omg.env.modelica\n   omg.env.pyfmi\n   omg.env.plot\n   omg.env.plotmanager\n\nModule contents\n---------------\n\n.. automodule:: openmodelica_microgrid_gym.env\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.execution.callbacks.rst",
    "content": "omg.execution.callbacks\n=======================================================\n\n.. automodule:: openmodelica_microgrid_gym.execution.callbacks\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.execution.rst",
    "content": "omg.execution\n================================\n\nSubmodules\n----------\n\n.. toctree::\n\n   omg.execution.runner\n   omg.execution.callbacks\n\nModule contents\n---------------\n\n.. automodule:: openmodelica_microgrid_gym.execution\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.execution.runner.rst",
    "content": "omg.execution.runner\n======================================\n\n.. automodule:: openmodelica_microgrid_gym.execution.runner\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.net.base.rst",
    "content": "omg.net.base\n============================================\n\n.. automodule:: openmodelica_microgrid_gym.net.base\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.net.components.rst",
    "content": "omg.net.components\n==================================================\n\n.. automodule:: openmodelica_microgrid_gym.net.components\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.net.rst",
    "content": "omg.net\n========================================\n\nSubmodules\n----------\n\n.. toctree::\n   :maxdepth: 4\n\n   omg.net.base\n   omg.net.components\n\nModule contents\n---------------\n\n.. automodule:: openmodelica_microgrid_gym.net\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.util.fastqueue.rst",
    "content": "omg.util.fastqueue\n==================================================\n\n.. automodule:: openmodelica_microgrid_gym.util.fastqueue\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.util.itertools_.rst",
    "content": "omg.util.itertools\\_\n========================================\n\n.. automodule:: openmodelica_microgrid_gym.util.itertools_\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.util.randproc.rst",
    "content": "omg.util.randproc\n==================================\n\n.. automodule:: openmodelica_microgrid_gym.util.randproc\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.util.recorder.rst",
    "content": "omg.util.recorder\n==================================\n\n.. automodule:: openmodelica_microgrid_gym.util.recorder\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.util.rst",
    "content": "omg.util\n=============================\n\nSubmodules\n----------\n\n.. toctree::\n\n   omg.util.fastqueue\n   omg.util.itertools_\n   omg.util.transforms\n   omg.util.randproc\n   omg.util.recorder\n\nModule contents\n---------------\n\n.. automodule:: openmodelica_microgrid_gym.util\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/api/omg.util.transforms.rst",
    "content": "omg.util.transforms\n=======================================\n\n.. automodule:: openmodelica_microgrid_gym.util.transforms\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# Configuration file for the Sphinx documentation builder.\n#\n# This file does only contain a selection of the most common options. For a\n# full list see the documentation:\n# http://www.sphinx-doc.org/en/master/config\n\n# -- Path setup --------------------------------------------------------------\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\nimport os\nimport sys\n\nsys.path.insert(0, os.path.abspath('..'))\nsys.setrecursionlimit(1500)\n# -- Project information -----------------------------------------------------\n\nproject = 'OpenModelica Microgrid'\ncopyright = '2020, Bode, Heid, Wallscheid, Weber'\nauthor = 'Bode, Heid, Wallscheid, Weber'\n\n# The short X.Y version\nversion = ''\n# The full version, including alpha/beta/rc tags\nrelease = '2020'\n\n# -- General configuration ---------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.autodoc',\n    'sphinx_autodoc_typehints',\n    'sphinx.ext.coverage',\n    'sphinx.ext.mathjax',\n    'sphinx.ext.viewcode',\n    'sphinx.ext.napoleon',\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\nsource_suffix = ['.rst']\n# source_suffix = '.rst'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# Include Constructor documentation\nautoclass_content = 'both'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path .\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'sphinx_rtd_theme'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\n# html_theme_options = {}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n# Custom sidebar templates, must be a dictionary that maps document names\n# to template names.\n#\n# The default sidebars (for documents that don't match any pattern) are\n# defined by theme itself.  Builtin themes are using these templates by\n# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',\n# 'searchbox.html']``.\n#\n# html_sidebars = {}\n\n\n# -- Options for HTMLHelp output ---------------------------------------------\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'LEA-RLdoc'\n\n# -- Options for LaTeX output ------------------------------------------------\n\nlatex_elements = {\n    # The paper size ('letterpaper' or 'a4paper').\n    #\n    # 'papersize': 'letterpaper',\n\n    # The font size ('10pt', '11pt' or '12pt').\n    #\n    # 'pointsize': '10pt',\n\n    # Additional stuff for the LaTeX preamble.\n    #\n    # 'preamble': '',\n\n    # Latex figure (float) alignment\n    #\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, 'LEA-RL.tex', 'LEA-RL Documentation',\n     'Wallscheid, Weber, Heid, Bode', 'manual'),\n]\n\n# -- Options for manual page output ------------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    (master_doc, 'lea-rl', 'LEA-RL Documentation',\n     [author], 1)\n]\n\n# -- Options for Texinfo output ----------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (master_doc, 'LEA-RL', 'LEA-RL Documentation',\n     author, 'LEA-RL', 'One line description of project.',\n     'Miscellaneous'),\n]\n\n# -- Extension configuration -------------------------------------------------\n"
  },
  {
    "path": "docs/index.rst",
    "content": "Welcome to OpenModelica Microgrid Gym Toolbox Documentation!\n=====================================================================\n\nThe OpenModelica Microgrid Gym (OMG) package is a software toolbox for the simulation of power electronics-driven microgrids and to\ntrain and test reinforcement learning agents and to compare them with classical control approaches.\n\nContent\n*******\n\nIn the examples section all available use cases are presented with their default configuration.\nFor quick start, one of these can be selected and used out of the box.\n\n\nThe documentation of the base classes is important for the development of own modules like further reward functions or\nreference generators. In this part, the basic interfaces of each module are specified.\nFor the creation of additional grid constellations, Openmodelica (nightly build recommended) can be used.\n\n\n.. toctree::\n   :maxdepth: 4\n   :titlesonly:\n   :caption: User Guide:\n\n   parts/user_guide/getting_started\n   parts/user_guide/OpenModelica\n   parts/user_guide/fmu\n   parts/user_guide/Pythoncode\n   parts/user_guide/examples\n   parts/user_guide/controller_tuning\n\n\n.. toctree::\n   :maxdepth: 4\n   :titlesonly:\n   :caption: API:\n\n   api/omg.agents\n   api/omg.aux_ctl\n   api/omg.env\n   api/omg.execution\n   api/omg.net\n   api/omg.util\n\n\n.. GENERATE APIDOC\n.. - sphinx-apidoc -o docs/api openmodelica_microgrid_gym/ -e\n.. - delete module.rst\n.. - remove package and module names:\n..   - execute regex '.* package$' ''\n..   - execute regex '.* module$' ''\n\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/make.bat",
    "content": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset SOURCEDIR=.\nset BUILDDIR=_build\n\nif \"%1\" == \"\" goto help\n\n%SPHINXBUILD% >NUL 2>NUL\nif errorlevel 9009 (\n\techo.\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\n\techo.installed, then set the SPHINXBUILD environment variable to point\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\n\techo.may add the Sphinx directory to PATH.\n\techo.\n\techo.If you don't have Sphinx installed, grab it from\n\techo.http://sphinx-doc.org/\n\texit /b 1\n)\n\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\ngoto end\n\n:help\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\n\n:end\npopd\n"
  },
  {
    "path": "docs/parts/user_guide/OpenModelica.rst",
    "content": "OpenModelica\n============\n\n`OpenModelica <https://openmodelica.org/>`__ is an open-source\nModelica-based modeling and simulation environment intended for\nindustrial and academic usage.\n\nInstallation of OpenModelica\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe models shown below were created by using\n`OMEdit <https://openmodelica.org/download/download-windows>`__ v1.16.\n\nUsing a Linux OS, sometimes may lead to problems while trying to install\nOpenModelica. In this case, try to download the pre-built `virtual\nmachine <https://openmodelica.org/download/virtual-machine>`__.\n\nMac users are strongly encouraged to run OpenModelica as well as the OMG toolbox in a Linux VM.\n\nCreating Microgrids with OpenModelica\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe microgrids are created with a  user defined library. To start it, run the file *grid.mo* directly in the folder *omg_grid*.\n\n\nThis package contains all components and required for\ncreating the power electronics driven microgrids, as well as some example networks.\n\n.. figure:: ../../pictures/library1.jpg\n   :alt: \n\nIt contains several folders with predefined components,\nfilters, loads etc. as well as microgrids for the FMU export to Python and some stand-alone examples which can be run directly in OpenModelica.\n\n\n\n.. figure:: ../../pictures/omedit.jpg\n   :alt: \n\nMain components of any microgrid are the three-phase inverters. They consist\nof three input voltages controlled by a cascaded PI-PI controller in\nPython. Default nomenclature for those inputs is i1p1 for\n\"**i**\\ nverter 1 **p**\\ hase 1\" etc, but it can be changed in the\npython code (model\\_input=['one', 'two',...] in the env=gym.make()\ncall).\n\n**Important**: By using filters/loads with inductors or capacitors,\nleave the checkbox for the initialization in the provided settings. Changes are likely to\nresult in errors while creating the FMU or running the Python files.\n\nThe provided examples are designed for up to two inverters, but the underlying models can be\neasily extended in order to investigate on more complex microgrid topologies.\nExtended example showcases are also planed for future releases.\n\nPower Losses\n^^^^^^^^^^^^\n\nIn the default example \"network\", no power losses in the inverters or filters are included.\nFor the latter they can be added by using parts out of the \"Filter.LossesFilter\" package instead of\nthe \"Filter.IdealFilter\" package. Due to a big increase of components and\nequations in the ODE-system, the simulation time will increase.\nFor modeling losses inside the power electronic converters, adding a model in the Python interface\nscripts is recommending. Integrating, e.g switching losses, directly in the OpenModelia model will\nrequire to reduce to simulation step size significantly.\n\nFor larger simulations with a demand of power loss modeling, it is recommended to\ncreate user defined filters with only the resistors which are needed for\nthe calculation. To modify them, create a new package (ctrl+N,\nspecialization: package), duplicate the part which is to modify in the\nnew package (right-click on it, duplicate, select the previously created\npackage in path) and modify it there.\n\nSwitches / Transistors\n^^^^^^^^^^^^^^^^^^^^^^\n\nModeling switching-like events inside the model,\ne.g. triggering loadsteps by adding or removing loads, is\ndesirable, but difficult to implement with the possibilities of\nOpenModelica. Switches in OpenModelica - like in many other free\nmodelling languages - are designed as resistors. A closed switch has a\nlow resistance, an open switch a high one. \"Removed\" loads are still\nconnected to the grid. Connections with resistors in such dimension\ncause numerical issues while simulating as the ODE system becomes stiff.\nThere are solvers available for stiff equation systems like BDF and\nRadau or ones with automatic stiffness detection, but using the switches\noften runs into non-converging systems and execution errors.\n\nThe working alternative is a parameter-variation of the load. It is\npossible to change the parameters of any load during a simulation and\napply loadsteps in this way (see the topic\n`Python code <Pythoncode.html>`__).\n\nSetting of v\\_DC\n^^^^^^^^^^^^^^^^\n\nThe DC supply voltage *v*\\_DC can be set either directly in the\nOpenModelica model or via `Python <Pythoncode.html#setting-of-v-dc>`__.\nIn the OM model, doubleclick in your network on the inverter, and change\nthe parameter *v*\\_ DC to the demanded value. All three phases of the\ninverter will be supplied with the same DC voltage. The default value is\n1000 V. The default value can be changed with a right-click on an\ninverter, *open class*, select *text view* on the top left corner of the\nmodel canvas, and change the number in the following code line to\nthe demanded default value:\n\n::\n\n      parameter Real v_DC = 1000;\n      \n\nPhase-Locked Loop (PLL)\n^^^^^^^^^^^^^^^^^^^^^^^\n\nThe PLL blocks are working for simulations in OpenModelica, but out of\nstructural reasons, the PLL is calculated in Python.\n"
  },
  {
    "path": "docs/parts/user_guide/Pythoncode.rst",
    "content": "Toolbox Installation and General Remarks\n================================\n\n\nIn the following, some installation hints and an introduction to the Python code written for the OMG\ntoolbox is presented.\n\n\nInstallation and Requirements\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis is the short installation guide for Windows and Linux. `OpenModelica <https://openmodelica.org/download/download-mac>`__ is hardly supported for Mac, they suggest to install in a Linux VM. For this reason, running OMG in a Linux `VM <https://openmodelica.org/download/virtual-machine>`__ is strongly recommended for Mac users!\n\n\n\nIt is recommended to install OMG via pip:\n\n::\n\n    pip install openmodelica_microgrid_gym\n\nAlternatively, you can clone the GitHub repository. A list of\nrequirements is provided in the\nhome-directory.\n\n.. literalinclude:: ../../../requirements.txt\n\n\n**Hint:** If you are running a windows, PyFMI might throw some errors\nwhile installing via pip. It can be installed via *conda* by running:\n\n::\n\n    conda install -c conda-forge pyfmi \n\nSimulation Settings\n~~~~~~~~~~~~~~~~~~~\n\nHeart of the program structure is the creation of the environment via\n**gym.make()** in the main programm (in the folder example). Nearly\nevery simulation setting can be done directly in here. Some of the most\nimportant ones are described in the following. For further information, see the `API-documentation <../../api/omg.env.modelica.html>`__.\n\n-  **time\\_step:** step size of the simulation in seconds. Too large\n   timesteps may result in numerical issues, small timesteps result in a\n   high simulation time. 1e-4 seems to be a good compromise as many real\n   controllers operate in timesteps like this.\n\n-  **reward\\_fun:** Callable - Reward function for the RL-algorithm. Minimal value\n   of rewards is for example used as lower bound for the safe Bayseian\n   algorithm (see single_inverter_current_control_safe_opt.py). Has to be adjusted problem-specific.\n\n-  **solver\\_method:** Solver used for the ODE system. Every solver from\n   `scipy.integrate.solve\\_ivp <https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html>`__\n   can be selected. Though it is highly recommended to use the implicit\n   ones. Default solver is the \"LSODA\" with its integrated stiffness\n   detection. Non-stiff systems become solved with the faster \"Adams\"\n   method, stiff systems with \"BDF\".\n\n\n-  **model\\_params:** Parameters for the simulation, which should be\n   changed compared to the default values from the OpenModelica model.\n   Also usable for loadsteps as replacement for\n   `switches <OpenModelica.md>`__.\n\nExample which increases the resistors in the load after 0.2 seconds from\n20 Ohm to 40 Ohm:\n\n::\n\n    def f(t):\n        return 20 if t < .2 else 40\n\n    model_params={'rl.resistor1.R': f, 'rl.resistor.R': f, 'rl.resistor.R': f},\n\nThe function :code:`f` is passed as a callable (function reference).\nThe environment will evaluate this function in every timestep passing this timestep as parameter to the function\nautomatically.\n\n\nSetting of v\\_DC\n~~~~~~~~~~~~~~~~\n\nThe DC supply voltage v\\_DC can be set either directly in the\n`OpenModelica model <OpenModelica.html#setting-of-v-dc>`__ or via\nPython. The default value is 1000 V. It can be changed in the\nenvironment creation with the line:\n\n::\n\n    model_params={'inverter1.v_DC': 700, 'inverter2.v_DC': 500}, \n\nIt will be set for every of the three phases of the inverter. Take care\nto set the param for every inverter which should no have the default\nsupply voltage of 1000 V.\n\nData Logging\n~~~~~~~~~~~~\n\nTo enable logging, the root logger needs to be initialized in the\nmain function. To do so, call:\n\n::\n\n    import numpy as np\n\n    logging.basicConfig()\n\n    if __name__ == '__main__':\n        ctrl = dict()\n\nFor further information about logging and the level see the `logging\nstandard library <https://docs.python.org/3/library/logging.html>`__.\n"
  },
  {
    "path": "docs/parts/user_guide/controller_tuning.rst",
    "content": "Controller Tuning Hints\n=======================\n\n1. Current controller of primary inverter\n\n-  With no droop, in other words constant mains frequency, apply a short\n   circuit to inverter and tune Kp, Ki of the current controller. Can\n   tune Kp then Ki, finally Kp again if both can’t be tuned at the same\n   time.\n\n   -  Try tune for 90-95% of max current. Not peak current limit, but\n      maximum allowed during nominal operation.\n   -  Ensure that the tuning does not allow the current to exceed the\n      peak current limit. In real world scenarios this is when an\n      inverter will shutoff or explode.\n\n2. Voltage controller of primary inverter\n\n-  With no droop and an open circuit load of just the inverter tune Kp,\n   Ki of the voltage controller. Can tune Kp then Ki, finally Kp again\n   if both can’t be tuned at the same time.\n\n3. PLL of secondary inverter\n\n-   With primary inverter providing a constant frequency start tuning\n    Kp, Ki of the PLL. Noise on the frequency output of the PLL is\n    acceptable, as long as the output phasors of the PLL accurately\n    match the incoming voltage signal.\n-   The secondary inverter power electronics should be\n    disconnected/open-circuit for this.\n-   If possible inject step changes to the frequency setpoint of the\n    primary inverter, watching how accurately the PLL tracks the\n    external voltage reference. Continue tuning if necessary.\n\n4. Current controller of secondary inverter\n\n-   With the PLL of the secondary inverter locked to the primary inverter\n    tune the Kp, Ki of the current controller. No droops at this stage.\n\n   -  Might need to create a step change for the setpoint for this to\n      test accurate tracking.\n   -  Try tune for 90-95% of max current. Not peak current limit, but\n      maximum allowed during nominal operation.\n\n5. Droop of the primary inverter\n\n-  This isn’t really a tuning step as the parameters won’t affect any\n   other system.\n\n6. Droops of the secondary inverter\n\n-  Firstly, the filter for the frequency and the voltage feedback to the\n   droop controllers should be set before tuning the droop.\n-  Tune the droop controllers of the secondary inverter.\n\n   -  The frequency of the filter (for the frequency feedback from the\n      PLL) affects the droop parameters and should thus be considered in\n      the tuning.\n\n\n"
  },
  {
    "path": "docs/parts/user_guide/examples/basic_agent.rst",
    "content": "Creating an agent and a runner\n==============================\n\nAdditionally to the environment, an an agent will be created and a runner will be used. The runner class will take care of initializing and termination\nof agents and environments, as well as the execution of multiple episodes. The class will handle all information\nexchange between agent and environment like presented in the high level code architecture shown below:\n\n.. figure:: ../../../pictures/highlevel.png\n   :width: 400\n   :alt:\n\nSince the inputs are used for both the agent and the environment, they are defined in advance. Although the Agent gets information of the environment, in this small example, its action is still a random number.\n\nThe environment is the same as above. Afterwards, the agent and the runner get defined, and the runner runs for one episode.\n\n.. literalinclude:: ../../../../examples/simple_agent.py\n   :linenos:"
  },
  {
    "path": "docs/parts/user_guide/examples/creating_env.rst",
    "content": "Creating an environment\n=======================\n\n\nFollowing is a minimal example how to set-up and run an environment.\nNecessary is the definition of model inputs, in this case the three phases of the first inverter.\nThe model outputs will be shown as simulation results, and the model path is the relative location of the FMU file, which contains the network.\nFor any other simulation parameters, for example the step-size, default values will be used.\n\nFor the initialisation, the environment needs to be reseted, and env.render will define the output plots.\nThe simulation will perform 1000 steps. A different random number will be provided to every of the three previously defined model_inputs.\nAfterwards, the inductor currents of the LC-filter \"lc1\"shown in the figure above will be plotted, which should result in three increasing and due to the random function noisy  lines.\n\n.. literalinclude:: ../../../../examples/basic_env.py\n   :linenos:"
  },
  {
    "path": "docs/parts/user_guide/examples/plotting.rst",
    "content": "Creating plots of environment variables\n=======================================\n\nIn this example is shown how the environment variables can be plotted and how the plots can be adjusted.\nThe environment gets an object of the class PlotTmpl, where can be chosen which environment variables are visualized.\nFor style, linestyle and color the grouping is detected in comparison to the environment variables to be plotted.\nAjdusting labels and saving the figure can be done via callable, like shown in the example below.\n\n.. literalinclude:: ../../../../examples/plotting.py\n   :linenos:"
  },
  {
    "path": "docs/parts/user_guide/examples/single_inverter_current_control_safe_opt.rst",
    "content": "Single Inverter Current Control\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn this example a three phase inverter is supplying a load (rl1) via a filter (lc1)\nlike shown in the figure below. From that model a FMU is\nbuilt to create the environment.\n\n.. figure:: ../../../pictures/Model.png\n\nAn optimization method developed by `Berkenkamp et al.`_ called Safe Controller Optimization (safeopt) is used which takes a Gaussian process and Bayesian\noptimization to safely determine \"optimal\" controller parameters. The\ngoal of the standard PI current controller is to supply an exemplary 15 A d-current\nto the load.\n\n.. _`Berkenkamp et al.`: https://arxiv.org/abs/1509.01066\n\nThe `generated FMU <fmu.html>`__ is used in the environment to build up\na gym env like the examples from OpenAI Gym (https://gym.openai.com/).\nThe gym enviroment is defined in (examples/single\\_inverter\\_current\\_control\\_safe\\_opt.py).\nIt generates a gym environment using\n\n- a reward function,\n- plotting the inductor values (current) from the lc1-filter (which should be controlled) like shown in the figure below,\n- simulating 300 timesteps of delta\\_t of the FMU grid.network\\_singleInverter.fmu (generated from the model in the plot above),\n- using the setpoints for the inverters (modulation indices) i1p{1,2,3} as inputs,\n- and the inductor currents and capacitor voltages of lc1-filter as outputs.\n\n.. figure:: ../../../pictures/i_abc_bk_kp15_Ki121.png\n\nThe agent used in this simple RL-example is taken from the class\n:code:`SafeOptAgent`. It contains the controller a\n:code:`MultiPhaseDQCurrentSourcingController`, which consists of multiphase\n(3) PI controllers to control the current across the inductor of the\nlc1-filter. There are also droop controllers implemented to calculate\ne.g. the frequency drop due to load changes. The agent's task is to find better\nparameters for the current controllers (Kp & Ki). Therefore, they are\ndefined as mutable\\_params (e.g.\nexamples/single\\_inverter\\_current\\_control\\_safe\\_opt.py) to\nadopt them between the episodes. The SafeOpt algorithm uses a Gaussian\nprocess to estimate the performance of the controller. Thus, the\nbounds and the lengthscale (c.f. examples/single\\_inverter\\_current\\_control\\_safe\\_opt.py) for\nthe gain parameters (Kp and Ki) have to be defined.\n\nOne can adjust one of the parameters (Kp or Ki) (1D case) or both of them\n(2D case) using the algorithm. Therefore, the following flag parameters have to\nbe adjusted accoridngly:\n\n- To adjust only Kp set :code:`adjust = 'Kp'`\n- To adjust only Ki set :code:`adjust = 'Kp'`\n- To adjust only Kp and Ki set :code:`adjust = 'Kpi'`\n\nDue to SafeOpt the agent need a safe starting point (Kp and Ki). Then it\ntries to calculate safely parameters with better performance. The\nperformance is calculated using the reward function from the environment.\nThere the mean-root-error (RME) from the measured currents and the setpoints are\ncalculated. Additionally a barrier function is used to penalize\nover-currents. The barrier function can be adjusted using the parameter mu.\n\nThe safe threshold for the agent is set as safe\\_threshold-times of\nthe initial performance (c.f. agents/safeopt.py). For example,\nsafe\\_threshold = 1.2 and the initial reward is -10 the safe threshold\nwould be -12.\n\nIn the end of the script a :code:`Runner` is used to execute 10 episodes\nusing the agent to control the environment. For every episode the\ncontrolled currents and the performance function as a function of Kp\nand/or Ki are plotted.\n\nSome exemplary results are shown below:\n\n-  If :code:`adjust == 'Kp'`, the agent tries to\n   find an optimal value for the proportional gain (Kp) of the\n   controller in the range of [0, 0.03] with a\n   lengthscale of 0.01. In the figure below on the x-axis is\n   the value for Kp and on the y-axis the performance value calculated\n   using the reward function mentioned above.\n\n.. figure:: ../../../pictures/kp_J.png\n\n-  If :code:`adjust == 'Ki'`, the agent tries to\n   find an optimal value for the integral gain (Ki) of the controller in\n   the range of [0, 300]  with a lengthscale of 50. In the figure below on the x-axis is the value for Ki and\n   on the y-axis the performance value calculated using the reward\n   function mentioned above.\n\n.. figure:: ../../../pictures/ki_J.png\n\nThe - due to the algorithm - \"unsafe\" point on the right (for Kp as well\nas for Ki) is not due to overcurrent but due to bad performance due to\npermanent control error. The resulting currents for Kp = 0.01 and Ki = 0 (\"unsafe\" point on the right in the figure above)\nis shown in the picture below. Due to the high error compared to the\nreference value (15 A d-current), the performance is as bad as the\nalgorithm defines it as unsafe - in comparison to the performance\nreached using the initial controller parameters.\n\n.. figure:: ../../../pictures/i_abc_ki_J_bad.png\n\n-  If :code:`adjust == 'Kpi'`, the agent tries to\n   find an optimal value for the proportional gain (Kp) as well as for\n   the integral gain (Ki) of the controller in the ranges of [0, 0.03]\n   and a lengthscale of 0.01 for Kp and a range of [0, 300] and a\n   lengthscale of 50 for Ki. In the figure below on the x-axis is the\n   value for Kp, the y-axis the value for Ki and the z-axis the\n   performance value calculated using the reward function.\n\n.. figure:: ../../../pictures/kp_ki_J.png\n\nThe results of the algorithm are printed into the console in the form\nlike below:\n\nIteration, performance J, Params [Kp, Ki]\n\n::\n\n           J                                      Params\n    0  -0.527522                                [0.01, 10.0]\n    1  -0.442648    [0.01517286546392185, 14.85163114970222]\n    2  -0.318154    [0.01426989823624961, 44.96747682456248]\n    3  -0.296940   [0.007935547159879385, 63.12800825929393]\n    4  -0.286636    [0.01482713453607815, 88.70170996759624]\n    5  -0.286815  [0.006770598304777539, 108.12303673537075]\n    6  -0.280167  [0.013261084415467694, 135.24448051372738]\n    7  -0.313204   [0.02201710533671064, 56.446583269542394]\n    8  -1.387003  [0.022868977920736434, 108.40140778199653]\n    9  -0.304403   [0.002145673177669012, 55.14569829606201]\n    10 -0.480421   [0.026197353734745858, 22.29566509028389]\n    11 -1.097157  [0.0055262530542335535, 157.4879776902759]\n    12 -0.391706                    [0.0, 17.86728037560901]\n    13 -1.307038                    [0.0, 106.0724160092763]\n    14 -1.561142                    [0.03, 42.1020413015999]\n\nThe best performance in this short example of -0.280167 produces the\nparameter set of Kp = 0.0132... and Ki = 135.244...\n"
  },
  {
    "path": "docs/parts/user_guide/examples/two_inverter_static_droop_control.rst",
    "content": "Two Inverter Static Droop Control\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn this example, a FMU generated by OpenModelica as gym environment containing two inverters, each connected via a\nfilter to supply in parallel a RC load is used which is shown in the figure below.\nThis example uses the controllers as defined in the auxiliaries. One inverter is set up as voltage forming inverter with a\ndirect droop controller which e.g. frequency drops due to the applied power. The other controller is used as current\nsourcing inverter with an inverse droop controller which reacts on the frequency and voltage change due to its droop\ncontrol parameters by a power/reactive power change.\nIn the default settings, plots of the abc signal as well as the dq0 signals of\nthe master and slave are provided.\n\nBy default, the following small network will be simulated:\n\n.. figure:: ../../../pictures/network.png\n\nA short introduction to experimental controller tuning with some hints\ncan be found `here <controller_tuning.html>`__.\n\nIf the controller works fine, a three phase voltage similar to the\nfollowing one should be one of the plots.\n\n.. figure:: ../../../pictures/abc.png\n\nAny other demanded signal which is provided by the FMU or saved during\nthe simulating can be plotted by adding it to\n\n::\n\n    viz_cols=['*.m[dq0]', 'slave.freq', 'lcl1.*'],\n\nin the gym.make() command. Make sure that demanded signal from the fmu\nare listed as a model\\_output:\n\n::\n\n    model_output={\n                       'lc1': [\n                           ['inductor1.i', 'inductor2.i', 'inductor3.i'],\n                           ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']],\n                       'rl1': [f'inductor{i}.i' for i in range(1, 4)],\n                       'lcl1':\n                           [['inductor1.i', 'inductor2.i', 'inductor3.i'],\n                            ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']]},\n                       )\n\nHint: Every possible variable which is provided by the FMU can be seen\nthe easiest in OpenModelica. Run the simulation without input signals,\nso every result for voltages and currents should be 0. On the bottom right side, you can select\neach component of the model in the tree structure. Clicking through the\ncomponents until reaching the variable will show the whole variable name\n(for example lcl2.inductor2.i) on top of the plotting window.\n\nThe parameters of the controller like the control frequency delta\\_t,\nthe voltage, frequency or droop characteristics can be set directly in\nthe main function."
  },
  {
    "path": "docs/parts/user_guide/examples.rst",
    "content": "Examples\n========\n\n.. toctree::\n   :maxdepth: 4\n   :titlesonly:\n   :hidden:\n\n   examples/creating_env\n   examples/basic_agent\n   examples/plotting\n\n   examples/single_inverter_current_control_safe_opt\n   examples/two_inverter_static_droop_control\n\n\n\n\nBasic Examples\n--------------\n\nTo get an idea how the toolbox works and how to set-up an environment, an agent or use plotting, three very basic examples are shown below. They use the network presented above, but only the first inverter will be used.\n\n- `Creating the Environment`_\n- `Writing an simple Agent`_\n- `Modify Plotting`_\n\n.. _`Creating the Environment`: examples/creating_env\n.. _`Writing an simple Agent`: examples/basic_agent\n.. _`Modify Plotting`: examples/plotting\n\n\n\nAdvanced Examples\n-----------------\n\n- `Single Inverter Current Control`_\n- `Two Inverter Static Droop Control`_\n\n.. _`Single Inverter Current Control`: examples/single_inverter_current_control_safe_opt\n.. _`Two Inverter Static Droop Control`: examples/two_inverter_static_droop_control\n"
  },
  {
    "path": "docs/parts/user_guide/fmu.rst",
    "content": "Functional Mock-up Unit (FMU)\n=============================\n\nWhat is FMI and FMU?\n^^^^^^^^^^^^^^^^^^^^\n\nThe Functional Mock-up Interface (`FMI <https://fmi-standard.org/>`__)\nis a free standard that defines a container and an interface to exchange\ndynamic models using a combination of XML files, binaries and C code\nzipped into a single file. The generated files for the exchange are\ncalled Functional Mock-up Units (FMU). The binaries are\nplatform-specific, so a linux user can´t run a FMU which is created on a\nwindows machine.\n\nThere are two versions of the FMI standard. The OMG toolbox uses **FMI\n2.0**. Furthermore, there are two simulation types, co-simulation (CS) and\nmodel exchange (ME). Co-simulation has an included solver in the FMU,\nmodel exchange provides the possibility to use solvers in Python. Due to\na lack of implicit solvers in CS, the OMG toolbox uses **ME**.\n\nCreate FMU´s\n^^^^^^^^^^^^\n\nThe best way to create a FMU for the OMG toolbox is to run the\ncreate\\_fmu.mos file in the folder \"omg_grid\" with the terminal. Make sure that\n`OpenModelica <https://openmodelica.org/download/download-windows>`__ is\ninstalled and your latest version of your OpenModelica package is in\nthe same folder as this script.\n\nBy running the create\\_fmu.mos with the command *omc*, you can create a\nFMU of the model *network* in the *Grids* folder.\n\n::\n\n    path\\omg_grid> omc create_fmu.mos\n\nWindows users might need to open the terminal out of OpenModelica by clicking 'tools' => 'OpenModelica Command Prompt' to make sure that the command *omc* gets recognized.\n\nCreating FMUs of other models can be generated by changing\n*omg_grid.Grids.Network* in the last line of the *create\\_fmu.mos* to\n*omg_grid.Grids.YourNetworksName*.\n\n::\n\n    OpenModelica.Scripting.loadFile(\"package.mo\"); getErrorString();\n    setCommandLineOptions(\"-d=newInst\"); getErrorString();\n    setCommandLineOptions(\"-d=initialization\"); getErrorString();\n    setCommandLineOptions(\"--simCodeTarget=Cpp\"); getErrorString();\n    setCommandLineOptions(\"-d=-disableDirectionalDerivatives\"); getErrorString();\n    OpenModelica.Scripting.translateModelFMU(omg_grid.Grids.Network, version=\"2.0\", fmuType = \"me\"); getErrorString();\n\nThe lines in between are setting flags for the FMU-creation. The\ntarget-language is C++ instead of the default C because of a missing\nimplementation of directional derivatives in C, which are necessary for the solvers in python.\n\nIt is possible to create FMUs directly in OpenModelica (File -> Export ->\nFMU). Settings for this can be done in Tools -> Options -> Simulation\n(Target language and Translation Flags) and Tools -> Options -> FMU for\nthe Version, type, name and path. This way is not recommended because of\nthe possibility to miss flags like the initialization. Furthermore,\nproblems with the providing of directional derivatives occurred during testing.\n\nMerge FMUs\n^^^^^^^^^^^\n\nAs mentioned above, FMUs only work on the operating system they are\ncreated on. Though, there is a possibility to combine previously\ngenerated FMUs to allow a usage on different platforms.\n\nFirst, generate FMUs on different platforms from the **same** model.\nThen run the Python file *merge*\\_fmus.py\\. Select the FMUs which\nshould be combined and choose a target file (does not need to exist\nbefore). The script checks at several points if the FMUs were generated\nfrom the same version of the library (careful: small changes like parameter variation\nmight not be detected) and if all FMUs contain binaries for different\nplatforms.\n\nAnnotation for Windows: Sometimes, the script throws an error because in\nthe temp-folder. The script still works and merges\nthe fmu. The resulting file just needs to be renamed with an \".fmu\" at the end and can\ndirectly be used.\n"
  },
  {
    "path": "docs/parts/user_guide/getting_started.rst",
    "content": "Getting Started\n===============\n\n.. figure:: ../../pictures/omg_flow.png\n   :alt: \n\nThis user guide covers all of OpenModelica Microgrids Gym (OMG) toolbox features by\ntopic area. Each of the following steps is introduced in a more detailed\nway in the different chapters of this users guide.\n\nFirst step is to `create the microgrid <OpenModelica.html>`__, which is\nthe environment for training reinforcement learning agents in power electronic-driven microgrids.\nIn addition, the OMG toolbox can be used for pure simulation and classical control purpose using OpenModelica models with a Python interface.\n\nEach microgrid model is built in the open source software\n`OpenModelica <https://www.openmodelica.org/>`__ and can be easily adapted.\n\n.. figure:: ../../pictures/network1.png\n   :alt: \n\nFor the transfer to Python, it needs to get exported as a `Functional\nMock-up Unit (FMU) <https://fmi-standard.org/>`__.\n\nThe creation process of the FMU is shown `here <fmu.html>`__. It is used to\nbuild a gym environment like in the examples from `OpenAI\nGym <https://gym.openai.com/>`__. In OMG, the gym environment is defined\nfor example in (examples/two_inverter_static_droop_control.py).\n\nAfter creating the environment, the network can be simulated in Python.\nOn the one hand, it is possible to test predefined, static controller designs\nlike described `here <examples.html#two-inverter-static-droop-control-py>`__.\n\n.. figure::  ../../pictures/abc.png\n   :alt: \n\nHowever, the main function of this toolbox is to apply reinforcement learning\napproaches by utilizing the OMG interface for optimal microgrid control as shown in this\n`example <examples.html#single-inverter-current-control-safe-opt-py>`__.\n\n.. figure::  ../../pictures/kp_kp_J.png\n   :alt: \n\nBasic Examples\n~~~~~~~~~~~~~~\n\nTo get an idea how the toolbox works and how to set-up an environment or an agent, two very basic examples are shown below. Both use the network presented above, but only the first inverter will be used.\n\n\nCreating an environment\n-----------------------\n\n\nFollowing is a minimal example how to set-up an run an environment.\nNecessary is the definition of model inputs, in this case the three phases of the first inverter.\nThe model outputs will be shown as simulation results, and the model path is the relative location of the FMU file, which contains the network.\nFor any other simulation parameters, for example the step-size, default values will be used.\n\nFor the initialisation, the environment needs to be reseted, and env.render will define the output plots.\nThe simulation will perform 1000 steps. A different random number will be provided to every of the three previously defined model_inputs.\nAfterwards, the inductor currents of the LC-filter \"lc1\"shown in the figure above will be plotted, which should result in three increasing and due to the random function noisy  lines.\n\n.. literalinclude:: ../../../examples/basic_env.py\n   :linenos:\n\n\n\nCreating an agent and a runner\n------------------------------\n\nAdditionally to the environment, an an agent will be created and a runner will be used. The runner class will take care of initializing and termination\nof agents and environments, as well as the execution of multiple episodes. The class will handle all information\nexchange between agent and environment like presented in the high level code architecture shown below:\n\n.. figure:: ../../pictures/highlevel.png\n   :width: 400\n   :alt:\n\nSince the inputs are used for both the agent and the environment, they are defined in advance. Although the Agent gets information of the environment, in this small example, its action is still a random number.\n\nThe environment is the same as above. Afterwards, the agent and the runner get defined, and the runner runs for one episode.\n\n.. literalinclude:: ../../../examples/simple_agent.py\n   :linenos:\n\n\n"
  },
  {
    "path": "examples/basic_env.py",
    "content": "import gym\n\nif __name__ == '__main__':\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv-v1',\n                   max_episode_steps=None,\n                   net='../net/net.yaml',\n                   model_path='../omg_grid/grid.network.fmu')\n\n    env.reset()\n    for _ in range(1000):\n        env.render()\n        env.step(env.action_space.sample())  # take a random action\n    env.close()\n"
  },
  {
    "path": "examples/basic_env_norm.py",
    "content": "import gym\n\nif __name__ == '__main__':\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   is_normalized=True,\n                   net='../net/net.yaml',\n                   model_path='../omg_grid/grid.network.fmu')\n\n    env.reset()\n    for _ in range(1000):\n        env.render()\n        obs, rew, done, info = env.step(env.action_space.sample())  # take a random action\n        if done:\n            break\n    env.close()\n"
  },
  {
    "path": "examples/network_callbacks.py",
    "content": "#####################################\n# Example using a FMU by OpenModelica as gym environment containing two inverters, each connected via an LC-filter to\n# supply in parallel a RL load.\n# This example uses the available standard controllers as defined in the 'auxiliaries' folder.\n# One inverter is set up as voltage forming inverter with a direct droop controller.\n# The other controller is used as current sourcing inverter with an inverse droop controller which reacts on the\n# frequency and voltage change due to its droop control parameters by a power/reactive power change.\n\nimport logging\nfrom functools import partial\n\nimport gym\nimport numpy as np\nfrom matplotlib import pyplot as plt\n\nfrom openmodelica_microgrid_gym import Runner\nfrom openmodelica_microgrid_gym.agents import StaticControlAgent\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, MultiPhaseDQ0PIPIController, \\\n    MultiPhaseDQCurrentController, InverseDroopParams, PLLParams\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom openmodelica_microgrid_gym.net import Network\n\n# Simulation definitions\nmax_episode_steps = 3000  # number of simulation steps per episode\nnum_episodes = 1  # number of simulation episodes\n# (here, only 1 episode makes sense since simulation conditions don't change in this example)\nDroopGain = 40000.0  # virtual droop gain for active power / W/Hz\nQDroopGain = 1000.0  # virtual droop gain for reactive power / VAR/V\nnet = Network.load('../net/net.yaml')\ndelta_t = net.ts  # simulation time step size / s\nfreq_nom = net.freq_nom  # nominal grid frequency / Hz\nv_nom = net.v_nom  # nominal grid voltage / V\nv_DC = net['inverter1'].v_DC  # DC-link voltage / V; will be set as model parameter in the FMU\ni_lim = net['inverter1'].i_lim  # inverter current limit / A\ni_nom = net['inverter1'].i_nom  # nominal inverter current / A\n\nlogging.basicConfig()\n\n\ndef load_step(t, gain):\n    \"\"\"\n    Doubles the load parameters\n    :param t:\n    :param gain: device parameter\n    :return: Dictionary with load parameters\n    \"\"\"\n    # Defines a load step after 0.2 s\n    return 1 * gain if t < .2 else 2 * gain\n\n\nif __name__ == '__main__':\n    ctrl = []  # Empty dict which shall include all controllers\n\n    #####################################\n    # Define the voltage forming inverter as master\n    # Voltage control PI gain parameters for the voltage sourcing inverter\n    voltage_dqp_iparams = PI_params(kP=0.025, kI=60, limits=(-i_lim, i_lim))\n    # Current control PI gain parameters for the voltage sourcing inverter\n    current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1))\n    # Droop characteristic for the active power Watt/Hz, delta_t\n    droop_param = DroopParams(DroopGain, 0.005, freq_nom)\n    # Droop characteristic for the reactive power VAR/Volt Var.s/Volt\n    qdroop_param = DroopParams(QDroopGain, 0.002, v_nom)\n    # Add to dict\n    ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param,\n                                            qdroop_param, ts_sim=delta_t, name='master'))\n\n    #####################################\n    # Define the current sourcing inverter as slave\n    # Current control PI gain parameters for the current sourcing inverter\n    current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1))\n    # PI gain parameters for the PLL in the current forming inverter\n    pll_params = PLLParams(kP=10, kI=200, limits=None, f_nom=freq_nom)\n    # Droop characteristic for the active power Watts/Hz, W.s/Hz\n    droop_param = InverseDroopParams(DroopGain, delta_t, freq_nom, tau_filt=0.04)\n    # Droop characteristic for the reactive power VAR/Volt Var.s/Volt\n    qdroop_param = InverseDroopParams(50, delta_t, v_nom, tau_filt=0.01)\n    # Add to dict\n    ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, i_lim,\n                                              droop_param, qdroop_param, ts_sim=delta_t, name='slave'))\n\n    # Define the agent as StaticControlAgent which performs the basic controller steps for every environment set\n    agent = StaticControlAgent(ctrl, {'master': [[f'lc1.inductor{k}.i' for k in '123'],\n                                                 [f'lc1.capacitor{k}.v' for k in '123']],\n                                      'slave': [[f'lcl1.inductor{k}.i' for k in '123'],\n                                                [f'lcl1.capacitor{k}.v' for k in '123'],\n                                                [f'inverter2.i_ref.{k}' for k in '012']]})  # np.zeros(3)]})\n\n\n    def update_legend(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$i_{\\mathrm{abc}}\\,/\\,\\mathrm{A}$')\n        ax.grid(which='both')\n        plt.legend(handles=ax.lines[::3], labels=('Measurement abc', 'Setpoint dq0'))\n        fig.show()\n\n\n    # Define the environment\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   viz_mode='episode',\n                   viz_cols=[\n                       PlotTmpl([[f'lcl1.inductor{i}.i' for i in '123'], [f'slave.SPI{i}' for i in 'dq0']],\n                                callback=update_legend,\n                                color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                style=[[None], ['--']],\n                                title='Example of using an timevariant external current reference'\n                                ),\n                   ],\n                   log_level=logging.INFO,\n                   max_episode_steps=max_episode_steps,\n                   model_params={'rl1.resistor1.R': partial(load_step, gain=20),\n                                 'rl1.resistor2.R': partial(load_step, gain=20),\n                                 'rl1.resistor3.R': partial(load_step, gain=20),\n                                 'rl1.inductor1.L': 0.001,\n                                 'rl1.inductor2.L': 0.001,\n                                 'rl1.inductor3.L': 0.001\n                                 },\n                   model_path='../omg_grid/grid.network.fmu',\n                   net=net\n                   )\n\n    # User runner to execute num_episodes-times episodes of the env controlled by the agent\n    runner = Runner(agent, env)\n\n\n    def timeshift(component, t):\n        if t > .1:\n            return dict(i_ref=np.array([30, 0, 0]))\n        return dict(i_ref=np.array([5, 0, 0]))\n\n\n    net['inverter2'].post_calculate_hook = timeshift\n    runner.run(num_episodes, visualise=True)\n"
  },
  {
    "path": "examples/plotting.py",
    "content": "import gym\n\nfrom openmodelica_microgrid_gym.env import PlotTmpl\n\nif __name__ == '__main__':\n    def second_plot(fig):\n        ax = fig.gca()\n        ax.set_ylabel('y label!')\n        ax.set_xlabel('$t\\,/\\,\\mathrm{ms}$')\n        fig.savefig('plot2.pdf')\n\n\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv-v1',\n                   viz_mode='episode',\n                   viz_cols=[\n                       PlotTmpl([f'lc1.inductor{i}.i' for i in '123'],\n                                callback=lambda fig: fig.savefig('plot.pdf'),\n                                linewidth=4,\n                                style=[None, '--', '*'],\n                                linestyle=['None', None, None],\n                                marker=[r'$\\heartsuit$', None, None],\n                                c=['pink', None, None],\n                                title='test'),\n                       PlotTmpl(['lc1.inductor1.i', 'lc1.inductor2.i'], callback=second_plot,\n                                legend=[False, True],\n                                label=[None, 'something'])\n                   ],\n                   max_episode_steps=None,\n                   net='../net/net.yaml',\n                   model_path='../omg_grid/grid.network.fmu')\n\n    env.reset()\n    for _ in range(100):\n        env.render()\n        env.step(env.action_space.sample())  # take a random action\n    env.close()\n"
  },
  {
    "path": "examples/simple_agent.py",
    "content": "import gym\nimport numpy as np\nimport pandas as pd\n\nfrom openmodelica_microgrid_gym import Agent, Runner\n\n\nclass RndAgent(Agent):\n    def act(self, obs: pd.Series) -> np.ndarray:\n        return self.env.action_space.sample()\n\n\nif __name__ == '__main__':\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv-v1',\n                   net='../net/net.yaml',\n                   model_path='../omg_grid/grid.network.fmu')\n\n    agent = RndAgent()\n    runner = Runner(agent, env)\n\n    runner.run(1)\n"
  },
  {
    "path": "examples/single_inverter_current_control_safe_opt.py",
    "content": "#####################################\n# Example using a FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters\n# Simulation setup: Single inverter supplying 15 A d-current to an RL-load via a LC filter\n# Controller: PI current controller gain parameters are optimized by SafeOpt\n\n\nimport logging\nfrom typing import List\n\nimport GPy\nimport gym\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nfrom openmodelica_microgrid_gym import Runner\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, MultiPhaseDQCurrentSourcingController\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom openmodelica_microgrid_gym.net import Network\nfrom openmodelica_microgrid_gym.util import dq0_to_abc, nested_map, FullHistory\n\n# Choose which controller parameters should be adjusted by SafeOpt.\n# - Kp: 1D example: Only the proportional gain Kp of the PI controller is adjusted\n# - Ki: 1D example: Only the integral gain Ki of the PI controller is adjusted\n# - Kpi: 2D example: Kp and Ki are adjusted simultaneously\n\nadjust = 'Kpi'\n\n# Check if really only one simulation scenario was selected\nif adjust not in {'Kp', 'Ki', 'Kpi'}:\n    raise ValueError(\"Please set 'adjust' to one of the following values: 'Kp', 'Ki', 'Kpi'\")\n\n# Simulation definitions\nmax_episode_steps = 600  # number of simulation steps per episode\nnum_episodes = 10  # number of simulation episodes (i.e. SafeOpt iterations)\nmu = 2  # factor for barrier function (see below)\nnet = Network.load('../net/net_single-inv-curr.yaml')\ni_lim = net['inverter1'].i_lim  # inverter current limit / A\ni_nom = net['inverter1'].i_nom  # nominal inverter current / A\ni_ref = np.array(net['inverter1'].i_ref)  # exemplary set point i.e. id = 15, iq = 0, i0 = 0 / A\n\n\nclass Reward:\n    def __init__(self):\n        self._idx = None\n\n    def set_idx(self, obs):\n        if self._idx is None:\n            self._idx = nested_map(\n                lambda n: obs.index(n),\n                [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0']])\n\n    def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and setpoints to evaluate the quality of the\n        used parameters.\n        Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic\n        barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu.\n\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        self.set_idx(cols)\n        idx = self._idx\n\n        Iabc_master = data[idx[0]]  # 3 phase currents at LC inductors\n        phase = data[idx[1]]  # phase from the master controller needed for transformation\n\n        # setpoints\n        ISPdq0_master = data[idx[2]]  # setting dq reference\n        ISPabc_master = dq0_to_abc(ISPdq0_master, phase)  # convert dq set-points into three-phase abc coordinates\n\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        # plus barrier penalty for violating the current constraint\n        error = (np.sum((np.abs((ISPabc_master - Iabc_master)) / i_lim) ** 0.5, axis=0) \\\n                 + -np.sum(mu * np.log(1 - np.maximum(np.abs(Iabc_master) - i_nom, 0) / (i_lim - i_nom)), axis=0)) \\\n                / max_episode_steps\n\n        return -error.squeeze()\n\n\nif __name__ == '__main__':\n    #####################################\n    # Definitions for the GP\n    prior_mean = 0  # mean factor of the GP prior mean which is multiplied with the first performance of the initial set\n    noise_var = 0.001  # measurement noise sigma_omega\n    prior_var = 2  # prior variance of the GP\n\n    bounds = None\n    lengthscale = None\n    if adjust == 'Kp':\n        bounds = [(0.00, 0.03)]  # bounds on the input variable Kp\n        lengthscale = [.01]  # length scale for the parameter variation [Kp] for the GP\n\n    # For 1D example, if Ki should be adjusted\n    if adjust == 'Ki':\n        bounds = [(0, 300)]  # bounds on the input variable Ki\n        lengthscale = [50.]  # length scale for the parameter variation [Ki] for the GP\n\n    # For 2D example, choose Kp and Ki as mutable parameters (below) and define bounds and lengthscale for both of them\n    if adjust == 'Kpi':\n        bounds = [(0.0, 0.07), (0, 300)]\n        lengthscale = [.02, 50.]\n\n    # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times\n    # the initial performance: safe_threshold = 1.2 means. Performance measurement for optimization are seen as\n    # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!)\n    # parameter set\n    safe_threshold = 0\n    # minimal allowed performance (depending on episode return)\n    j_min = -2\n\n    # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop\n    # expanding points eventually.\n    # The following variable is multiplied with the first performance of the initial set by the factor below:\n    explore_threshold = 0\n\n    # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of\n    # limit exceeded\n    abort_reward = 10 * j_min\n\n    # Definition of the kernel\n    kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True)\n\n    #####################################\n    # Definition of the controllers\n    mutable_params = None\n    current_dqp_iparams = None\n    if adjust == 'Kp':\n        # mutable_params = parameter (Kp gain of the current controller of the inverter) to be optimized using\n        # the SafeOpt algorithm\n        mutable_params = dict(currentP=MutableFloat(5e-3))\n\n        # Define the PI parameters for the current controller of the inverter\n        current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=115, limits=(-1, 1))\n\n    # For 1D example, if Ki should be adjusted\n    elif adjust == 'Ki':\n        mutable_params = dict(currentI=MutableFloat(10))\n        current_dqp_iparams = PI_params(kP=10e-3, kI=mutable_params['currentI'], limits=(-1, 1))\n\n    # For 2D example, choose Kp and Ki as mutable parameters\n    elif adjust == 'Kpi':\n        mutable_params = dict(currentP=MutableFloat(40e-3), currentI=MutableFloat(10))\n        current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'], limits=(-1, 1))\n\n    # Define a current sourcing inverter as master inverter using the pi and droop parameters from above\n    ctrl = MultiPhaseDQCurrentSourcingController(current_dqp_iparams, ts_sim=net.ts, f_nom=net.freq_nom,\n                                                 ts_ctrl=1e-4, name='master')\n\n    #####################################\n    # Definition of the optimization agent\n    # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example\n    # Arguments described above\n    # History is used to store results\n    agent = SafeOptAgent(mutable_params, abort_reward,\n                         j_min, kernel,\n                         dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean,\n                              safe_threshold=safe_threshold, explore_threshold=explore_threshold),\n                         [ctrl],\n                         dict(master=[[f'lc1.inductor{k}.i' for k in '123'],\n                                      i_ref]),\n                         history=FullHistory()\n                         )\n\n\n    #####################################\n    # Definition of the environment using a FMU created by OpenModelica\n    # (https://www.openmodelica.org/)\n    # Using an inverter supplying a load\n    # - using the reward function described above as callable in the env\n    # - viz_cols used to choose which measurement values should be displayed (here, only the 3 currents across the\n    #   inductors of the inverters are plotted. Labels and grid is adjusted using the PlotTmpl (For more information,\n    #   see UserGuide)\n    # - inputs to the models are the connection points to the inverters (see user guide for more details)\n    # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors\n\n    def xylables(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$i_{\\mathrm{abc}}\\,/\\,\\mathrm{A}$')\n        ax.grid(which='both')\n        fig.show()\n        # fig.savefig('Inductor_currents.pdf')\n\n\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   reward_fun=Reward().rew_fun,\n                   viz_cols=[\n                       PlotTmpl([f'lc1.inductor{i}.i' for i in '123'],\n                                callback=xylables\n                                )\n                   ],\n                   log_level=logging.INFO,\n                   viz_mode='episode',\n                   max_episode_steps=max_episode_steps,\n                   net=net,\n                   model_path='../omg_grid/grid.network_singleInverter.fmu',\n                   history=FullHistory()\n                   )\n\n    #####################################\n    # Execution of the experiment\n    # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations)\n    runner = Runner(agent, env)\n\n    runner.run(num_episodes, visualise=True)\n\n    print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df[:]))\n\n    print('\\n Experiment finished with best set: \\n')\n    print('\\n  {} = {}'.format(adjust, agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n    print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n    print('\\n\\nBest experiment results are plotted in the following:')\n\n    # Show best episode measurment (current) plot\n    best_env_plt = runner.run_data['best_env_plt']\n    if best_env_plt:\n        ax = best_env_plt[0].axes[0]\n        ax.set_title('Best Episode')\n        best_env_plt[0].show()\n        best_env_plt[0].savefig('best_env_plt.png')\n\n    # Show last performance plot\n    best_agent_plt = runner.run_data['last_agent_plt']\n    if best_agent_plt:\n        ax = best_agent_plt.axes[0]\n        ax.grid(which='both')\n        ax.set_axisbelow(True)\n\n        if adjust == 'Ki':\n            ax.set_xlabel(r'$K_\\mathrm{i}\\,/\\,\\mathrm{(VA^{-1}s^{-1})}$')\n            ax.set_ylabel(r'$J$')\n        elif adjust == 'Kp':\n            ax.set_xlabel(r'$K_\\mathrm{p}\\,/\\,\\mathrm{(VA^{-1})}$')\n            ax.set_ylabel(r'$J$')\n        elif adjust == 'Kpi':\n            agent.params.reset()\n            ax.set_xlabel(r'$K_\\mathrm{i}\\,/\\,\\mathrm{(VA^{-1}s^{-1})}$')\n            ax.set_ylabel(r'$K_\\mathrm{p}\\,/\\,\\mathrm{(VA^{-1})}$')\n            ax.get_figure().axes[1].set_ylabel(r'$J$')\n            plt.plot(bounds[0], [mutable_params['currentP'].val, mutable_params['currentP'].val], 'k-', zorder=1, lw=4,\n                     alpha=.5)\n        best_agent_plt.show()\n        best_agent_plt.savefig('agent_plt.png')\n"
  },
  {
    "path": "examples/single_inverter_voltage_current_control_safe_opt.py",
    "content": "#####################################\n# Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters\n# Simulation setup: Single voltage forming inverter supplying an RL-load via an LC-filter\n# Controller: Cascaded PI-PI voltage and current controller gain parameters are optimized by SafeOpt\n\n\nimport logging\nimport os\nfrom time import strftime, gmtime\nfrom typing import List\n\nimport GPy\nimport gym\nimport numpy as np\n\nfrom openmodelica_microgrid_gym import Runner\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, MultiPhaseDQ0PIPIController\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom openmodelica_microgrid_gym.net import Network\nfrom openmodelica_microgrid_gym.util import dq0_to_abc, nested_map, FullHistory\n\n# Simulation definitions\nmax_episode_steps = 300  # number of simulation steps per episode\nnum_episodes = 2  # number of simulation episodes (i.e. SafeOpt iterations)\nmu = 2  # factor for barrier function (see below)\nDroopGain = 40000.0  # virtual droop gain for active power / W/Hz\nQDroopGain = 1000.0  # virtual droop gain for reactive power / VAR/V\nnet = Network.load('../net/net_single-inv-volt.yaml')\ni_lim = net['inverter1'].i_lim  # inverter current limit / A\ni_nom = net['inverter1'].i_nom  # nominal inverter current / A\n\n# Files saves results and  resulting plots to the folder saves_VI_control_safeopt in the current directory\ncurrent_directory = os.getcwd()\nsave_folder = os.path.join(current_directory, r'saves_VI_control_safeopt')\nos.makedirs(save_folder, exist_ok=True)\n\n\nclass Reward:\n    def __init__(self):\n        self._idx = None\n\n    def set_idx(self, obs):\n        if self._idx is None:\n            self._idx = nested_map(\n                lambda n: obs.index(n),\n                [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'],\n                 [f'lc1.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0']])\n\n    def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of\n        the used parameters.\n        Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a\n        logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using\n        parameter mu.\n\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        self.set_idx(cols)\n        idx = self._idx\n\n        iabc_master = data[idx[0]]  # 3 phase currents at LC inductors\n        phase = data[idx[1]]  # phase from the master controller needed for transformation\n        vabc_master = data[idx[3]]  # 3 phase currents at LC inductors\n\n        # set points (sp)\n        isp_dq0_master = data[idx[2]]  # setting dq current reference\n        isp_abc_master = dq0_to_abc(isp_dq0_master, phase)  # convert dq set-points into three-phase abc coordinates\n        vsp_dq0_master = data[idx[4]]  # setting dq voltage reference\n        vsp_abc_master = dq0_to_abc(vsp_dq0_master, phase)  # convert dq set-points into three-phase abc coordinates\n\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        # plus barrier penalty for violating the current constraint\n        error = np.sum((np.abs((isp_abc_master - iabc_master)) / i_lim) ** 0.5, axis=0) \\\n                + -np.sum(mu * np.log(1 - np.maximum(np.abs(iabc_master) - i_nom, 0) / (i_lim - i_nom)), axis=0) \\\n                + np.sum((np.abs((vsp_abc_master - vabc_master)) / net.v_nom) ** 0.5, axis=0)\n\n        return -error.squeeze()\n\n\nif __name__ == '__main__':\n    #####################################\n    # Definitions for the GP\n    prior_mean = 0  # 2  # mean factor of the GP prior mean which is multiplied with the first performance of the initial set\n    noise_var = 0.001 ** 2  # measurement noise sigma_omega\n    prior_var = 2  # prior variance of the GP\n\n    # Choose Kp and Ki (current and voltage controller) as mutable parameters (below) and define bounds and lengthscale\n    # for both of them\n    bounds = [(0.0, 0.03), (0, 300), (0.0, 0.03),\n              (0, 300)]  # bounds on the input variable current-Ki&Kp and voltage-Ki&Kp\n    lengthscale = [.005, 50., .005,\n                   50.]  # length scale for the parameter variation [current-Ki&Kp and voltage-Ki&Kp] for the GP\n\n    # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times\n    # the initial performance: safe_threshold = 1.2 means: performance measurement for optimization are seen as\n    # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!)\n    # parameter set\n    safe_threshold = 0.5\n    # minimal allowed performance (depending on episode return)\n    j_min = -2\n\n    # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop\n    # expanding points eventually.\n    # The following variable is multiplied with the first performance of the initial set by the factor below:\n    explore_threshold = 0\n\n    # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of\n    # limit exceeded\n    abort_reward = -10 * j_min\n\n    # Definition of the kernel\n    kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True)\n\n    #####################################\n    # Definition of the controllers\n    # Choose Kp and Ki for the current and voltage controller as mutable parameters\n    mutable_params = dict(currentP=MutableFloat(10e-3), currentI=MutableFloat(10), voltageP=MutableFloat(25e-3),\n                          voltageI=MutableFloat(60))\n\n    voltage_dqp_iparams = PI_params(kP=mutable_params['voltageP'], kI=mutable_params['voltageI'],\n                                    limits=(-i_lim, i_lim))\n    current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'], limits=(-1, 1))\n\n    # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for the\n    # filter and the nominal frequency\n    # Droop controller used to calculate the virtual frequency drop due to load changes\n    droop_param = DroopParams(DroopGain, 0.005, net.freq_nom)\n\n    # Define the Q-droop parameters for the inverter of the reactive power VAR/Volt, delta_t (0.002) used for the\n    # filter and the nominal voltage\n    qdroop_param = DroopParams(QDroopGain, 0.002, net.v_nom)\n\n    # Define a voltage forming inverter using the PIPI and droop parameters from above\n    ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param,\n                                       ts_sim=net.ts,\n                                       ts_ctrl=2 * net.ts, name='master')\n\n    #####################################\n    # Definition of the optimization agent\n    # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example\n    # Arguments described above\n    # History is used to store results\n    agent = SafeOptAgent(mutable_params,\n                         abort_reward,\n                         j_min,\n                         kernel,\n                         dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean,\n                              safe_threshold=safe_threshold, explore_threshold=explore_threshold),\n                         ctrls=[ctrl],\n                         obs_template=dict(master=[[f'lc1.inductor{k}.i' for k in '123'],\n                                                   [f'lc1.capacitor{k}.v' for k in '123'],\n                                                   ]),\n                         history=FullHistory()\n                         )\n\n\n    #####################################\n    # Definition of the environment using a FMU created by OpenModelica\n    # (https://www.openmodelica.org/)\n    # Using an inverter supplying a load\n    # - using the reward function described above as callable in the env\n    # - viz_cols used to choose which measurement values should be displayed.\n    #   Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide)\n    #   generated figures are stored to file\n    # - inputs to the models are the connection points to the inverters (see user guide for more details)\n    # - model outputs are the 3 currents through the inductors and the 3 voltages across the capacitors\n\n    def xylables_i(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$i_{\\mathrm{abc}}\\,/\\,\\mathrm{A}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/Inductor_currents' + time + '.pdf')\n\n\n    def xylables_v_abc(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$v_{\\mathrm{abc}}\\,/\\,\\mathrm{V}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/abc_voltage' + time + '.pdf')\n\n\n    def xylables_v_dq0(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$v_{\\mathrm{dq0}}\\,/\\,\\mathrm{V}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/dq0_voltage' + time + '.pdf')\n\n\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   reward_fun=Reward().rew_fun,\n                   viz_cols=[\n                       PlotTmpl([f'lc1.inductor{i}.i' for i in '123'],\n                                callback=xylables_i\n                                ),\n                       PlotTmpl([f'lc1.capacitor{i}.v' for i in '123'],\n                                callback=xylables_v_abc\n                                ),\n                       PlotTmpl([f'master.CVV{i}' for i in 'dq0'],\n                                callback=xylables_v_dq0\n                                )\n                   ],\n                   log_level=logging.INFO,\n                   viz_mode='episode',\n                   max_episode_steps=max_episode_steps,\n                   net=net,\n                   model_path='../omg_grid/grid.network_singleInverter.fmu',\n                   history=FullHistory()\n                   )\n\n    #####################################\n    # Execution of the experiment\n    # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations)\n    runner = Runner(agent, env)\n\n    runner.run(num_episodes, visualise=True)\n\n    #####################################\n    # Performance results and parameters as well as plots are stored in folder pipi_signleInvALT\n    agent.history.df.to_csv(save_folder + '/result.csv')\n\n    print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4})))\n    print('\\n Experiment finished with best set: \\n')\n    print('\\n  Current-Ki&Kp and voltage-Ki&Kp = {}'.format(\n        agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n    print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n    print('\\n\\nBest experiment results are plotted in the following:')\n\n    # Show best episode measurment (current) plot\n    best_env_plt = runner.run_data['best_env_plt']\n    for ii in range(len(best_env_plt)):\n        ax = best_env_plt[ii].axes[0]\n        ax.set_title('Best Episode')\n        best_env_plt[ii].show()\n        # best_env_plt[0].savefig('best_env_plt.png')\n"
  },
  {
    "path": "examples/stocastic_load.py",
    "content": "from functools import partial\n\nimport gym\nimport matplotlib.pyplot as plt\nfrom stochastic.processes import VasicekProcess\n\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom openmodelica_microgrid_gym.net import Network\nfrom openmodelica_microgrid_gym.util import RandProcess\n\nload = 28\nupper_bound_load = 45\nlower_bound_load = 11\nnet = Network.load('../net/net.yaml')\n\n\ndef load_step(t):\n    \"\"\"\n    Doubles the load parameters\n    :param t:\n    :param gain: device parameter\n    :return: Dictionary with load parameters\n    \"\"\"\n    # Defines a load step after 0.01 s\n    if .01 < t <= .01 + net.ts:\n        gen.proc.mean = load * 0.55\n        gen.reserve = load * 0.55\n\n    return gen.sample(t)\n\n\nif __name__ == '__main__':\n    gen = RandProcess(VasicekProcess, proc_kwargs=dict(speed=100, vol=70, mean=load), initial=load,\n                      bounds=(lower_bound_load, upper_bound_load))\n\n\n    def xylables(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$R_{\\mathrm{load}}\\,/\\,\\mathrm{\\Omega}$')\n        ax.grid(which='both')\n        ax.set_ylim([lower_bound_load - 2, upper_bound_load + 2])\n        plt.title('Load example drawn from Ornstein-Uhlenbeck process \\n- Clipping outside the shown y-range')\n        plt.legend()\n        fig.show()\n\n\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   net=net,\n                   model_params={'rl1.resistor1.R': load_step,\n                                 'rl1.resistor2.R': load_step,\n                                 'rl1.resistor3.R': load_step},\n                   viz_cols=[\n                       PlotTmpl([f'rl1.resistor{i}.R' for i in '123'],\n                                callback=xylables\n                                )],\n                   model_path='../omg_grid/grid.network.fmu')\n\n    env.reset()\n    for _ in range(1000):\n        env.render()\n        obs, rew, done, info = env.step(env.action_space.sample())  # take a random action\n        if done:\n            break\n    env.close()\n"
  },
  {
    "path": "examples/two_inverter_droop_safe_opt.py",
    "content": "#####################################\n# Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters\n# Simulation setup: Two inverters (one voltage forming, one current sourcing) supplying an RL-load via an LC-filter\n# Controller: droop controller gains are optimized by SafeOpt\n\n\nimport logging\nimport os\nfrom functools import partial\nfrom time import strftime, gmtime\nfrom typing import List\n\nimport GPy\nimport gym\nimport numpy as np\n\nfrom openmodelica_microgrid_gym import Runner\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, \\\n    MultiPhaseDQ0PIPIController, PLLParams, InverseDroopParams, MultiPhaseDQCurrentController\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom openmodelica_microgrid_gym.net import Network\nfrom openmodelica_microgrid_gym.util import nested_map, FullHistory\n\n# Simulation definitions\nmax_episode_steps = 6000  # number of simulation steps per episode\nnum_episodes = 1  # number of simulation episodes (i.e. SafeOpt iterations)\nmu = 2  # factor for barrier function (see below)\nDroopGain = 40000.0  # virtual droop gain for active power / W/Hz\nQDroopGain = 1000.0  # virtual droop gain for reactive power / VA/V\nnet = Network.load('../net/net.yaml')\ndelta_t = net.ts  # simulation time step size / s\nfreq_nom = net.freq_nom  # nominal grid frequency / Hz\nv_nom = net.v_nom  # nominal grid voltage / V\nv_DC = net['inverter1'].v_DC  # DC-link voltage / V; will be set as model parameter in the FMU\ni_lim = net['inverter1'].i_lim  # inverter current limit / A\ni_nom = net['inverter1'].i_nom  # nominal inverter current / A\n\n# Files saves results and  resulting plots to the folder saves_VI_control_safeopt in the current directory\ncurrent_directory = os.getcwd()\nsave_folder = os.path.join(current_directory, r'saves_droop_control_safeopt')\nos.makedirs(save_folder, exist_ok=True)\n\n\ndef load_step(t, gain):\n    \"\"\"\n    Increases the load parameters by a factor of 5\n    :param t: time\n    :param gain: device parameter\n    :return: Dictionary with load parameters\n    \"\"\"\n    # Defines a load step after 0.15 s\n    return 1 * gain if t < .15 else 5 * gain\n\n\nclass Reward:\n    def __init__(self):\n        self._idx = None\n\n    def set_idx(self, obs):\n        if self._idx is None:\n            self._idx = nested_map(\n                lambda n: obs.index(n),\n                [[f'slave.freq'],\n                 [f'master.CVV{s}' for s in 'dq0']])\n\n    def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of\n        the used parameters.\n        Takes current measurement and set-points so calculate the mean-root control error\n\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        self.set_idx(cols)\n        idx = self._idx\n        freq = data[idx[0]]\n\n        vdq0_master = data[idx[1]]  # 3 phase voltages at LC capacitor\n\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        error = np.sum((np.abs((freq_nom - freq)) / freq_nom) ** 0.5, axis=0) + \\\n                np.sum((np.abs(([v_nom, 0, 0] - vdq0_master)) / v_nom) ** 0.5, axis=0)\n\n        return -error.squeeze()\n\n\nif __name__ == '__main__':\n    ctrl = []  # Empty dict which shall include all controllers\n\n    #####################################\n    # Definitions for the GP\n    prior_mean = 2  # mean factor of the GP prior mean which is multiplied with the first performance of the initial set\n    noise_var = 0.001 ** 2  # measurement noise sigma_omega\n    prior_var = 0.2  # prior variance of the GP\n\n    # Choose all droop params as mutable parameters (below) and define bounds and lengthscale for both of them\n    bounds = [(0, 100000), (0, 100000), (0, 3000), (0, 100)]  # bounds on the input variable\n    lengthscale = [10000, 10000, 300., 10.]  # length scale for the parameter variation for the GP\n\n    # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times\n    # the initial performance: safe_threshold = 1.2 means. Performance measurement for optimization are seen as\n    # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!)\n    # parameter set\n    safe_threshold = 0.5\n    # minimal allowed performance (depending on episode return)\n    j_min = -2\n\n    # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop\n    # expanding points eventually.\n    # The following variable is multiplied with the first performance of the initial set by the factor below:\n    explore_threshold = 0\n\n    # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of\n    # limit exceeded\n    abort_reward = -10 * j_min\n\n    # Definition of the kernel\n    kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True)\n\n    #####################################\n    # Definition of the controllers\n\n    # Choose all droop parameter as mutable parameters\n    mutable_params = dict(pDroop_master=MutableFloat(30000.0), pDroop_slave=MutableFloat(30000.0),\n                          qDroop_master=MutableFloat(QDroopGain), qDroop_slave=MutableFloat(10))\n\n    # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for\n    # the filter and the nominal frequency\n    # Droop controller used to calculate the virtual frequency drop due to load changes\n    droop_param_master = DroopParams(mutable_params['pDroop_master'], 0.005, freq_nom)\n    # droop parameter used to react on frequency drop\n    droop_param_slave = InverseDroopParams(mutable_params['pDroop_slave'], delta_t, freq_nom, tau_filt=0.04)\n    # Droop characteristic for the reactive power VAR/Volt Var.s/Volt\n    # qDroop parameter used for virtual voltage drop\n    qdroop_param_master = DroopParams(mutable_params['qDroop_master'], 0.002, v_nom)\n    # Droop characteristic for the reactive power VAR/Volt Var.s/Volt\n    qdroop_param_slave = InverseDroopParams(mutable_params['qDroop_slave'], delta_t, v_nom, tau_filt=0.01)\n\n    ###############\n    # define Master\n    voltage_dqp_iparams = PI_params(kP=0.025, kI=60, limits=(-i_lim, i_lim))\n    # Current control PI gain parameters for the voltage sourcing inverter\n    current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1))\n\n    # Define a current sourcing inverter as master inverter using the pi and droop parameters from above\n    ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param_master,\n                                            qdroop_param_master, ts_sim=delta_t, ts_ctrl=2 * delta_t, name='master'))\n\n    ###############\n    # define slave\n    current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1))\n    # PI gain parameters for the PLL in the current forming inverter\n    pll_params = PLLParams(kP=10, kI=200, limits=(-10000, 10000), f_nom=freq_nom)\n    # Droop characteristic for the active power Watts/Hz, W.s/Hz\n\n    # Add to dict\n    ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, i_lim, droop_param_slave,\n                                              qdroop_param_slave, ts_sim=delta_t, name='slave'))\n\n    #####################################\n    # Definition of the optimization agent\n    # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example\n    # Arguments described above\n    # History is used to store results\n    agent = SafeOptAgent(mutable_params,\n                         abort_reward,\n                         j_min,\n                         kernel,\n                         dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean,\n                              safe_threshold=safe_threshold, explore_threshold=explore_threshold),\n                         ctrl,\n                         {'master': [[f'lc1.inductor{k}.i' for k in '123'],\n                                     [f'lc1.capacitor{k}.v' for k in '123'],\n                                     ],\n                          'slave': [[f'lcl1.inductor{k}.i' for k in '123'],\n                                    [f'lcl1.capacitor{k}.v' for k in '123'],\n                                    np.zeros(3)]},\n                         history=FullHistory()\n                         )\n\n\n    #####################################\n    # Definition of the environment using a FMU created by OpenModelica\n    # (https://www.openmodelica.org/)\n    # Using two inverters supplying a load\n    # - using the reward function described above as callable in the env\n    # - viz_cols used to choose which measurement values should be displayed\n    #   Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide)\n    #   figures are stored to folder\n    # - inputs to the models are the connection points to the inverters (see user guide for more details)\n    # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors for each\n    #   inverter and the 3 currents through the load\n\n    def xylables_i(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$i_{\\mathrm{abc}}\\,/\\,\\mathrm{A}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/Inductor_currents' + time + '.pdf')\n\n\n    def xylables_v_abc(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$v_{\\mathrm{abc}}\\,/\\,\\mathrm{V}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/abc_voltage' + time + '.pdf')\n\n\n    def xylables_v_dq0(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$v_{\\mathrm{dq0}}\\,/\\,\\mathrm{V}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/dq0_voltage' + time + '.pdf')\n\n\n    def xylables_P_master(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$P_{\\mathrm{master}}\\,/\\,\\mathrm{W}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/P_master' + time + '.pdf')\n\n\n    def xylables_P_slave(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$P_{\\mathrm{slave}}\\,/\\,\\mathrm{W}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/P_slave' + time + '.pdf')\n\n\n    def xylables_freq(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$f_{\\mathrm{slave}}\\,/\\,\\mathrm{Hz}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/f_slave' + time + '.pdf')\n\n\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   reward_fun=Reward().rew_fun,\n                   viz_cols=[\n                       PlotTmpl([f'lc1.inductor{i}.i' for i in '123'],\n                                callback=xylables_i\n                                ),\n                       PlotTmpl([f'lc1.capacitor{i}.v' for i in '123'],\n                                callback=xylables_v_abc\n                                ),\n                       PlotTmpl([f'master.instPow'],\n                                callback=xylables_P_master\n                                ),\n                       PlotTmpl([f'slave.instPow'],\n                                callback=xylables_P_slave\n                                ),\n                       PlotTmpl([f'slave.freq'],\n                                callback=xylables_freq\n                                ),\n                       PlotTmpl([f'master.CVV{i}' for i in 'dq0'],\n                                callback=xylables_v_dq0\n                                )\n                   ],\n                   log_level=logging.INFO,\n                   viz_mode='episode',\n                   max_episode_steps=max_episode_steps,\n                   model_params={'rl1.resistor1.R': partial(load_step, gain=20),\n                                 'rl1.resistor2.R': partial(load_step, gain=20),\n                                 'rl1.resistor3.R': partial(load_step, gain=20),\n                                 'rl1.inductor1.L': partial(load_step, gain=0.001),  # 0.001,\n                                 'rl1.inductor2.L': partial(load_step, gain=0.001),  # 0.001,\n                                 'rl1.inductor3.L': partial(load_step, gain=0.001)  # 0.001\n                                 },\n                   model_path='../omg_grid/grid.network.fmu',\n                   net=net,\n                   )\n\n    #####################################\n    # Execution of the experiment\n    # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations)\n    runner = Runner(agent, env)\n\n    runner.run(num_episodes, visualise=True)\n\n    #####################################\n    # Performance results and Parameters as well as plots are stored in folder saves_droop\n    agent.history.df.to_csv(save_folder + '/result.csv')\n\n    print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4})))\n    print('\\n Experiment finished with best set: \\n')\n    print('\\n  [pDroop_master, pDroop_slave, qDroop_master, qDroop_slave]= {}'.format(\n        agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n    print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n    print('\\n\\nBest experiment results are plotted in the following:')\n"
  },
  {
    "path": "examples/two_inverter_static_droop_control.py",
    "content": "#####################################\n# Example using a FMU by OpenModelica as gym environment containing two inverters, each connected via an LC-filter to\n# supply in parallel a RL load.\n# This example uses the available standard controllers as defined in the 'auxiliaries' folder.\n# One inverter is set up as voltage forming inverter with a direct droop controller.\n# The other controller is used as current sourcing inverter with an inverse droop controller which reacts on the\n# frequency and voltage change due to its droop control parameters by a power/reactive power change.\n\nimport logging\nfrom functools import partial\n\nimport gym\nimport numpy as np\n\nfrom openmodelica_microgrid_gym import Runner\nfrom openmodelica_microgrid_gym.agents import StaticControlAgent\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, MultiPhaseDQ0PIPIController, \\\n    MultiPhaseDQCurrentController, InverseDroopParams, PLLParams\nfrom openmodelica_microgrid_gym.net import Network\n\n# Simulation definitions\nmax_episode_steps = 6000  # number of simulation steps per episode\nnum_episodes = 1  # number of simulation episodes\n# (here, only 1 episode makes sense since simulation conditions don't change in this example)\nDroopGain = 40000.0  # virtual droop gain for active power / W/Hz\nQDroopGain = 1000.0  # virtual droop gain for reactive power / VAR/V\nnet = Network.load('../net/net.yaml')\ndelta_t = net.ts  # simulation time step size / s\nfreq_nom = net.freq_nom  # nominal grid frequency / Hz\nv_nom = net.v_nom  # nominal grid voltage / V\nv_DC = net['inverter1'].v_DC  # DC-link voltage / V; will be set as model parameter in the FMU\ni_lim = net['inverter1'].i_lim  # inverter current limit / A\ni_nom = net['inverter1'].i_nom  # nominal inverter current / A\n\nlogging.basicConfig()\n\n\ndef load_step(t, gain):\n    \"\"\"\n    Doubles the load parameters\n    :param t:\n    :param gain: device parameter\n    :return: Dictionary with load parameters\n    \"\"\"\n    # Defines a load step after 0.2 s\n    return 1 * gain if t < .2 else 2 * gain\n\n\nif __name__ == '__main__':\n    ctrl = []  # Empty dict which shall include all controllers\n\n    #####################################\n    # Define the voltage forming inverter as master\n    # Voltage control PI gain parameters for the voltage sourcing inverter\n    voltage_dqp_iparams = PI_params(kP=0.025, kI=60, limits=(-i_lim, i_lim))\n    # Current control PI gain parameters for the voltage sourcing inverter\n    current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1))\n    # Droop characteristic for the active power Watt/Hz, delta_t\n    droop_param = DroopParams(DroopGain, 0.005, freq_nom)\n    # Droop characteristic for the reactive power VAR/Volt Var.s/Volt\n    qdroop_param = DroopParams(QDroopGain, 0.002, v_nom)\n    # Add to dict\n    ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param,\n                                            qdroop_param, ts_sim=delta_t, name='master'))\n\n    #####################################\n    # Define the current sourcing inverter as slave\n    # Current control PI gain parameters for the current sourcing inverter\n    current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1))\n    # PI gain parameters for the PLL in the current forming inverter\n    pll_params = PLLParams(kP=10, kI=200, limits=None, f_nom=freq_nom)\n    # Droop characteristic for the active power Watts/Hz, W.s/Hz\n    droop_param = InverseDroopParams(DroopGain, delta_t, freq_nom, tau_filt=0.04)\n    # Droop characteristic for the reactive power VAR/Volt Var.s/Volt\n    qdroop_param = InverseDroopParams(50, delta_t, v_nom, tau_filt=0.01)\n    # Add to dict\n    ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, i_lim,\n                                              droop_param, qdroop_param, ts_sim=delta_t, name='slave'))\n\n    # Define the agent as StaticControlAgent which performs the basic controller steps for every environment set\n    agent = StaticControlAgent(ctrl, {'master': [[f'lc1.inductor{k}.i' for k in '123'],\n                                                 [f'lc1.capacitor{k}.v' for k in '123']],\n                                      'slave': [[f'lcl1.inductor{k}.i' for k in '123'],\n                                                [f'lcl1.capacitor{k}.v' for k in '123'],\n                                                np.zeros(3)]})\n\n    # Define the environment\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   viz_mode='episode',\n                   # viz_cols=['*.m[dq0]', 'slave.freq', 'lcl1.*'],\n                   viz_cols=['master.inst*', 'slave.inst*', 'lcl1.*', 'lc1.*', 'slave.freq'],\n                   log_level=logging.INFO,\n                   max_episode_steps=max_episode_steps,\n                   model_params={'rl1.resistor1.R': partial(load_step, gain=20),\n                                 'rl1.resistor2.R': partial(load_step, gain=20),\n                                 'rl1.resistor3.R': partial(load_step, gain=20),\n                                 'rl1.inductor1.L': 0.001,\n                                 'rl1.inductor2.L': 0.001,\n                                 'rl1.inductor3.L': 0.001\n                                 },\n                   model_path='../omg_grid/grid.network.fmu',\n                   net=net\n                   )\n\n    # User runner to execute num_episodes-times episodes of the env controlled by the agent\n    runner = Runner(agent, env)\n    runner.run(num_episodes, visualise=True)\n"
  },
  {
    "path": "experiments/model_validation/env/physical_testbench.py",
    "content": "import gym\nimport paramiko\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom time import strftime, gmtime, sleep\n\n\nclass TestbenchEnv(gym.Env):\n    viz_modes = {'episode', 'step', None}\n    \"\"\"Set of all valid visualisation modes\"\"\"\n\n    def __init__(self, host: str = '131.234.172.139', username: str = 'root', password: str = 'omg',\n                 DT: float = 1 / 20000, executable_script_name: str = 'my_first_hps', num_steps: int = 1000,\n                 kP: float = 0.01, kI: float = 5.0, kPV: float = 0.01, kIV: float = 5.0, ref: float = 10.0,\n                 ref2: float = 12, f_nom: float = 50.0, i_limit: float = 25,\n                 i_nominal: float = 15, v_nominal: float = 20, mu=2):\n\n        \"\"\"\n        Environment to connect the code to an FPGA-Controlled test-bench via SSH.\n        Just for demonstration purpose\n        \"\"\"\n\n        self.ssh = paramiko.SSHClient()\n        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())\n        self.host = host\n        self.username = username\n        self.password = password\n        self.DT = DT\n        self.executable_script_name = executable_script_name\n        self.max_episode_steps = num_steps\n        self.kP = kP\n        self.kI = kI\n        self.kPV = kPV\n        self.kIV = kIV\n        self.ref = ref\n        self.ref2 = ref2\n        self.f_nom = f_nom\n        self.data = np.array(list())\n        self.current_step = 0\n        self.done = False\n        self.i_limit = i_limit\n        self.i_nominal = i_nominal\n        self.v_nominal = v_nominal\n        self.mu = mu\n\n    @staticmethod\n    def __decode_result(ssh_result):\n        \"\"\"\n        Methode to decode the data sent from FPGA\n        \"\"\"\n\n        result = list()\n        for line in ssh_result.read().splitlines():\n\n            temp = line.decode(\"utf-8\").split(\",\")\n\n            if len(temp) == 31:\n                # temp.pop(-1)  # Drop the last item\n\n                floats = [float(i) for i in temp]\n                # print(floats)\n                result.append(floats)\n            elif len(temp) != 1:\n                print(temp)\n\n        decoded_result = np.array(result)\n\n        return decoded_result\n\n    def rew_fun(self, Iabc_meas, Iabc_SP) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and setpoints to evaluate the quality of the\n        used parameters.\n        Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic\n        barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu.\n\n        :param Iabc_meas:\n        :param Iabc_SP:\n        :return: Error as negative reward\n        \"\"\"\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        # plus barrier penalty for violating the current constraint\n        error = (np.sum((np.abs((Iabc_SP - Iabc_meas)) / self.i_limit) ** 0.5, axis=0)\n                 + -np.sum(self.mu * np.log(1 - np.maximum(np.abs(Iabc_meas) - self.i_nominal, 0) /\n                                            (self.i_limit - self.i_nominal)), axis=0)\n                 ) / self.max_episode_steps\n\n        return -error.squeeze()\n\n    def reset(self, kP, kI):\n        # toDo: ssh connection not open every episode!\n        self.kP = kP\n        self.kI = kI\n\n        count_retries = 0\n        connected = False\n\n        # for x in range(10):\n        while not connected and count_retries < 10:\n            count_retries += 1\n            try:\n                print('I Try!')\n                self.ssh.connect(self.host, username=self.username, password=self.password)\n                connected = True\n                # return True\n            except:  # (BadHostKeyException, AuthenticationException,\n                # SSHException, socket.gaierror) as e:\n                # print(e)\n                print('Argh')\n                sleep(1)\n\n        if count_retries == 10:\n            print('SSH connection not possible!')\n\n        str_command = './{} -u 100 -n {} -i {} -f {} -1 {} -2 {} -E'.format(self.executable_script_name,\n                                                                            self.max_episode_steps, self.ref,\n                                                                            self.f_nom, self.kP,\n                                                                            self.kI\n                                                                            )\n\n        ssh_stdin, ssh_stdout, ssh_stderr = self.ssh.exec_command(str_command)\n\n        self.data = self.__decode_result(ssh_stdout)\n\n        self.current_step = 0\n        self.done = False\n\n        self.ssh.close()\n\n    def step(self):\n        \"\"\"\n        Takes measured data and returns stepwise\n        Measured data recorded in reset -> controller part of env\n        \"\"\"\n        temp_data = self.data[self.current_step]\n        self.current_step += 1\n\n        i_abc_meas = temp_data[[3, 4, 5]]\n        i_abc_sp = temp_data[[9, 10, 11]]\n\n        reward = self.rew_fun(i_abc_meas, i_abc_sp)\n\n        if self.current_step == self.max_episode_steps:\n            self.done = True\n\n        info = []\n\n        return temp_data, reward, self.done, info\n\n    def render(self, J, save_folder):\n\n        N = (len(self.data))\n        t = np.linspace(0, N * self.DT, N)\n\n        V_A = self.data[:, 0]\n        V_B = self.data[:, 1]\n        V_C = self.data[:, 2]\n        I_A = self.data[:, 3]\n        I_B = self.data[:, 4]\n        I_C = self.data[:, 5]\n        I_D = self.data[:, 6]\n        I_Q = self.data[:, 7]\n        I_0 = self.data[:, 8]\n        SP_A = self.data[:, 9]\n        SP_B = self.data[:, 10]\n        SP_C = self.data[:, 11]\n        m_A = self.data[:, 12]\n        m_B = self.data[:, 13]\n        m_C = self.data[:, 14]\n        m_D = self.data[:, 15]\n        m_Q = self.data[:, 16]\n        m_0 = self.data[:, 17]\n        ICont_Out_D = self.data[:, 18]\n        ICont_Out_Q = self.data[:, 19]\n        ICont_Out_0 = self.data[:, 20]\n        UCont_Out_D = self.data[:, 21]\n        UCont_Out_Q = self.data[:, 22]\n        UCont_Out_0 = self.data[:, 23]\n        ISP_A = self.data[:, 24]\n        ISP_B = self.data[:, 25]\n        ISP_C = self.data[:, 26]\n        V_D = self.data[:, 27]\n        V_Q = self.data[:, 28]\n        V_0 = self.data[:, 29]\n        Ph = self.data[:, 30]\n\n        # store measurement to dataframe\n        df = pd.DataFrame({\n            'V_A': self.data[:, 0],\n            'V_B': self.data[:, 1],\n            'V_C': self.data[:, 2],\n            'I_A': self.data[:, 3],\n            'I_B': self.data[:, 4],\n            'I_C': self.data[:, 5],\n            'I_D': self.data[:, 6],\n            'I_Q': self.data[:, 7],\n            'I_0': self.data[:, 8],\n            'Ph': self.data[:, 30]\n        })\n\n\n        # df.to_pickle('Measurement')\n        # df.to_pickle('Noise_measurement')\n\n        \"\"\"\n        fig = plt.figure()\n        plt.plot(t, V_A, 'b', label=r'$v_{\\mathrm{a}}$')\n        plt.plot(t, V_B, 'g')\n        plt.plot(t, V_C, 'r')\n        plt.plot(t, SP_A, 'b--', label=r'$v_{\\mathrm{SP,a}}$')\n        plt.plot(t, SP_B, 'g--')\n        plt.plot(t, SP_C, 'r--')\n        plt.xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        plt.ylabel('$v_{\\mathrm{abc}}\\,/\\,\\mathrm{V}$')\n        plt.title('{}'.format(J))\n        plt.grid()\n        plt.legend()\n        plt.show()\n        time = strftime(\"%Y-%m-%d %H:%M:%S\", gmtime())\n        # fig.savefig('Paper_CC_meas3/{}_abcInductor_currents' + time + '.pdf'.format(J))\n        fig.savefig('Paper_CC_meas3/J_{}_abcvoltage.pdf'.format(J))\n        \"\"\"\n\n        fig = plt.figure()\n        plt.plot(t, I_A, 'b', label=r'Measurement')\n        plt.plot(t, I_B, 'g')\n        plt.plot(t, I_C, 'r')\n        plt.plot(t, SP_A, 'b--', label=r'Setpoint')\n        plt.plot(t, SP_B, 'g--')\n        plt.plot(t, SP_C, 'r--')\n        plt.xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        plt.ylabel('$i_{\\mathrm{abc}}\\,/\\,\\mathrm{A}$')\n        # plt.title('{}'.format(J))\n        plt.grid()\n        plt.legend()\n        plt.show()\n        fig.savefig(save_folder + '/J_{}_abcInductor_currents.pdf'.format(J))\n        fig.savefig(save_folder + '/J_{}_abcInductor_currents.pgf'.format(J))\n\n        fig = plt.figure()\n        plt.plot(t, I_D, t, I_Q, t, I_0)\n        plt.xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        plt.ylabel('$i_{\\mathrm{dq0}}\\,/\\,\\mathrm{A}$')\n        # plt.title('{}'.format(J))\n        # plt.ylim([-3, 16])\n        plt.grid()\n        plt.show()\n        fig.savefig(save_folder + '/J_{}_dq0Inductor_currents.pdf'.format(J))\n        fig.savefig(save_folder + '/J_{}_dq0Inductor_currents.pgf'.format(J))\n\n        fig = plt.figure()\n        plt.plot(t, m_D, t, m_Q, t, m_0)\n        plt.xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        plt.ylabel('$i_{\\mathrm{dq0}}\\,/\\,\\mathrm{A}$')\n        # plt.title('{}'.format(J))\n        # plt.ylim([-3, 16])\n        plt.grid()\n        plt.show()\n        time = strftime(\"%Y-%m-%d %H:%M:%S\", gmtime())\n        # fig.savefig('hardwareTest_plt/dq0Inductor_currents' + time + '.pdf')\n        fig.savefig(save_folder + '/J_{}_mdq0.pdf'.format(J))\n        fig.savefig(save_folder + '/J_{}_mdq0.pgf'.format(J))\n"
  },
  {
    "path": "experiments/model_validation/env/rewards.py",
    "content": "from typing import List\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.util import nested_map, dq0_to_abc\n\n\nclass Reward:\n\n    def __init__(self, i_limit: float = 15, i_nominal: float = 10, v_limit: float = 500,\n                 v_nominal: float = 230 * np.sqrt(2), mu_c: float = 1, mu_v: float = 1, max_episode_steps: float = 1000,\n                 obs_dict: List = None, funnel: np.ndarray = None):\n        \"\"\"\n        Reward class including different reward functions useable for current and voltage controller evaluation\n        :param i_limit: Current limit of inverter\n        :param i_nominal: Nominal current of inverter\n        :param v_limit: Voltage limit of inverter\n        :param v_nominal: Nominal voltage of inverter\n        :param mu_c: Weighting factor for barrier to punish over-current\n        :param mu_v: Weighting factor for barrier to punish over-voltage\n        :param max_episode_steps: number of episode steps\n        :param funnel: Defines area around setpoint in which a different reward function is valid to punish leaving\n                       funnel different\n        \"\"\"\n\n        self._idx = None\n        self.i_limit = i_limit\n        self.i_nominal = i_nominal\n        self.v_limit = v_limit\n        self.v_nominal = v_nominal\n        self.mu_c = mu_c\n        self.mu_v = mu_v\n        self.max_episode_steps = max_episode_steps\n        self.funnel = funnel\n        self.obs_dict = obs_dict\n\n    def set_idx(self, obs):\n        if self._idx is None:\n            self._idx = nested_map(\n                lambda n: obs.index(n), self.obs_dict)\n            # [[f'lc.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'],\n            # [f'lc.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0'],\n            # [f'master.CVV{k}' for k in 'dq0']])\n\n    def rew_fun_c(self, cols: List[str], data: np.ndarray, risk) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and setpoints to evaluate the quality of the\n        used parameters.\n        Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic\n        barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu.\n\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        self.set_idx(cols)\n        idx = self._idx\n\n        i_abc_master = data[idx[0]]  # 3 phase currents at LC inductors\n        phase = data[idx[1]]  # phase from the master controller needed for transformation\n\n        # setpoints\n        is_pdq0_master = data[idx[2]]  # setting dq reference\n        i_sp_abc_master = dq0_to_abc(is_pdq0_master,\n                                     phase)  # +0.417e-4*50)  # convert dq set-points into three-phase abc coordinates\n\n        # Idq0_master = data[idx[0]]\n        # ISPdq0_master = data[idx[1]]  # convert dq set-points into three-phase abc coordinates\n\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        # plus barrier penalty for violating the current constraint\n        error = (np.sum((np.abs((i_sp_abc_master - i_abc_master)) / self.i_limit) ** 0.5, axis=0)\n                 + -np.sum(self.mu_c * np.log(1 - np.maximum(np.abs(i_abc_master) - self.i_nominal, 0) /\n                                              (self.i_limit - self.i_nominal)), axis=0)) / self.max_episode_steps\n\n        return -error.squeeze()\n\n    def rew_fun_v(self, cols: List[str], data: np.ndarray, risk) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of\n        the used parameters.\n        Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a\n        logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using\n        parameter mu.\n\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        self.set_idx(cols)\n        idx = self._idx\n\n        phase = data[idx[1]]  # phase from the master controller needed for transformation\n        v_abc_master = data[idx[3]]  # 3 phase currents at LC inductors\n\n        # set points (sp)\n        vsp_dq0_master = data[idx[4]]  # setting dq voltage reference\n        vsp_abc_master = dq0_to_abc(vsp_dq0_master, phase)  # convert dq set-points into three-phase abc coordinates\n\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        # plus barrier penalty for violating the current constraint\n        error = (np.sum((np.abs((vsp_abc_master - v_abc_master)) / self.v_limit) ** 0.5, axis=0) + -np.sum(\n            self.mu_v * np.log(\n                1 - np.maximum(np.abs(v_abc_master) - self.v_nominal, 0) / (self.v_limit - self.v_nominal)),\n            axis=0)) \\\n                / self.max_episode_steps\n\n        return -error.squeeze()\n\n    def rew_fun_vc(self, cols: List[str], data: np.ndarray, risk) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of\n        the used parameters.\n        Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a\n        logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using\n        parameter mu.\n\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        self.set_idx(cols)\n        idx = self._idx\n\n        iabc_master = data[idx[0]]  # 3 phase currents at LC inductors\n        phase = data[idx[1]]  # phase from the master controller needed for transformation\n        vabc_master = data[idx[3]]  # 3 phase currents at LC inductors\n\n        # set points (sp)\n        isp_dq0_master = data[idx[2]]  # setting dq current reference\n        isp_abc_master = dq0_to_abc(isp_dq0_master, phase)  # convert dq set-points into three-phase abc coordinates\n        vsp_dq0_master = data[idx[4]]  # setting dq voltage reference\n        vsp_abc_master = dq0_to_abc(vsp_dq0_master, phase)  # convert dq set-points into three-phase abc coordinates\n\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        # plus barrier penalty for violating the current constraint\n\n        error = (np.sum((np.abs((isp_abc_master - iabc_master)) / self.i_limit) ** 0.5, axis=0)\n                 + -np.sum(self.mu_c * np.log(1 - np.maximum(np.abs(iabc_master) - self.i_nominal, 0) /\n                                              (self.i_limit - self.i_nominal)), axis=0) +\n                 np.sum((np.abs((vsp_abc_master - vabc_master)) / self.v_limit) ** 0.5, axis=0) \\\n                 + -np.sum(self.mu_v * np.log(1 - np.maximum(np.abs(vabc_master) - self.v_nominal, 0) /\n                                              (self.v_limit - self.v_nominal)), axis=0)) \\\n                / self.max_episode_steps\n\n        return -error.squeeze()\n\n    def rew_fun_funnel(self, cols: List[str], data: np.ndarray, risk) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of\n        the used parameters.\n        Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a\n        logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using\n        parameter mu.\n\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        self.set_idx(cols)\n        idx = self._idx\n\n        phase = data[idx[1]]  # phase from the master controller needed for transformation\n        vabc_master = data[idx[3]]  # 3 phase currents at LC inductors\n        vdq0_master = data[idx[5]]\n\n        # set points (sp)\n        vsp_dq0_master = data[idx[4]]  # setting dq voltage reference\n        vsp_abc_master = dq0_to_abc(vsp_dq0_master, phase)  # convert dq set-points into three-phase abc coordinates\n\n        # calculate\n        err = vdq0_master - vsp_dq0_master\n\n        if (abs(err) > self.funnel).any():\n            # E2 + Offset\n            error = (np.sum((np.abs((vsp_abc_master - vabc_master)) / self.v_limit) ** 0.5,\n                            axis=0) + 20) / self.max_episode_steps\n        else:\n            # E1 <! E2 and E1 << Offset\n            error = np.sum((np.abs((vsp_abc_master - vabc_master)) / self.v_limit) ** 2,\n                           axis=0) / self.max_episode_steps\n\n        return -error.squeeze()\n"
  },
  {
    "path": "experiments/model_validation/env/stochastic_components.py",
    "content": "import numpy as np\n\n\nclass Load:\n\n    def __init__(self, load_mean: float, load_std: float = None, balanced: bool = True, tolerance: float = 0.05):\n        \"\"\"\n        Load class which defines stochastic load. Samples load value from normal Gaussian distribution (GD) using given\n        mean and standard deviation\n        :param load_mean: Mean of the GD the load is sampled from\n        :param load_std: Standard deviation of the GD the load is sampled from\n        :param balanced: If True: all 3 phases are takes as equal; if False symmetrical load is applied\n        :param tolerance: Device manufacturing tolerance (with reference to the mean value)\n        \"\"\"\n\n        self.balanced = balanced\n        self.load_mean = load_mean\n        self.tolerance = tolerance\n        if load_std is None:\n            self.load_std = self.load_mean * 0.1\n        else:\n            self.load_std = load_std\n        self.gains = np.clip(\n            [np.random.normal(self.load_mean, self.load_std) for _ in range(1 if self.balanced else 3)],\n            (self.load_mean - self.load_mean * self.tolerance),\n            (self.load_mean + self.load_mean * self.tolerance)).tolist()\n\n    def load_step(self, t, n: int):\n        \"\"\"\n        Defines a load step after 0.2 s\n        Doubles the load parameters\n        :param t: t :D\n        :param n: Index referring to the current phase\n        :return: Dictionary with load parameters\n        \"\"\"\n\n        if n > 2:\n            raise ValueError('Choose between single or three phase!')\n\n        if len(self.gains) == 1:\n            return 1 * self.gains[0] if (t < .05 + 0.023  or t > 0.1 + 0.023) else 0.55 * self.gains[0]\n        else:\n            return 1 * self.gains[n] if (t < .05 + 0.023  or t > 0.1 + 0.023) else 0.55 * self.gains[n]\n\n    def give_value(self, t, n: int):\n        \"\"\"\n        Defines a load step after 0.2 s\n        Doubles the load parameters\n        :param t: t :D\n        :param n: Index referring to the current phase\n        :return: Dictionary with load parameters\n        \"\"\"\n\n        if n > 2:\n            raise ValueError('Choose between single or three phase!')\n\n        if len(self.gains) == 1:\n            return self.gains[0]\n        else:\n            return self.gains[n]\n\n    def reset(self):\n        self.gains = np.clip(\n            [np.random.normal(self.load_mean, self.load_std) for _ in range(1 if self.balanced else 3)],\n            (self.load_mean - self.load_mean * self.tolerance),\n            (self.load_mean + self.load_mean * self.tolerance)).tolist()\n\n\n"
  },
  {
    "path": "experiments/model_validation/env/testbench_voltage_ctrl.py",
    "content": "from socket import socket\n\nimport gym\nimport paramiko\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom time import strftime, gmtime, sleep\n\nfrom paramiko import BadHostKeyException, AuthenticationException, SSHException\n\nfrom openmodelica_microgrid_gym.util import dq0_to_abc\n\n\nclass TestbenchEnvVoltage(gym.Env):\n\n    viz_modes = {'episode', 'step', None}\n    \"\"\"Set of all valid visualisation modes\"\"\"\n\n    def __init__(self, host: str = '131.234.172.139', username: str = 'root', password: str = 'omg',\n                 DT: float = 1/20000, executable_script_name: str = 'my_first_hps' ,num_steps: int = 1000,\n                 kP: float = 0.052346, kI: float = 15.4072 , kPV: float = 0.018619 , kIV: float = 10.0,\n                 ref: float = 10.0, ref2: float =12, f_nom: float = 50.0, i_limit: float = 16,\n                 i_nominal: float = 12, v_nominal: float = 20, v_limit = 30, mu=2):\n\n        \"\"\"\n                Environment to connect the code to an FPGA-Controlled test-bench via SSH for votage controll.\n                Just for demonstration purpose - different\n        \"\"\"\n\n        self.ssh = paramiko.SSHClient()\n        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())\n\n        self.host = host\n        self.username = username\n        self.password = password\n        self.DT = DT\n        self.executable_script_name = executable_script_name\n        self.max_episode_steps = num_steps\n        self.kP = kP\n        self.kI = kI\n        self.kPV = kPV\n        self.kIV = kIV\n        self.ref = ref\n        self.ref2 = ref2\n        self.f_nom = f_nom\n        self.data = np.array(list())\n        self.data_obs = np.array(list())\n        self.current_step = 0\n        self.done = False\n        self.i_limit = i_limit\n        self.i_nominal = i_nominal\n        self.v_nominal = v_nominal\n        self.v_limit = v_limit\n        self.mu = mu\n\n    @staticmethod\n    def __decode_result(ssh_result):\n\n        result = list()\n        resultObs = list()\n        for line in ssh_result.read().splitlines():\n\n            temp = line.decode(\"utf-8\").split(\",\")\n\n            if len(temp) == 31:\n                #temp.pop(-1)  # Drop the last item\n\n                floats = [float(i) for i in temp]\n                # print(floats)\n                result.append(floats)\n            elif len(temp) == 9:\n                # print(temp)\n                floatsObs = [float(i) for i in temp]\n                # print(floatsObs)\n                resultObs.append(floatsObs)\n                # count=count+1\n            elif len(temp) != 1:\n                print(temp)\n\n        N = (len(result))\n        decoded_result = np.array(result)\n\n        return [decoded_result, np.array(resultObs)]\n\n    @staticmethod\n    def __decode_result_obs(ssh_result):\n\n\n        resultObs = list()\n        for line in ssh_result.read().splitlines():\n\n            temp = line.decode(\"utf-8\").split(\",\")\n\n            if len(temp) == 9:\n                # print(temp)\n                floatsObs = [float(i) for i in temp]\n                # print(floatsObs)\n                resultObs.append(floatsObs)\n                # count=count+1\n            elif len(temp) != 1:\n                print(temp)\n\n        N = (len(resultObs))\n        decoded_result = np.array(resultObs)\n\n        return decoded_result\n\n    def rew_fun(self, vabc_meas, vsp_abc) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and setpoints to evaluate the quality of the\n        used parameters.\n        Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic\n        barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu.\n\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        mu = self.mu\n\n        # setpoints\n        #Iabc_SP = dq0_to_abc(Idq0_SP, phase)  # convert dq set-points into three-phase abc coordinates\n\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        # plus barrier penalty for violating the current constraint\n        #error = np.sum((np.abs((Vabc_SP - vabc_meas)) / self.v_nominal) ** 0.5, axis=0) #+\\\n        error = (np.sum((np.abs((vsp_abc - vabc_meas)) / self.v_limit) ** 0.5, axis=0) +\\\n                -np.sum(mu * np.log(1 - np.maximum(np.abs(vabc_meas) - self.v_nominal, 0) / (self.v_limit - self.v_nominal)), axis=0) \\\n                 )/ self.max_episode_steps\n\n\n        return -error.squeeze()\n\n    #def reset(self, kP, kI, kPv, kIv):\n    def reset(self, kPv, kIv):\n        # toDo: ssh connection not open every episode!\n        #self.kP = kP\n        #self.kI = kI\n        self.kPV = kPv\n        self.kIV = kIv\n\n        #self.ssh.connect(self.host, username=self.username, password=self.password)\n\n        count_retries = 0\n        connected = False\n\n        #for x in range(10):\n        while not connected and count_retries < 10:\n            count_retries+=1\n            try:\n                print('I Try!')\n                self.ssh.connect(self.host, username=self.username, password=self.password)\n                connected =  True\n                # return True\n            except:  # (BadHostKeyException, AuthenticationException,\n                # SSHException, socket.gaierror) as e:\n                # print(e)\n                print('Argh')\n                sleep(1)\n\n        if count_retries == 10:\n            print('SSH FUCKED UP!')\n\n        # toDo: get SP and kP/I from agent?\n        # str_command = './{} {} {} {} {} {}'.format(self.executable_script_name, self.max_episode_steps, self.kP, self.kI,\n        #                                           self.i_ref, self.f_nom)\n\n        str_command = './{} -u 100 -n {} -v {} -f {} -1 {} -2 {} -4 {} -5 {} -L -E'.format(self.executable_script_name,\n                                                                                           self.max_episode_steps,\n                                                                                           self.v_nominal, self.f_nom,\n                                                                                           self.kP, self.kI, self.kPV,\n                                                                                           self.kIV\n                                                                                           )\n\n        ssh_stdin, ssh_stdout, ssh_stderr = self.ssh.exec_command(str_command)\n\n        self.data, self.data_obs = self.__decode_result(ssh_stdout)\n        # self.data_obs = self.__decode_result_obs(ssh_stdout)\n\n        self.current_step = 0\n        self.done = False\n\n        self.ssh.close()\n\n\n    def rew_fun4D(self, vabc_meas, vsp_abc, iabc_meas, iabc_SP) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and setpoints to evaluate the quality of the\n        used parameters.\n        Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic\n        barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu.\n\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        mu = self.mu\n\n        # setpoints\n        # Iabc_SP = dq0_to_abc(Idq0_SP, phase)  # convert dq set-points into three-phase abc coordinates\n\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        # plus barrier penalty for violating the current constraint\n        # error = np.sum((np.abs((Vabc_SP - vabc_meas)) / self.v_nominal) ** 0.5, axis=0) #+\\\n        error = (np.sum((np.abs((iabc_SP - iabc_meas)) / self.i_limit) ** 0.5, axis=0) \\\n                + -np.sum(80 * np.log(1 - np.maximum(np.abs(iabc_meas) - self.i_nominal, 0) / \\\n                (self.i_limit - self.i_nominal)), axis=0)+\n                            np.sum((np.abs((vsp_abc - vabc_meas)) / self.v_limit) ** 0.5, axis=0) + \\\n                     -np.sum(mu * np.log(\n                         1 - np.maximum(np.abs(vabc_meas) - self.v_nominal, 0) / (self.v_limit - self.v_nominal)), axis=0) \\\n                     ) / self.max_episode_steps\n\n        return -error.squeeze()\n\n    def reset4D(self, kP, kI, kPv, kIv):\n        # toDo: ssh connection not open every episode!\n        self.kP = kP\n        self.kI = kI\n        self.kPV = kPv\n        self.kIV = kIv\n\n        # self.ssh.connect(self.host, username=self.username, password=self.password)\n\n        count_retries = 0\n        connected = False\n\n        # for x in range(10):\n        while not connected and count_retries < 10:\n            count_retries += 1\n            try:\n                print('I Try!')\n                self.ssh.connect(self.host, username=self.username, password=self.password)\n                connected = True\n                # return True\n            except:  # (BadHostKeyException, AuthenticationException,\n                # SSHException, socket.gaierror) as e:\n                # print(e)\n                print('Argh')\n                sleep(1)\n\n        if count_retries == 10:\n            print('SSH FUCKED UP!')\n\n            # toDo: get SP and kP/I from agent?\n            # str_command = './{} {} {} {} {} {}'.format(self.executable_script_name, self.max_episode_steps, self.kP, self.kI,\n            #                                           self.i_ref, self.f_nom)\n\n        str_command = './{} {} {} {} {} {} {} {} {} {}'.format(self.executable_script_name, self.max_episode_steps,\n                                                                   np.int(self.max_episode_steps / 3 - 220),\n                                                                   np.int(self.max_episode_steps * 2 / 3 - 520),\n                                                                   self.kP, self.kI, self.kPV, self.kIV,\n                                                                   self.v_nominal, self.f_nom)\n\n        ssh_stdin, ssh_stdout, ssh_stderr = self.ssh.exec_command(str_command)\n\n        self.data, self.data_obs = self.__decode_result(ssh_stdout)\n            # self.data_obs = self.__decode_result_obs(ssh_stdout)\n\n        self.current_step = 0\n        self.done = False\n\n        self.ssh.close()\n\n    def step(self):\n        \"\"\"\n        Takes measured data and returns stepwise\n        Measured data recorded in reset -> controller part of env\n        \"\"\"\n        temp_data = self.data[self.current_step]\n        self.current_step += 1\n\n\n        V_abc_meas = temp_data[[0,1,2]]\n        V_abc_SP = temp_data[[10,11,12]]\n        I_abc_meas = temp_data[[3,4,5]]\n        I_abc_SP = temp_data[[24,25,26]]\n        #phase = temp_data[9]\n\n        #reward = self.rew_fun(V_abc_meas, V_abc_SP)\n        reward = self.rew_fun4D(V_abc_meas, V_abc_SP, I_abc_meas, I_abc_SP)\n\n        #V_abc_meas = temp_data[[0, 1, 2]]\n        # Idq0_SP = np.array([self.i_ref,0,0])\n        #V_abc_SP = temp_data[[10, 11, 12]]\n        #reward = self.rew_fun(V_abc_meas, V_abc_SP)\n\n        if self.current_step == self.max_episode_steps:\n            self.done = True\n\n        info = []\n\n        return temp_data, reward, self.done, info\n\n    def render(self, J, save_folder):\n\n        N = (len(self.data))\n        t = np.linspace(0, N * self.DT, N)\n\n        V_A = self.data[:, 0]\n        V_B = self.data[:, 1]\n        V_C = self.data[:, 2]\n        I_A = self.data[:, 3]\n        I_B = self.data[:, 4]\n        I_C = self.data[:, 5]\n        I_D = self.data[:, 6]\n        I_Q = self.data[:, 7]\n        I_0 = self.data[:, 8]\n        SP_A = self.data[:, 9]\n        SP_B = self.data[:, 10]\n        SP_C = self.data[:, 11]\n        m_A = self.data[:, 12]\n        m_B = self.data[:, 13]\n        m_C = self.data[:, 14]\n        m_D = self.data[:, 15]\n        m_Q = self.data[:, 16]\n        m_0 = self.data[:, 17]\n        ICont_Out_D = self.data[:, 18]\n        ICont_Out_Q = self.data[:, 19]\n        ICont_Out_0 = self.data[:, 20]\n        UCont_Out_D = self.data[:, 21]\n        UCont_Out_Q = self.data[:, 22]\n        UCont_Out_0 = self.data[:, 23]\n        ISP_A = self.data[:, 24]\n        ISP_B = self.data[:, 25]\n        ISP_C = self.data[:, 26]\n        V_D = self.data[:, 27]\n        V_Q = self.data[:, 28]\n        V_0 = self.data[:, 29]\n        Ph = self.data[:, 30]\n\n        \"\"\"\n        If_A = self.data[:, 31];\n        If_B = self.data[:, 32];\n        If_C = self.data[:, 33];\n        Vc_A = self.data[:, 34];\n        Vc_B = self.data[:, 35];\n        Vc_C = self.data[:, 36];\n        \"\"\"\n        #        Io_A = self.data_obs[:, 6]\n        #        Io_B = self.data_obs[:, 7]\n        #        Io_C = self.data_obs[:, 8]\n\n        # store measurment to dataframe\n        df = pd.DataFrame({'V_A': self.data[:, 0],\n                           'V_B': self.data[:, 1],\n                           'V_C': self.data[:, 2],\n                           'I_A': self.data[:, 3],\n                           'I_B': self.data[:, 4],\n                           'I_C': self.data[:, 5],\n                           'I_D': self.data[:, 6],\n                           'I_Q': self.data[:, 7],\n                           'I_0': self.data[:, 8],\n                           'Ph': self.data[:, 9],\n                           'SP_A': self.data[:, 10],\n                           'SP_B': self.data[:, 11],\n                           'SP_C': self.data[:, 12],\n                           'm_A': self.data[:, 13],\n                           'm_B': self.data[:, 14],\n                           'm_C': self.data[:, 15],\n                           'm_D': self.data[:, 16],\n                           'm_Q': self.data[:, 17],\n                           'm_0': self.data[:, 18]})\n\n        #df.to_pickle('Measurement')\n        #df.to_pickle('Noise_measurement')\n\n        #plt.plot(t, V_A, t, V_B, t, V_C)\n        #plt.ylabel('Voltages (V)')\n        #plt.show()\n\n        fig = plt.figure()\n        plt.plot(t, V_A, 'b', label=r'$v_{\\mathrm{a}}$')\n        plt.plot(t, V_B, 'g')\n        plt.plot(t, V_C, 'r')\n        plt.plot(t, SP_A, 'b--', label=r'$v_{\\mathrm{SP,a}}$')\n        plt.plot(t, SP_B, 'g--')\n        plt.plot(t, SP_C, 'r--')\n        plt.xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        plt.ylabel('$v_{\\mathrm{abc}}\\,/\\,\\mathrm{V}$')\n        #plt.title('{}'.format(J))\n        plt.grid()\n        plt.legend()\n        plt.show()\n        #time = strftime(\"%Y-%m-%d %H:%M:%S\", gmtime())\n        #fig.savefig('hardwareTest_plt/{}_abcInductor_currents' + time + '.pdf'.format(J))\n        fig.savefig(save_folder +'/J_{}_abcvoltage.pdf'.format(J))\n        fig.savefig(save_folder +'/J_{}_abcvoltage.pgf'.format(J))\n\n\n\n\n        fig = plt.figure()\n        plt.plot(t, I_A, 'b' , label = r'$i_{\\mathrm{a}}$')\n        plt.plot(t, I_B, 'g')\n        plt.plot(t, I_C, 'r')\n        #plt.plot(t, SP_A, 'b--', label = r'$i_{\\mathrm{a}}$')\n        #plt.plot(t, SP_B, 'g--')\n        #plt.plot(t, SP_C, 'r--')\n        plt.xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        plt.ylabel('$i_{\\mathrm{abc}}\\,/\\,\\mathrm{A}$')\n        #plt.title('{}'.format(J))\n        plt.grid()\n        plt.legend()\n        plt.show()\n        #time = strftime(\"%Y-%m-%d %H:%M:%S\", gmtime())\n        #fig.savefig('hardwareTest_plt/{}_abcInductor_currents' + time + '.pdf'.format(J))\n        fig.savefig(save_folder +'/J_{}_abcInductor_currents.pdf'.format(J))\n        fig.savefig(save_folder +'/J_{}_abcInductor_currents.pgf'.format(J))\n\n\n        fig = plt.figure()\n        plt.plot(t, I_D, t, I_Q, t, I_0)\n        plt.xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        plt.ylabel('$v_{\\mathrm{dq0}}\\,/\\,\\mathrm{V}$')\n        plt.title('{}'.format(J))\n        #plt.ylim([-3, 16])\n        plt.grid()\n        plt.show()\n        time = strftime(\"%Y-%m-%d %H:%M:%S\", gmtime())\n        #fig.savefig('hardwareTest_plt/dq0Inductor_currents' + time + '.pdf')\n        #fig.savefig('Paper_meas/J_{}_dq0Inductor_currents.pdf'.format(J))\n\n        fig = plt.figure()\n        plt.plot(t, I_D, t, I_Q, t, I_0)\n        plt.xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        plt.ylabel('$i_{\\mathrm{dq0}}\\,/\\,\\mathrm{A}$')\n        plt.title('{}'.format(J))\n        # plt.ylim([-3, 16])\n        plt.grid()\n        plt.show()\n        time = strftime(\"%Y-%m-%d %H:%M:%S\", gmtime())\n\n\n        fig = plt.figure()\n        plt.plot(t, V_D, t, V_Q, t, V_0)\n        plt.xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        plt.ylabel('$v_{\\mathrm{dq0}}\\,/\\,\\mathrm{V}$')\n        plt.title('{}'.format(J))\n        # plt.ylim([-3, 16])\n        #plt.xlim([0, 0.025])\n        plt.grid()\n        plt.show()\n        time = strftime(\"%Y-%m-%d %H:%M:%S\", gmtime())\n        fig.savefig(save_folder + '/J_{}_dq0voltage.pdf'.format(J))\n        fig.savefig(save_folder + '/J_{}_dq0voltage.pgf'.format(J))\n\n        \"\"\"\n        fig = plt.figure()\n        plt.plot(t, V_D, t, V_Q, t, V_0)\n        plt.xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        plt.ylabel('$v_{\\mathrm{dq0}}\\,/\\,\\mathrm{V}$')\n        plt.title('{}'.format(J))\n        # plt.ylim([-3, 16])\n        plt.xlim([0, 0.025])\n        plt.grid()\n        plt.show()\n        time = strftime(\"%Y-%m-%d %H:%M:%S\", gmtime())\n        \"\"\"\n\n#       fig = plt.figure()\n#       plt.plot(t, Io_A, 'b', label=r'$i_{\\mathrm{a}}$')\n#       plt.plot(t, Io_B, 'g')\n#       plt.plot(t, Io_C, 'r')\n#       # plt.plot(t, SP_A, 'b--', label = r'$i_{\\mathrm{a}}$')\n#       # plt.plot(t, SP_B, 'g--')\n#       # plt.plot(t, SP_C, 'r--')\n#       plt.xlabel(r'$t\\,/\\,\\mathrm{s}$')\n#       plt.ylabel('$i_{\\mathrm{abc,est}}\\,/\\,\\mathrm{A}$')\n#       # plt.title('{}'.format(J))\n#       plt.grid()\n#       plt.legend()\n#       plt.show()\n"
  },
  {
    "path": "experiments/model_validation/execution/monte_carlo_runner.py",
    "content": "import numpy as np\nfrom typing import Dict, Any\nfrom tqdm import tqdm\nfrom openmodelica_microgrid_gym.agents.episodic import EpisodicLearnerAgent\nfrom openmodelica_microgrid_gym.env import ModelicaEnv\n\n\nclass MonteCarloRunner:\n    \"\"\"\n    This class will execute an agent on the environment.\n    It handles communication between agent and environment and handles the execution of multiple epochs\n    Additionally to runner, the Monte-Carlo runner has an additional loop to perform n_MC experiments using one\n    (controller) parameter set before update the (controller) parameters.\n    Therefore, the agent.observe function is used.\n    Inside the MC-loop the observe function is called with terminated = False to only update the return.\n    The return is stored in an array at the end of the MC-loop.\n    After finishing the MC-loop, the average of the return-array is used to update the (controller) parameters.\n    Therefore, the agent-observe function is called with terminated = True\n    \"\"\"\n\n    def __init__(self, agent: EpisodicLearnerAgent, env: ModelicaEnv):\n        \"\"\"\n\n        :param agent: Agent that acts on the environment\n        :param env: Environment tha Agent acts on\n        \"\"\"\n        self.env = env\n        self.agent = agent\n        self.agent.env = env\n        self.run_data = dict()  # type: Dict[str,Any]\n        \"\"\"\n        Dictionary storing information about the experiment.\n\n        - \"best_env_plt\": environment best plots\n        - \"best_episode_idx\": index of best episode\n        - \"agent_plt\": last agent plot\n        \"\"\"\n\n    def run(self, n_episodes: int = 10, n_mc: int = 5, visualise: bool = False, prepare_mc_experiment=lambda: True,\n            return_gradient_extend: bool = False):\n        \"\"\"\n        Trains/executes the agent on the environment for a number of epochs\n\n        :param n_episodes: number of epochs to play\n        :param n_mc: number of Monte-Carlo experiments using the same parameter set before updating the latter\n        :param visualise: turns on visualization of the environment\n        :param prepare_mc_experiment: prepares experiment by resetting stochastic components\n        :param return_gradient_extend: calculates gradient extension for return if return_gradient_extend\n        \"\"\"\n        t = np.linspace(0, self.env.max_episode_steps * self.env.net.ts, self.env.max_episode_steps + 1)\n        self.agent.reset()\n        self.env.history.cols = self.env.history.structured_cols(None) + self.agent.measurement_cols\n        self.agent.obs_varnames = self.env.history.cols\n        self.env.measure = self.agent.measure\n\n        initial_performance_mc = np.zeros(n_mc)\n        performance_mc = np.zeros(n_mc)\n\n        if not visualise:\n            self.env.viz_mode = None\n        agent_fig = None\n\n        for i in tqdm(range(n_episodes), desc='episodes', unit='epoch'):\n            done, r = False, None\n            np.random.seed(0)\n            for m in tqdm(range(n_mc), desc='monte_carlo_run', unit='epoch', leave=False):\n                prepare_mc_experiment()  # reset stoch components\n\n                r_vec = np.zeros(self.env.max_episode_steps)\n\n                obs = self.env.reset()\n\n                for p in tqdm(range(self.env.max_episode_steps), desc='steps', unit='step', leave=False):\n                    self.agent.observe(r, False)\n                    act = self.agent.act(obs)\n                    obs, r, done, info = self.env.step(act)\n                    r_vec[p] = r\n                    self.env.render()\n                    if done:\n                        self.agent.observe(r, False)\n\n                        if return_gradient_extend:\n                            w = self.env.history['master.CVVd'].values\n                            w1 = self.env.history['master.CVVq'].values\n                            w2 = self.env.history['master.CVV0'].values\n                            v = self.env.history['master.SPVd'].values\n\n                            SP_sattle = (abs(w - v) < v * 0.12).astype(int)  # 0.12 -> +-20V setpoint\n\n                            dw = np.gradient(w)\n                            dw1 = np.gradient(w1)\n                            dw2 = np.gradient(w2)\n\n                            dev_return = (np.mean(abs(SP_sattle * dw)) + np.mean(abs(SP_sattle * dw1)) + np.mean(\n                                abs(SP_sattle * dw2)))\n                        else:\n                            dev_return = 0\n                            print('NO DEV RETURN!!!!')\n\n                        dev_fac = 5  # 3\n\n                        print(self.agent.episode_return)\n                        print(dev_return)\n\n                        self.agent.performance = ((\n                                                              self.agent.episode_return - dev_return * dev_fac) - self.agent.min_performance) \\\n                                                 / (self.agent.initial_performance - self.agent.min_performance)\n\n                        if m == 0 and i == 0:\n                            self.agent.initial_performance = self.agent.episode_return - dev_return * dev_fac\n                            self.agent.performance = ((\n                                                                  self.agent.episode_return - dev_return * dev_fac) - self.agent.min_performance) \\\n                                                     / (\n                                                                 self.agent.initial_performance - self.agent.min_performance)  # instead of perf/initial_perf\n                            self.agent.last_best_performance = self.agent.performance\n                            self.agent.last_worst_performance = self.agent.performance\n\n                            self.agent.best_episode = self.agent.history.df.shape[0]\n                            self.agent.last_best_performance = self.agent.performance\n                            self.agent.worst_episode = self.agent.history.df.shape[0]\n                            self.agent.last_worst_performance = self.agent.performance\n\n                        self.agent.performance = ((\n                                                          self.agent.episode_return - dev_return * dev_fac) - self.agent.min_performance) \\\n                                                 / (self.agent.initial_performance - self.agent.min_performance)\n\n                        performance_mc[m] = self.agent.performance\n                        initial_performance_mc[m] = self.agent.episode_return\n                        # set iterations and episode return = 0\n                        self.agent.prepare_episode()\n\n                        break\n\n                _, env_fig = self.env.close()\n\n                # vor break?\n                if (m == 0 and i == 0):  # and self.agent.has_improved:\n                    self.run_data['best_env_plt'] = env_fig\n                    self.run_data['best_episode_idx'] = i\n                    self.agent.last_best_performance = self.agent.performance\n\n                if (m == 0 and i == 0):  # and self.agent.has_worsened:\n                    self.run_data['worst_env_plt'] = env_fig\n                    self.run_data['worst_episode_idx'] = i\n                    self.agent.last_worst_performance = self.agent.performance\n\n            if i == 0:\n                # performance was normalized to first run -> use average of first episode so that J_initial for first\n                # is 1\n                eps_ret = performance_mc * (\n                            self.agent.initial_performance - self.agent.min_performance) + self.agent.min_performance\n                self.agent.initial_performance = np.mean(eps_ret)\n                performance_mc = (eps_ret - self.agent.min_performance) \\\n                                 / (self.agent.initial_performance - self.agent.min_performance)\n\n            self.agent.performance = np.mean(performance_mc)\n            self.agent.update_params()\n\n            if visualise:\n                agent_fig = self.agent.render()\n\n            self.run_data['last_agent_plt'] = agent_fig\n"
  },
  {
    "path": "experiments/model_validation/execution/runner_hardware.py",
    "content": "from typing import Dict, Any\n\nfrom tqdm import tqdm\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.agents import Agent\nfrom experiments.model_validation.env.physical_testbench import TestbenchEnv\nfrom experiments.model_validation.env.testbench_voltage_ctrl import TestbenchEnvVoltage\n\n\nclass RunnerHardware:\n    \"\"\"\n    This class will execute an agent on the environment.\n    It handles communication between agent and environment and handles the execution of multiple epochs\n    \"\"\"\n\n    def __init__(self, agent: Agent, env: TestbenchEnv):\n        \"\"\"\n\n        :param agent: Agent that acts on the environment\n        :param env: Environment that Agent acts on\n        \"\"\"\n        self.env = env\n        self.agent = agent\n        self.agent.env = env\n        self.run_data = dict()  # type: Dict[str,Any]\n        \"\"\"\n        :type dict:\n        \n        Stores information about the experiment.\n        best_env_plt - environment best plots\n        best_episode_idx - index of best episode\n        agent_plt - last agent plot\n        \n        \"\"\"\n\n    def run(self, n_episodes: int = 10, visualise: bool = False, save_folder: str = 'Mess'):\n        \"\"\"\n        Trains/executes the agent on the environment for a number of epochs\n\n        :param n_episodes: number of epochs to play\n        :param visualise: turns on visualization of the environment\n        :param save_folder: string with save folder name\n        \"\"\"\n        self.agent.reset()\n\n        agent_fig = None\n\n        for i in tqdm(range(n_episodes), desc='episodes', unit='epoch'):\n            self.env.reset(self.agent.params[0], self.agent.params[1])\n            #self.env.render(0)\n            done, r = False, None\n            for _ in tqdm(range(self.env.max_episode_steps), desc='steps', unit='step', leave=False):\n                self.agent.observe(r, done)\n                #act = self.agent.act(obs)\n                #self.env.measurement = self.agent.measurement\n                obs, r, done, info = self.env.step()\n                #self.env.render()\n                if done:\n                    break\n            self.env.render(0, save_folder)\n            self.agent.observe(r, done)\n\n            self.env.render(self.agent.history.df.J.iloc[-1], save_folder)\n\n\n            print(self.agent.unsafe)\n\n            if visualise:\n                agent_fig = self.agent.render()\n\n            self.run_data['last_agent_plt'] = agent_fig\n\n            if i == 0 or self.agent.has_improved:\n                #self.run_data['best_env_plt'] = env_fig\n                self.run_data['best_episode_idx'] = i\n\n\nclass RunnerHardwareGradient:\n    \"\"\"\n    This class will execute an agent on the environment.\n    It handles communication between agent and environment and handles the execution of multiple epochs\n    adds gradient depending return\n    \"\"\"\n\n    def __init__(self, agent: Agent, env: TestbenchEnvVoltage):\n        \"\"\"\n\n        :param agent: Agent that acts on the environment\n        :param env: Environment that Agent acts on\n        \"\"\"\n        self.env = env\n        self.agent = agent\n        self.agent.env = env\n        self.run_data = dict()  # type: Dict[str,Any]\n        \"\"\"\n        :type dict:\n\n        Stores information about the experiment.\n        best_env_plt - environment best plots\n        best_episode_idx - index of best episode\n        agent_plt - last agent plot\n\n        \"\"\"\n\n    def run(self, n_episodes: int = 10, visualise: bool = False, save_folder: str = 'Mess'):\n        \"\"\"\n        Trains/executes the agent on the environment for a number of epochs\n\n        :param n_episodes: number of epochs to play\n        :param visualise: turns on visualization of the environment\n        :param save_folder: string with save folder name\n        \"\"\"\n        self.agent.reset()\n        # self.env.history.cols = self.env.history.structured_cols(None) + self.agent.measurement_cols\n        # self.agent.obs_varnames = self.env.history.cols\n\n        # if not visualise:\n        #    self.env.viz_mode = None\n        agent_fig = None\n\n        for i in tqdm(range(n_episodes), desc='episodes', unit='epoch'):\n            self.env.reset4D(self.agent.params[0], self.agent.params[1], self.agent.params[2], self.agent.params[3])\n            #self.env.reset(self.agent.params[0], self.agent.params[1])\n            # self.env.reset(0.01, self.agent.params[0])\n            # self.env.render(0)\n            print(self.agent.params[0])\n            print(self.agent.params[1])\n            done, r = False, None\n            for _ in tqdm(range(self.env.max_episode_steps), desc='steps', unit='step', leave=False):\n                self.agent.observe(r, False)\n                # act = self.agent.act(obs)\n                # self.env.measurement = self.agent.measurement\n                obs, r, done, info = self.env.step()\n                # self.env.render()\n                if done:\n                    self.agent.observe(r, False)\n\n                    w = self.env.data[:, 27]\n                    w1 = self.env.data[:, 28]\n                    w2 = self.env.data[:, 29]\n\n                    v = np.ones(len(w)) * 169.7\n\n                    SP_sattle = (abs(w - v) < v * 0.12).astype(int)  # 0.12 -> +-20V setpoint\n\n                    dw = np.gradient(w)\n                    dw1 = np.gradient(w1)\n                    dw2 = np.gradient(w2)\n\n                    dev_return = (np.mean(abs(SP_sattle * dw)) + np.mean(abs(SP_sattle * dw1)) + np.mean(\n                        abs(SP_sattle * dw2)))\n\n                    dev_fac = 2.5\n\n\n                    if i == 0:\n                        self.agent.initial_performance = self.agent.episode_return - dev_return *dev_fac\n                        self.agent.performance = ((\n                                                              self.agent.episode_return - dev_return *dev_fac) - self.agent.min_performance) \\\n                                                 / (self.agent.initial_performance - self.agent.min_performance)\n                        self.agent.last_best_performance = self.agent.performance\n                        self.agent.last_worst_performance = self.agent.performance\n\n                    self.agent.performance = ((self.agent.episode_return - dev_return *dev_fac) - self.agent.min_performance) \\\n                                             / (self.agent.initial_performance - self.agent.min_performance)\n\n                    self.agent.prepare_episode()\n            #self.env.render(0, save_folder)\n            self.agent.update_params()\n            # self.agent.observe(r, done)\n\n            self.env.render(self.agent.history.df.J.iloc[-1], save_folder)\n\n            print(self.agent.unsafe)\n\n            if visualise:\n                agent_fig = self.agent.render()\n\n            self.run_data['last_agent_plt'] = agent_fig\n\n            if i == 0 or self.agent.has_improved:\n                # self.run_data['best_env_plt'] = env_fig\n                self.run_data['best_episode_idx'] = i\n"
  },
  {
    "path": "experiments/model_validation/lengthScaleSweepMC650.py",
    "content": "#####################################\n# Experiment Multicore to search for propper lengthscale: Single inverter supplying current to an short circuit via a LR filter\n# Controller: PI current controller gain parameters are optimized by SafeOpt\n# a) FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters\n# b) connecting via ssh to a testbench to perform real-world measurement\n\n\nimport logging\nimport os\nfrom distutils.util import strtobool\nfrom functools import partial\nfrom itertools import product\nfrom itertools import tee\nfrom multiprocessing import Pool\nfrom os.path import isfile\n\nimport GPy\nimport gym\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\n\nfrom openmodelica_microgrid_gym.env.plotmanager import PlotManager\nfrom experiments.model_validation.env.rewards import Reward\nfrom openmodelica_microgrid_gym.net import Network\n\nparams = {'backend': 'ps',\n          'axes.labelsize': 8,  # fontsize for x and y labels (was 10)\n          'axes.titlesize': 8,\n          'font.size': 8,  # was 10\n          'legend.fontsize': 8,  # was 10\n          'xtick.labelsize': 8,\n          'ytick.labelsize': 8,\n          # 'text.usetex': True,\n          # 'figure.figsize': [3.39, 2.5],\n          'figure.figsize': [3.9, 3.1],\n          'font.family': 'serif',\n          'lines.linewidth': 1\n          }\nmatplotlib.rcParams.update(params)\n\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat, MutableParams\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, MultiPhaseDQCurrentSourcingController\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom experiments.model_validation.env.stochastic_components import Load\nfrom experiments.model_validation.execution.monte_carlo_runner import MonteCarloRunner\nfrom openmodelica_microgrid_gym.util import FullHistory\n\nlengthscale_vec_kP = 0.0005 * np.logspace(.5, 1.5, 5)\nlengthscale_vec_kI = np.logspace(.5, 1.5, 5)\n\n# Choose which controller parameters should be adjusted by SafeOpt.\n# - Kp: 1D example: Only the proportional gain Kp of the PI controller is adjusted\n# - Ki: 1D example: Only the integral gain Ki of the PI controller is adjusted\n# - Kpi: 2D example: Kp and Ki are adjusted simultaneously\nadjust = 'Ki'\n\n# Check if really only one simulation scenario was selected\nif adjust not in {'Kp', 'Ki', 'Kpi'}:\n    raise ValueError(\"Please set 'adjust' to one of the following values: 'Kp', 'Ki', 'Kpi'\")\n\ninclude_simulate = True\nshow_plots = False\nbalanced_load = True\ndo_measurement = False\nsave_results = True\n\n# Files saves results and  resulting plots to the folder saves_VI_control_safeopt in the current directory\ncurrent_directory = os.getcwd()\nsave_folder = os.path.join(current_directory, r'../1Test')\nos.makedirs(save_folder, exist_ok=True)\n\nnp.random.seed(1)\n\n# Simulation definitions\nnet = Network.load('../../net/net_single-inv-curr_Paper_SC.yaml')\ndelta_t = 1e-4  # simulation time step size / s\nundersample = 1  # undersampling of controller\nmax_episode_steps = 1000  # number of simulation steps per episode\nnum_episodes = 1  # number of simulation episodes (i.e. SafeOpt iterations)\nn_MC = 1  # number of Monte-Carlo samples for simulation - samples device parameters (e.g. L,R, noise) from\niLimit = 16  # inverter current limit / A\niNominal = 12  # nominal inverter current / A\nmu = 80  # factor for barrier function (see below)\ni_ref1 = np.array([10, 0, 0])  # exemplary set point i.e. id = 10, iq = 0, i0 = 0 / A\ni_ref2 = np.array([5, 0, 0])  # exemplary set point i.e. id = 15, iq = 0, i0 = 0 / A\n\n# plant\nL = 2.3e-3  # / H\nR = 400e-3  # / Ohm\n\nphase_shift = 5\namp_dev = 1.1\n\n\ndef pairwise(iterable):\n    \"s -> (s0,s1), (s1,s2), (s2, s3), ...\"\n    a, b = tee(iterable)\n    next(b, None)\n    return zip(a, b)\n\n\ndef cal_j_min(phase_shift, amp_dev):\n    \"\"\"\n    Calulated the miminum performance for safeopt\n    Best case error of all safe boundary scenarios is used (max) to indicate which typ of error tears\n    the safe boarder first (the weakest link in the chain)\n    \"\"\"\n\n    ph_list = [phase_shift, 0]\n    amp_list = [1, amp_dev]\n    return_j_min = np.empty(len(ph_list))\n    error_j_min = np.empty(3)\n    ph_shift = [0, 120, 240]\n    t = np.linspace(0, max_episode_steps * delta_t, max_episode_steps)\n\n    # risetime = 0.0015 -> 15 steps um auf 10A zu kommen: grade = 10/15\n    grad = 0.66  # 1e-1\n    irefs = [0, i_ref1[0], i_ref2[0]]\n    ts = [0, max_episode_steps // 2, max_episode_steps]\n\n    for q in range(len(ph_list)):\n        for p in range(3):\n            amplitude_sp = np.concatenate([np.full(t1 - t0, r1)\n                                           for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))])\n            amplitude = np.concatenate(\n                [np.minimum(\n                    r0 + grad * np.arange(0, t1 - t0),  # ramp up phase\n                    np.full(t1 - t0, r1)  # max amplitude\n                ) for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))])\n            mess = amp_list[q] * amplitude * np.cos(\n                2 * np.pi * 50 * t + (ph_list[q] * np.pi / 180) + (ph_shift[p] * np.pi / 180))\n            sp = amplitude_sp * np.cos(2 * np.pi * 50 * t + (ph_shift[p] * np.pi / 180))\n            error_j_min[p] = -np.sum((np.abs((sp - mess)) / iLimit) ** 0.5, axis=0) / max_episode_steps\n        return_j_min[q] = np.sum(error_j_min)  # Sum all 3 phases\n    return max(return_j_min)\n\n\n# @memoize\ndef run_experiment(len_kp, len_ki):\n    if isfile(f'{save_folder}/{len_kp:.4f},{len_ki:.4f}.txt'):\n        with open(f'{save_folder}/{len_kp:.4f},{len_ki:.4f}.txt', 'r')as f:\n            return strtobool(f.read().strip())\n\n    rew = Reward(i_limit=iLimit, i_nominal=iNominal, mu_c=mu, max_episode_steps=max_episode_steps,\n                 obs_dict=[[f'lc.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0']])\n\n    #####################################\n    # Definitions for the GP\n    prior_mean = 0  # 2  # mean factor of the GP prior mean which is multiplied with the first performance of the\n    # initial set\n    noise_var = 0.001  # 0.001 ** 2  # measurement noise sigma_omega\n    prior_var = 2  # prior variance of the GP\n\n    bounds = None\n    lengthscale = None\n    if adjust == 'Kp':\n        bounds = [(0.0001, 0.1)]  # bounds on the input variable Kp\n        lengthscale = [.025]  # length scale for the parameter variation [Kp] for the GP\n\n    # For 1D example, if Ki should be adjusted\n    if adjust == 'Ki':\n        bounds = [(0, 20)]  # bounds on the input variable Ki\n        lengthscale = [10]  # length scale for the parameter variation [Ki] for the GP\n\n    # For 2D example, choose Kp and Ki as mutable parameters (below) and define bounds and lengthscale for both of them\n    if adjust == 'Kpi':\n        bounds = [(0.001, 0.07), (2, 150)]\n        lengthscale = [0.012, 30.]\n\n    df_len = pd.DataFrame({'lengthscale': lengthscale,\n                           'bounds': bounds,\n                           'balanced_load': balanced_load,\n                           'barrier_param_mu': mu})\n\n    # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times\n    # the initial performance: safe_threshold = 0.8 means. Performance measurement for optimization are seen as\n    # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!)\n    # parameter set\n    safe_threshold = 0\n    j_min = cal_j_min(phase_shift, amp_dev)  # Used for normalization\n\n    # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop\n    # expanding points eventually.\n    # The following variable is multiplied with the first performance of the initial set by the factor below:\n    explore_threshold = 0\n\n    # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of\n    # limit exceeded\n    # has to be negative due to normalized performance (regarding J_init = 1)\n    abort_reward = 100 * j_min\n\n    # Definition of the kernel\n    kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True)\n\n    #####################################\n    # Definition of the controllers\n    mutable_params = None\n    current_dqp_iparams = None\n    if adjust == 'Kp':\n        # mutable_params = parameter (Kp gain of the current controller of the inverter) to be optimized using\n        # the SafeOpt algorithm\n        mutable_params = dict(currentP=MutableFloat(0.04))\n\n        # Define the PI parameters for the current controller of the inverter\n        current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=12, limits=(-1, 1))\n\n    # For 1D example, if Ki should be adjusted\n    elif adjust == 'Ki':\n        mutable_params = dict(currentI=MutableFloat(5))\n        current_dqp_iparams = PI_params(kP=0.005, kI=mutable_params['currentI'], limits=(-1, 1))\n\n    # For 2D example, choose Kp and Ki as mutable parameters\n    elif adjust == 'Kpi':\n        mutable_params = dict(currentP=MutableFloat(0.04), currentI=MutableFloat(11.8))\n        current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'],\n                                        limits=(-1, 1))\n\n    # Define a current sourcing inverter as master inverter using the pi and droop parameters from above\n    ctrl = MultiPhaseDQCurrentSourcingController(current_dqp_iparams, delta_t,\n                                                 undersampling=undersample, name='master', f_nom=net.freq_nom)\n\n    i_ref = MutableParams([MutableFloat(f) for f in i_ref1])\n    #####################################\n    # Definition of the optimization agent\n    # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example\n    # Arguments described above\n    # History is used to store results\n    agent = SafeOptAgent(mutable_params,\n                         abort_reward,\n                         j_min,\n                         kernel,\n                         dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold,\n                              explore_threshold=explore_threshold), [ctrl],\n                         dict(master=[[f'lc.inductor{k}.i' for k in '123'], i_ref]), history=FullHistory(),\n                         )\n\n    #####################################\n    # Definition of the environment using a FMU created by OpenModelica\n    # (https://www.openmodelica.org/)\n    # Using an inverter supplying a load\n    # - using the reward function described above as callable in the env\n    # - viz_cols used to choose which measurement values should be displayed (here, only the 3 currents across the\n    #   inductors of the inverters are plotted. Labels and grid is adjusted using the PlotTmpl (For more information,\n    #   see UserGuide)\n    # - inputs to the models are the connection points to the inverters (see user guide for more details)\n    # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors\n\n    if include_simulate:\n\n        # Defining unbalanced loads sampling from Gaussian distribution with sdt = 0.2*mean\n        # r_load = Load(R, 0.1 * R, balanced=balanced_load, tolerance=0.1)\n        # l_load = Load(L, 0.1 * L, balanced=balanced_load, tolerance=0.1)\n        # i_noise = Noise([0, 0, 0], [0.0023, 0.0015, 0.0018], 0.0005, 0.32)\n\n        # if no noise should be included:\n        r_load = Load(R, 0 * R, balanced=balanced_load)\n        l_load = Load(L, 0 * L, balanced=balanced_load)\n\n        def reset_loads():\n            r_load.reset()\n            l_load.reset()\n\n        plotter = PlotManager(agent, save_results=save_results, save_folder=save_folder,\n                              show_plots=show_plots)\n\n        def ugly_foo(t):\n\n            if t >= .05:\n                i_ref[:] = i_ref2\n            else:\n                i_ref[:] = i_ref1\n\n            return partial(l_load.give_value, n=2)(t)\n\n        env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                       # reward_fun=Reward().rew_fun,\n                       reward_fun=rew.rew_fun_c,\n                       # time_step=delta_t,\n                       viz_cols=[\n                           PlotTmpl([[f'lc.inductor{i}.i' for i in '123'], [f'master.SPI{i}' for i in 'abc']],\n                                    callback=plotter.xylables_i_abc,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    ),\n                           PlotTmpl([[f'master.m{i}' for i in 'abc']],\n                                    callback=lambda fig: plotter.update_axes(fig, title='Simulation',\n                                                                             ylabel='$m_{\\mathrm{abc}}\\,/\\,\\mathrm{}$')\n                                    ),\n                           PlotTmpl([[f'master.CVI{i}' for i in 'dq0'], [f'master.SPI{i}' for i in 'dq0']],\n                                    callback=plotter.xylables_i_dq0,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    )\n                       ],\n                       log_level=logging.INFO,\n                       viz_mode='episode',\n                       max_episode_steps=max_episode_steps,\n                       model_params={'lc.resistor1.R': partial(r_load.give_value, n=0),\n                                     'lc.resistor2.R': partial(r_load.give_value, n=1),\n                                     'lc.resistor3.R': partial(r_load.give_value, n=2),\n                                     'lc.inductor1.L': partial(l_load.give_value, n=0),\n                                     'lc.inductor2.L': partial(l_load.give_value, n=1),\n                                     'lc.inductor3.L': ugly_foo},\n                       model_path='../../omg_grid/grid.paper.fmu',\n                       # model_path='../omg_grid/omg_grid.Grids.Paper_SC.fmu',\n                       net=net,\n                       history=FullHistory(),\n                       action_time_delay=1 * undersample\n                       )\n\n        runner = MonteCarloRunner(agent, env)\n\n        runner.run(num_episodes, n_mc=n_MC, visualise=True, prepare_mc_experiment=reset_loads)\n\n        with open(f'{save_folder}/{len_kp:.4f},{len_ki:.4f}.txt', 'w')as f:\n            print(f'{agent.unsafe}', file=f)\n\n        return agent.unsafe\n\n\nif __name__ == '__main__':\n    print(lengthscale_vec_kP, lengthscale_vec_kI)\n    with Pool(5) as p:\n        is_unsafe = p.starmap(run_experiment, product(lengthscale_vec_kP, lengthscale_vec_kI))\n\n    safe_vec = np.empty([len(lengthscale_vec_kP), len(lengthscale_vec_kI)])\n\n    for ((kk, ls_kP), (ii, ls_IP)), unsafe in zip(product(enumerate(lengthscale_vec_kP), enumerate(lengthscale_vec_kI)),\n                                                  is_unsafe):\n        safe_vec[kk, ii] = int(not unsafe)\n\n    df = pd.DataFrame(safe_vec, index=[f'{i:.3f}' for i in lengthscale_vec_kP],\n                      columns=[f'{i:.2f}' for i in lengthscale_vec_kI])\n    df.to_pickle(save_folder + '/Unsafe_matrix')\n    print(df)\n    sns.heatmap(df)\n    plt.show()\n\n    # agent.unsafe = False\n    #####################################\n    # Performance results and parameters as well as plots are stored in folder pipi_signleInv\n    # agent.history.df.to_csv('len_search/result.csv')\n    # if safe_results:\n    #   env.history.df.to_pickle('Simulation')\n\n    print(safe_vec)\n"
  },
  {
    "path": "experiments/model_validation/single_inverter_current_control_safe_opt_includingTB.py",
    "content": "#####################################\n# Experiment : Single inverter supplying current to an short circuit via a LR filter\n# Controller: PI current controller gain parameters are optimized by SafeOpt\n# a) FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters\n# b) connecting via ssh to a testbench to perform real-world measurement\n\n\nimport logging\nimport os\nfrom functools import partial\nfrom itertools import tee\n\nimport GPy\nimport gym\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfrom experiments.model_validation.env.physical_testbench import TestbenchEnv\nfrom experiments.model_validation.env.rewards import Reward\nfrom experiments.model_validation.env.stochastic_components import Load\nfrom experiments.model_validation.execution.monte_carlo_runner import MonteCarloRunner\nfrom experiments.model_validation.execution.runner_hardware import RunnerHardware\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat, MutableParams\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, MultiPhaseDQCurrentSourcingController\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom openmodelica_microgrid_gym.env.plotmanager import PlotManager\nfrom openmodelica_microgrid_gym.net import Network\nfrom openmodelica_microgrid_gym.util import FullHistory\n\n# Plotting params\nparams = {'backend': 'ps',\n          'text.latex.preamble': [r'\\usepackage{gensymb}'\n                                  r'\\usepackage{amsmath,amssymb,mathtools}'\n                                  r'\\newcommand{\\mlutil}{\\ensuremath{\\operatorname{ml-util}}}'\n                                  r'\\newcommand{\\mlacc}{\\ensuremath{\\operatorname{ml-acc}}}'],\n          'axes.labelsize': 8,  # fontsize for x and y labels (was 10)\n          'axes.titlesize': 8,\n          'font.size': 8,  # was 10\n          'legend.fontsize': 8,  # was 10\n          'xtick.labelsize': 8,\n          'ytick.labelsize': 8,\n          'text.usetex': True,\n          'figure.figsize': [3.9, 3.1],\n          'font.family': 'serif',\n          'lines.linewidth': 1\n          }\nmatplotlib.rcParams.update(params)\n\n# Choose which controller parameters should be adjusted by SafeOpt.\n# - Kp: 1D example: Only the proportional gain Kp of the PI controller is adjusted\n# - Ki: 1D example: Only the integral gain Ki of the PI controller is adjusted\n# - Kpi: 2D example: Kp and Ki are adjusted simultaneously\n\nadjust = 'Kpi'\n\n# Check if really only one simulation scenario was selected\nif adjust not in {'Kp', 'Ki', 'Kpi'}:\n    raise ValueError(\"Please set 'adjust' to one of the following values: 'Kp', 'Ki', 'Kpi'\")\n\ninclude_simulate = True\nshow_plots = True\nbalanced_load = True\ndo_measurement = False\nsave_results = False\n\n# Files saves results and  resulting plots to the folder saves_VI_control_safeopt in the current directory\ncurrent_directory = os.getcwd()\nsave_folder = os.path.join(current_directory, r'../1Test')\nos.makedirs(save_folder, exist_ok=True)\n\nnp.random.seed(1)\n\n# Simulation definitions\nnet = Network.load('../../net/net_single-inv-curr_Paper_SC.yaml')\ndelta_t = 1e-4  # simulation time step size / s\nundersample = 1  # undersampling of controller\nmax_episode_steps = 1000  # number of simulation steps per episode\nnum_episodes = 1  # number of simulation episodes (i.e. SafeOpt iterations)\nn_MC = 1  # number of Monte-Carlo samples for simulation - samples device parameters (e.g. L,R, noise) from\niLimit = 16  # inverter current limit / A\niNominal = 12  # nominal inverter current / A\nmu = 80  # factor for barrier function (see below)\ni_ref1 = np.array([10, 0, 0])  # exemplary set point i.e. id = 10, iq = 0, i0 = 0 / A\ni_ref2 = np.array([5, 0, 0])  # exemplary set point i.e. id = 15, iq = 0, i0 = 0 / A\n\n# plant\nL = 2.3e-3  # / H\nR = 400e-3  # / Ohm\n\nphase_shift = 5\namp_dev = 1.1\n\n\ndef pairwise(iterable):\n    \"s -> (s0,s1), (s1,s2), (s2, s3), ...\"\n    a, b = tee(iterable)\n    next(b, None)\n    return zip(a, b)\n\n\ndef cal_j_min(phase_shift, amp_dev):\n    \"\"\"\n    Calulated the miminum performance for safeopt\n    Best case error of all safe boundary scenarios is used (max) to indicate which typ of error tears\n    the safe boarder first (the weakest link in the chain)\n    \"\"\"\n\n    ph_list = [phase_shift, 0]\n    amp_list = [1, amp_dev]\n    return_j_min = np.empty(len(ph_list))\n    error_j_min = np.empty(3)\n    ph_shift = [0, 120, 240]\n    t = np.linspace(0, max_episode_steps * delta_t, max_episode_steps)\n\n    # risetime = 0.0015 -> 15 steps um auf 10A zu kommen: grade = 10/15\n    grad = 0.66  # 1e-1\n    irefs = [0, i_ref1[0], i_ref2[0]]\n    ts = [0, max_episode_steps // 2, max_episode_steps]\n\n    for q in range(len(ph_list)):\n        for p in range(3):\n            amplitude_sp = np.concatenate([np.full(t1 - t0, r1)\n                                           for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))])\n            amplitude = np.concatenate(\n                [np.minimum(\n                    r0 + grad * np.arange(0, t1 - t0),  # ramp up phase\n                    np.full(t1 - t0, r1)  # max amplitude\n                ) for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))])\n            mess = amp_list[q] * amplitude * np.cos(\n                2 * np.pi * 50 * t + (ph_list[q] * np.pi / 180) + (ph_shift[p] * np.pi / 180))\n            sp = amplitude_sp * np.cos(2 * np.pi * 50 * t + (ph_shift[p] * np.pi / 180))\n            error_j_min[p] = -np.sum((np.abs((sp - mess)) / iLimit) ** 0.5, axis=0) / max_episode_steps\n        return_j_min[q] = np.sum(error_j_min)  # Sum all 3 phases\n    return max(return_j_min)\n\n\nif __name__ == '__main__':\n\n    rew = Reward(i_limit=iLimit, i_nominal=iNominal, mu_c=mu, max_episode_steps=max_episode_steps,\n                 obs_dict=[[f'lc.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0']])\n\n    #####################################\n    # Definitions for the GP\n    prior_mean = 0  # 2  # mean factor of the GP prior mean which is multiplied with the first performance of the\n    # initial set\n    noise_var = 0.001  # 0.001 ** 2  # measurement noise sigma_omega\n    prior_var = 2  # prior variance of the GP\n\n    bounds = None\n    lengthscale = None\n    if adjust == 'Kp':\n        bounds = [(0.0001, 0.1)]  # bounds on the input variable Kp\n        lengthscale = [.025]  # length scale for the parameter variation [Kp] for the GP\n\n    # For 1D example, if Ki should be adjusted\n    if adjust == 'Ki':\n        bounds = [(0, 20)]  # bounds on the input variable Ki\n        lengthscale = [10]  # length scale for the parameter variation [Ki] for the GP\n\n    # For 2D example, choose Kp and Ki as mutable parameters (below) and define bounds and lengthscale for both of them\n    if adjust == 'Kpi':\n        bounds = [(0.001, 0.07), (2, 150)]\n        lengthscale = [0.012, 30.]\n\n    df_len = pd.DataFrame({'lengthscale': lengthscale,\n                           'bounds': bounds,\n                           'balanced_load': balanced_load,\n                           'barrier_param_mu': mu})\n\n    # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times\n    # the initial performance: safe_threshold = 0.8 means. Performance measurement for optimization are seen as\n    # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!)\n    # parameter set\n    safe_threshold = 0\n    j_min = cal_j_min(phase_shift, amp_dev)  # Used for normalization\n\n    # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop\n    # expanding points eventually.\n    # The following variable is multiplied with the first performance of the initial set by the factor below:\n    explore_threshold = 0\n\n    # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of\n    # limit exceeded\n    # has to be negative due to normalized performance (regarding J_init = 1)\n    abort_reward = 100 * j_min\n\n    # Definition of the kernel\n    kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True)\n\n    #####################################\n    # Definition of the controllers\n    mutable_params = None\n    current_dqp_iparams = None\n    if adjust == 'Kp':\n        # mutable_params = parameter (Kp gain of the current controller of the inverter) to be optimized using\n        # the SafeOpt algorithm\n        mutable_params = dict(currentP=MutableFloat(0.04))\n\n        # Define the PI parameters for the current controller of the inverter\n        current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=12, limits=(-1, 1))\n\n    # For 1D example, if Ki should be adjusted\n    elif adjust == 'Ki':\n        mutable_params = dict(currentI=MutableFloat(5))\n        current_dqp_iparams = PI_params(kP=0.005, kI=mutable_params['currentI'], limits=(-1, 1))\n\n    # For 2D example, choose Kp and Ki as mutable parameters\n    elif adjust == 'Kpi':\n        mutable_params = dict(currentP=MutableFloat(0.04), currentI=MutableFloat(11.8))\n        current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'],\n                                        limits=(-1, 1))\n\n    # Define a current sourcing inverter as master inverter using the pi and droop parameters from above\n    ctrl = MultiPhaseDQCurrentSourcingController(current_dqp_iparams, ts_sim=delta_t,\n                                                 ts_ctrl=undersample * delta_t, name='master', f_nom=net.freq_nom)\n\n    i_ref = MutableParams([MutableFloat(f) for f in i_ref1])\n    #####################################\n    # Definition of the optimization agent\n    # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example\n    # Arguments described above\n    # History is used to store results\n    agent = SafeOptAgent(mutable_params,\n                         abort_reward,\n                         j_min,\n                         kernel,\n                         dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold,\n                              explore_threshold=explore_threshold), [ctrl],\n                         dict(master=[[f'lc.inductor{k}.i' for k in '123'], i_ref]), history=FullHistory()\n                         )\n\n    #####################################\n    # Definition of the environment using a FMU created by OpenModelica\n    # (https://www.openmodelica.org/)\n    # Using an inverter supplying a load\n    # - using the reward function described above as callable in the env\n    # - viz_cols used to choose which measurement values should be displayed (here, only the 3 currents across the\n    #   inductors of the inverters are plotted. Labels and grid is adjusted using the PlotTmpl (For more information,\n    #   see UserGuide)\n    # - inputs to the models are the connection points to the inverters (see user guide for more details)\n    # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors\n\n    if include_simulate:\n\n        # Defining unbalanced loads sampling from Gaussian distribution with sdt = 0.2*mean\n        r_load = Load(R, 0.1 * R, balanced=balanced_load, tolerance=0.1)\n        l_load = Load(L, 0.1 * L, balanced=balanced_load, tolerance=0.1)\n\n\n        # i_noise = Noise([0, 0, 0], [0.0023, 0.0015, 0.0018], 0.0005, 0.32)\n\n        # if no noise should be included:\n        # r_load = Load(R, 0 * R, balanced=balanced_load)\n        # l_load = Load(L, 0 * L, balanced=balanced_load)\n\n        # i_noise = Noise([0, 0, 0], [0.0, 0.0, 0.0], 0.0, 0.0)\n\n        def reset_loads():\n            r_load.reset()\n            l_load.reset()\n\n\n        plotter = PlotManager(agent, save_results=save_results, save_folder=save_folder,\n                              show_plots=show_plots)\n\n\n        def reference_step(t):\n\n            if t >= .05:\n                i_ref[:] = i_ref2\n            else:\n                i_ref[:] = i_ref1\n\n            return partial(l_load.give_value, n=2)(t)\n\n\n        env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                       # reward_fun=Reward().rew_fun,\n                       reward_fun=rew.rew_fun_c,\n                       # time_step=delta_t,\n                       viz_cols=[\n                           PlotTmpl([[f'lc.inductor{i}.i' for i in '123'], [f'master.SPI{i}' for i in 'abc']],\n                                    callback=plotter.xylables_i_abc,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    ),\n                           PlotTmpl([[f'master.m{i}' for i in 'abc']],\n                                    callback=lambda fig: plotter.update_axes(fig, title='Simulation',\n                                                                             ylabel='$m_{\\mathrm{abc}}\\,/\\,\\mathrm{}$')\n\n                                    ),\n                           PlotTmpl([[f'master.CVI{i}' for i in 'dq0'], [f'master.SPI{i}' for i in 'dq0']],\n                                    callback=plotter.xylables_i_dq0,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    )\n                       ],\n                       log_level=logging.INFO,\n                       viz_mode='episode',\n                       max_episode_steps=max_episode_steps,\n                       model_params={'lc.resistor1.R': partial(r_load.give_value, n=0),\n                                     'lc.resistor2.R': partial(r_load.give_value, n=1),\n                                     'lc.resistor3.R': partial(r_load.give_value, n=2),\n                                     'lc.inductor1.L': partial(l_load.give_value, n=0),\n                                     'lc.inductor2.L': partial(l_load.give_value, n=1),\n                                     'lc.inductor3.L': reference_step},\n                       model_path='../../omg_grid/grid.paper.fmu',\n                       # model_path='../omg_grid/omg_grid.Grids.Paper_SC.fmu',\n                       net=net,\n                       history=FullHistory(),\n                       action_time_delay=1 * undersample\n                       )\n\n        runner = MonteCarloRunner(agent, env)\n\n        runner.run(num_episodes, n_mc=n_MC, visualise=True, prepare_mc_experiment=reset_loads)\n\n        #####################################\n        # Results\n        best_agent_plt = runner.run_data['last_agent_plt']\n        ax = best_agent_plt.axes[0]\n        ax.grid(which='both')\n        ax.set_axisbelow(True)\n\n        if adjust == 'Ki':\n            ax.set_xlabel(r'$K_\\mathrm{i}\\,/\\,\\mathrm{(VA^{-1}s^{-1})}$')\n            ax.set_ylabel(r'$J$')\n            ax.set_ylim([-0.5, 1.5])\n        elif adjust == 'Kp':\n            ax.set_xlabel(r'$K_\\mathrm{p}\\,/\\,\\mathrm{(VA^{-1})}$')\n            ax.set_ylabel(r'$J$')\n\n        elif adjust == 'Kpi':\n            agent.params.reset()\n            ax.set_ylabel(r'$K_\\mathrm{i}\\,/\\,\\mathrm{(VA^{-1}s^{-1})}$')\n            ax.set_xlabel(r'$K_\\mathrm{p}\\,/\\,\\mathrm{(VA^{-1})}$')\n            ax.get_figure().axes[1].set_ylabel(r'$J$')\n            plt.title('Lengthscale = {}; balanced = '.format(lengthscale, balanced_load))\n            # ax.plot([mutable_params['currentP'].val, mutable_params['currentP'].val], bounds[1], 'k-', zorder=1,\n            #        lw=4,\n            #        alpha=.5)\n        best_agent_plt.show()\n\n        if save_results:\n            best_agent_plt.savefig(save_folder + '/_agent_plt.pdf')\n            best_agent_plt.savefig(save_folder + '/_agent_plt.pgf')\n            agent.history.df.to_csv(save_folder + '/_result.csv')\n            df_len.to_csv(save_folder + '/_params.csv')\n\n        print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df[:]))\n        print('\\n Experiment finished with best set: \\n')\n        print('\\n  {} = {}'.format(adjust, agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n        print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n        print('\\n\\nBest experiment results are plotted in the following:')\n        print(agent.unsafe)\n\n    if do_measurement:\n        #####################################\n        # Execution of the experiment\n        # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations)\n\n        env = TestbenchEnv(num_steps=max_episode_steps, DT=1 / 10000, ref=10, ref2=5,\n                           i_limit=iLimit, i_nominal=iNominal, f_nom=60, mu=mu)\n        runner = RunnerHardware(agent, env)\n\n        runner.run(num_episodes, visualise=True, save_folder=save_folder)\n\n        print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df[:]))\n        print('\\n Experiment finished with best set: \\n')\n        print('\\n  {} = {}'.format(adjust, agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n        print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n        print('\\n\\nBest experiment results are plotted in the following:')\n\n        if save_results:\n            agent.history.df.to_csv(save_folder + '/_meas_result.csv')\n            df_len.to_csv(save_folder + '/_meas_params.csv')\n\n        # Show last performance plot\n        best_agent_plt = runner.run_data['last_agent_plt']\n        ax = best_agent_plt.axes[0]\n        ax.grid(which='both')\n        ax.set_axisbelow(True)\n\n        if adjust == 'Ki':\n            ax.set_xlabel(r'$K_\\mathrm{i}\\,/\\,\\mathrm{(VA^{-1}s^{-1})}$')\n            ax.set_ylabel(r'$J$')\n        elif adjust == 'Kp':\n            ax.set_xlabel(r'$K_\\mathrm{p}\\,/\\,\\mathrm{(VA^{-1})}$')\n            ax.set_ylabel(r'$J$')\n        elif adjust == 'Kpi':\n            agent.params.reset()\n            ax.set_ylabel(r'$K_\\mathrm{i}\\,/\\,\\mathrm{(VA^{-1}s^{-1})}$')\n            ax.set_xlabel(r'$K_\\mathrm{p}\\,/\\,\\mathrm{(VA^{-1})}$')\n            ax.get_figure().axes[1].set_ylabel(r'$J$')\n            # plt.plot(bounds[0], [mutable_params['currentP'].val, mutable_params['currentP'].val], 'k-', zorder=1,\n            #         lw=4,\n            #         alpha=.5)\n        best_agent_plt.show()\n        if save_results:\n            best_agent_plt.savefig(save_folder + '/_meas_agent_plt.pgf')\n            best_agent_plt.savefig(save_folder + '/_meas_agent_plt.pdf')\n"
  },
  {
    "path": "experiments/model_validation/single_inverter_voltage_current_control_safe_opt_includingTB.py",
    "content": "#####################################\n# Experiment : Single voltage forming inverter supplying an RL-load via an LC-filter\n# Controller: Cascaded PI-PI voltage and current controller gain parameters are optimized by SafeOpt\n# a) FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters\n# b) connecting via ssh to a testbench to perform real-world measurement\n\nimport logging\nimport os\nfrom functools import partial\nfrom itertools import tee\n\nimport GPy\nimport gym\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfrom experiments.model_validation.env.testbench_voltage_ctrl import TestbenchEnvVoltage\nfrom experiments.model_validation.execution.monte_carlo_runner import MonteCarloRunner\nfrom experiments.model_validation.execution.runner_hardware import RunnerHardwareGradient\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, \\\n    MultiPhaseDQ0PIPIController\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom openmodelica_microgrid_gym.env.plotmanager import PlotManager\nfrom experiments.model_validation.env.rewards import Reward\nfrom experiments.model_validation.env.stochastic_components import Load\nfrom openmodelica_microgrid_gym.net import Network\nfrom openmodelica_microgrid_gym.util import FullHistory\n\n# Plot setting\nparams = {'backend': 'ps',\n          'text.latex.preamble': [r'\\usepackage{gensymb}'\n                                  r'\\usepackage{amsmath,amssymb,mathtools}'\n                                  r'\\newcommand{\\mlutil}{\\ensuremath{\\operatorname{ml-util}}}'\n                                  r'\\newcommand{\\mlacc}{\\ensuremath{\\operatorname{ml-acc}}}'],\n          'axes.labelsize': 8,  # fontsize for x and y labels (was 10)\n          'axes.titlesize': 8,\n          'font.size': 8,  # was 10\n          'legend.fontsize': 8,  # was 10\n          'xtick.labelsize': 8,\n          'ytick.labelsize': 8,\n          'text.usetex': True,\n          'figure.figsize': [3.9, 3.1],\n          'font.family': 'serif',\n          'lines.linewidth': 1\n          }\nmatplotlib.rcParams.update(params)\n\ninclude_simulate = True\nshow_plots = True\nbalanced_load = False\ndo_measurement = False\nsave_results = False\n\n# Files saves results and  resulting plots to the folder saves_VI_control_safeopt in the current directory\ncurrent_directory = os.getcwd()\nsave_folder = os.path.join(current_directory, r'VSim_rebase2_MC3')\nos.makedirs(save_folder, exist_ok=True)\n\nnp.random.seed(1)\n\n# Simulation definitions\nnet = Network.load('../../net/net_single-inv-Paper_Loadstep.yaml')\ndelta_t = 1e-4  # simulation time step size / s\nundersample = 1\nmax_episode_steps = 2000  # number of simulation steps per episode\nnum_episodes = 1  # number of simulation episodes (i.e. SafeOpt iterations)\nn_MC = 1  # number of Monte-Carlo samples for simulation - samples device parameters (e.g. L,R, noise) from\nv_DC = 600  # DC-link voltage / V; will be set as model parameter in the FMU\nnomFreq = 60  # nominal grid frequency / Hz\nnomVoltPeak = 169.7  # 230 * 1.414  # nominal grid voltage / V\niLimit = 16  # inverter current limit / A\niNominal = 12  # nominal inverter current / A\nvNominal = 190  # nominal inverter current / A\nvLimit = vNominal * 1.5  # inverter current limit / A\nfunnelFactor = 0.02\nvFunnel = np.array([vNominal * funnelFactor, vNominal * funnelFactor, vNominal * funnelFactor])\nmu = 400  # factor for barrier function (see below)\nDroopGain = 0.0  # virtual droop gain for active power / W/Hz\nQDroopGain = 0.0  # virtual droop gain for reactive power / VAR/V\n\n# plant\nL_filter = 2.3e-3  # / H\nR_filter = 400e-3  # / Ohm\nC_filter = 10e-6  # / F\nR = 28  # nomVoltPeak / 7.5   # / Ohm\n\nphase_shift = 5\namp_dev = 1.1\n\n# Observer matrices\nA = np.array([[-R_filter, -1 / L_filter, 0],\n              [1 / C_filter, 0, -1 / C_filter],\n              [0, 0, 0]])\n\nB = np.array([[1 / L_filter, 0, 0]]).T\n\nC = np.array([[1, 0, 0],\n              [0, 1, 0]])\n\n# Observer values\nL_iL_iL = 2e3\nL_vc_iL = -435  # influence from delta_y_vc onto xdot = iL\nL_iL_vc = 100229\nL_vc_vc = 4000\nL_iL_io = -13.22\nL_vc_io = -80\n\nL = np.array([[L_iL_iL, L_vc_iL],\n              [L_iL_vc, L_vc_vc],\n              [L_iL_io, L_vc_io]])\n\n\ndef pairwise(iterable):\n    \"s -> (s0,s1), (s1,s2), (s2, s3), ...\"\n    a, b = tee(iterable)\n    next(b, None)\n    return zip(a, b)\n\n\ndef cal_J_min(phase_shift, amp_dev):\n    \"\"\"\n    Calulated the miminum performance for safeopt\n    Best case error of all safe boundary scenarios is used (max) to indicate which typ of error tears\n    the safe boarder first (the weakest link in the chain)\n    \"\"\"\n\n    ph_list = [phase_shift, 0]\n    amp_list = [1, amp_dev]\n    return_Jmin = np.empty(len(ph_list))\n    error_Jmin = np.empty(3)\n    ph_shift = [0, 120, 240]\n    t = np.linspace(0, max_episode_steps * delta_t, max_episode_steps)\n\n    grad = 0.3\n    irefs = [0, nomVoltPeak, nomVoltPeak]\n    ts = [0, max_episode_steps // 2, max_episode_steps]\n\n    noiseH = 0.02 * np.sin(2 * np.pi * 1500 * t)  # add noise for dev return\n\n    for l in range(len(ph_list)):\n        for p in range(3):\n            amplitudeSP = np.concatenate([np.full(t1 - t0, r1)\n                                          for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))])\n            amplitude = np.concatenate(\n                [np.minimum(\n                    r0 + grad * np.arange(0, t1 - t0),  # ramp up phase\n                    np.full(t1 - t0, r1)  # max amplitude\n                ) for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))])\n            Mess = noiseH * amplitude + amp_list[l] * amplitude * np.cos(\n                2 * np.pi * 60 * t + (ph_list[l] * np.pi / 180) + (ph_shift[p] * np.pi / 180))\n            SP = amplitudeSP * np.cos(2 * np.pi * 60 * t + (ph_shift[p] * np.pi / 180))\n            error_Jmin[p] = -np.sum((np.abs((SP - Mess)) / vLimit) ** 0.5, axis=0) / max_episode_steps\n            w2 = noiseH * amplitude + amplitude\n            dw2 = np.gradient(w2)\n            SP_sattle = (amplitude > amplitudeSP * (1 - 0.12)).astype(int)\n            error2 = -np.mean(abs(SP_sattle * dw2))\n            error_Jmin[p] += error2 * .5  # add gradient error\n        return_Jmin[l] = np.sum(error_Jmin)  # Sum all 3 phases\n\n    return max(return_Jmin)\n\n\nif __name__ == '__main__':\n\n    rew = Reward(i_limit=iLimit, i_nominal=iNominal, mu_v=mu, max_episode_steps=max_episode_steps,\n                 v_limit=vLimit, v_nominal=vNominal,\n                 obs_dict=[[f'lc.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'],\n                           [f'lc.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0'],\n                           [f'master.CVV{k}' for k in 'dq0']])\n\n    #####################################\n    # Definitions for the GP\n    prior_mean = 0  # 2  # mean factor of the GP prior mean which is multiplied with the first performance of the initial set\n    noise_var = 0.001  # ** 2  # measurement noise sigma_omega\n    prior_var = 2  # prior variance of the GP\n\n    # Choose Kp and Ki (current and voltage controller) as mutable parameters (below) and define bounds and lengthscale\n    # for both of them\n    bounds = [(0.000, 0.045), (4, 450)]  # bounds on the input variable current-Ki&Kp and voltage-Ki&Kp\n    lengthscale = [.003, 50.]  # length scale for the parameter variation [current-Ki&Kp and voltage-Ki&Kp] for the GP\n\n    # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times\n    # the initial performance: safe_threshold = 1.2 means: performance measurement for optimization are seen as\n    # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!)\n    # parameter set\n    safe_threshold = 0\n    j_min = cal_J_min(phase_shift, amp_dev)  # cal min allowed performance\n\n    # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop\n    # expanding points eventually.\n    # The following variable is multiplied with the first performance of the initial set by the factor below:\n    explore_threshold = 0\n\n    # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of\n    # limit exceeded\n    abort_reward = 100 * j_min\n\n    # Definition of the kernel\n    kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True)\n\n    #####################################\n    # Definition of the controllers\n    # Choose Kp and Ki for the current and voltage controller as mutable parameters\n    mutable_params = dict(voltageP=MutableFloat(0.0175), voltageI=MutableFloat(12))  # 300Hz\n    voltage_dqp_iparams = PI_params(kP=mutable_params['voltageP'], kI=mutable_params['voltageI'],\n                                    limits=(-iLimit, iLimit))\n\n    kp_c = 0.04\n    ki_c = 11.8\n    current_dqp_iparams = PI_params(kP=kp_c, kI=ki_c, limits=(-1, 1))  # Current controller values\n\n    # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for the\n    # filter and the nominal frequency\n    # Droop controller used to calculate the virtual frequency drop due to load changes\n    droop_param = DroopParams(DroopGain, 0.005, net.freq_nom)\n\n    # Define the Q-droop parameters for the inverter of the reactive power VAR/Volt, delta_t (0.002) used for the\n    # filter and the nominal voltage\n    qdroop_param = DroopParams(QDroopGain, 0.002, net.v_nom)\n\n    # Define a voltage forming inverter using the PIPI and droop parameters from above\n\n    # Controller with observer\n    # ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, delta_t, droop_param, qdroop_param,\n    #                                   observer=[Lueneberger(*params) for params in\n    #                                             repeat((A, B, C, L, delta_t * undersample, v_DC / 2), 3)], undersampling=undersample,\n    #                                   name='master')\n\n    # Controller without observer\n    ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param,\n                                       ts_sim=delta_t,\n                                       ts_ctrl=undersample * delta_t,\n                                       name='master')\n\n    #####################################\n    # Definition of the optimization agent\n    # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example\n    # Arguments described above\n    # History is used to store results\n    agent = SafeOptAgent(mutable_params,\n                         abort_reward,\n                         j_min,\n                         kernel,\n                         dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean,\n                              safe_threshold=safe_threshold, explore_threshold=explore_threshold),\n                         [ctrl],\n                         dict(master=[[f'lc.inductor{k}.i' for k in '123'],\n                                      [f'lc.capacitor{k}.v' for k in '123']\n                                      ]),\n                         history=FullHistory(),\n                         )\n\n    if include_simulate:\n        #####################################\n        # Definition of the environment using a FMU created by OpenModelica\n        # (https://www.openmodelica.org/)\n        # Using an inverter supplying a load\n        # - using the reward function described above as callable in the env\n        # - viz_cols used to choose which measurement values should be displayed.\n        #   Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide)\n        #   generated figures are stored to file\n        # - inputs to the models are the connection points to the inverters (see user guide for more details)\n        # - model outputs are the 3 currents through the inductors and the 3 voltages across the capacitors\n\n        # If without noise:\n        # r_filt = Load(R_filt, 0 * R_filt, balanced=balanced_load)\n        # l_filt = Load(L_filt, 0 * L_filt, balanced=balanced_load)\n        # c_filt = Load(C_filt, 0 * C_filt, balanced=balanced_load)\n        # r_load = Load(R, 0 * R, balanced=balanced_load)\n        # meas_noise = Noise([0, 0, 0, 0, 0, 0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 0.0, 0.0)\n\n        r_filt = Load(R_filter, 0.1 * R_filter, balanced=balanced_load)\n        l_filt = Load(L_filter, 0.1 * L_filter, balanced=balanced_load)\n        c_filt = Load(C_filter, 0.1 * C_filter, balanced=balanced_load)\n        r_load = Load(R, 0.1 * R, balanced=balanced_load)\n\n\n        # meas_noise = Noise([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n        #                   [0.45, 0.39, 0.42, 0.0023, 0.0015, 0.0018, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0.0, 0.5)\n\n        def reset_loads():\n            r_load.reset()\n            r_filt.reset()\n            l_filt.reset()\n            c_filt.reset()\n\n\n        plotter = PlotManager(agent, save_results=save_results, save_folder=save_folder,\n                              show_plots=show_plots)\n\n        env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                       reward_fun=rew.rew_fun_v,\n                       viz_cols=[\n                           PlotTmpl([[f'lc.capacitor{i}.v' for i in '123'], [f'master.SPV{i}' for i in 'abc']],\n                                    callback=plotter.xylables_v_abc,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    ),\n                           PlotTmpl([[f'master.CVV{i}' for i in 'dq0'], [f'master.SPV{i}' for i in 'dq0']],\n                                    callback=plotter.xylables_v_dq0,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    ),\n                           PlotTmpl([[f'lc.inductor{i}.i' for i in '123'], [f'master.SPI{i}' for i in 'abc']],\n                                    callback=plotter.xylables_i_abc,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    ),\n                           # PlotTmpl([[f'master.I_hat{i}' for i in 'abc'], [f'r_load.resistor{i}.i' for i in '123'], ],\n                           #         callback=lambda fig: plotter.update_axes(fig, title='Simulation',\n                           #                                                  ylabel='$i_{\\mathrm{o estimate,abc}}\\,/\\,\\mathrm{A}$'),\n                           #         color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                           #         style=[['-*'], ['--*']]\n                           #         ),\n                           # PlotTmpl([[f'master.m{i}' for i in 'dq0']],\n                           #         callback=lambda fig: plotter.update_axes(fig, title='Simulation',\n                           #                                                  ylabel='$m_{\\mathrm{dq0}}\\,/\\,\\mathrm{}$',\n                           #                                                  filename='Sim_m_dq0')\n                           #         ),\n                           PlotTmpl([[f'master.CVi{i}' for i in 'dq0'], [f'master.SPI{i}' for i in 'dq0']],\n                                    callback=plotter.xylables_i_dq0,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    )\n                       ],\n                       log_level=logging.INFO,\n                       viz_mode='episode',\n                       max_episode_steps=max_episode_steps,\n                       model_params={'lc.resistor1.R': partial(r_filt.give_value, n=0),\n                                     'lc.resistor2.R': partial(r_filt.give_value, n=1),\n                                     'lc.resistor2.R': partial(r_filt.give_value, n=1),\n                                     'lc.resistor3.R': partial(r_filt.give_value, n=2),\n                                     'lc.resistor4.R': 0.0000001,\n                                     'lc.resistor5.R': 0.0000001,\n                                     'lc.resistor6.R': 0.0000001,\n                                     'lc.inductor1.L': partial(l_filt.give_value, n=0),\n                                     'lc.inductor2.L': partial(l_filt.give_value, n=1),\n                                     'lc.inductor3.L': partial(l_filt.give_value, n=2),\n                                     'lc.capacitor1.C': partial(c_filt.give_value, n=0),\n                                     'lc.capacitor2.C': partial(c_filt.give_value, n=1),\n                                     'lc.capacitor3.C': partial(c_filt.give_value, n=2),\n                                     'r_load.resistor1.R': partial(r_load.load_step, n=0),\n                                     'r_load.resistor2.R': partial(r_load.load_step, n=1),\n                                     'r_load.resistor3.R': partial(r_load.load_step, n=2),\n                                     },\n                       net=net,\n                       model_path='../../omg_grid/grid.paper_loadstep.fmu',\n                       history=FullHistory(),\n                       action_time_delay=1 * undersample\n                       )\n\n        runner = MonteCarloRunner(agent, env)\n\n        runner.run(num_episodes, n_mc=n_MC, visualise=True, prepare_mc_experiment=reset_loads,\n                   return_gradient_extend=True)\n\n        df_len = pd.DataFrame({'lengthscale': lengthscale,\n                               'bounds': bounds,\n                               'balanced_load': balanced_load,\n                               'barrier_param_mu': mu,\n                               'J_min': j_min})\n\n        if save_results:\n            agent.history.df.to_csv(save_folder + '/_result.csv')\n            df_len.to_csv(save_folder + '/_params.csv')\n\n        best_agent_plt = runner.run_data['last_agent_plt']\n        ax = best_agent_plt.axes[0]\n        ax.grid(which='both')\n        ax.set_axisbelow(True)\n\n        agent.params.reset()\n        ax.set_ylabel(r'$K_\\mathrm{i}\\,/\\,\\mathrm{(AV^{-1}s^{-1})}$')\n        ax.set_xlabel(r'$K_\\mathrm{p}\\,/\\,\\mathrm{(AV^{-1})}$')\n        ax.get_figure().axes[1].set_ylabel(r'$J$')\n        plt.title('Lengthscale = {}; balanced = '.format(lengthscale, balanced_load))\n        # ax.plot([0.01, 0.01], [0, 250], 'k')\n        # ax.plot([mutable_params['currentP'].val, mutable_params['currentP'].val], bounds[1], 'k-', zorder=1,\n        #         lw=4,\n        #         alpha=.5)\n        best_agent_plt.show()\n        if save_results:\n            best_agent_plt.savefig(save_folder + '/_agent_plt.pdf')\n            best_agent_plt.savefig(save_folder + '/_agent_plt.pgf')\n            agent.history.df.to_csv(save_folder + '/_result.csv')\n\n        print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4})))\n        print('\\n Experiment finished with best set: \\n')\n        print('\\n  Current-Ki&Kp and voltage-Ki&Kp = {}'.format(\n            agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n        print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n        print('\\n\\nBest experiment results are plotted in the following:')\n\n    if do_measurement:\n        #####################################\n        # Execution of the experiment\n        # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations)\n\n        env = TestbenchEnvVoltage(num_steps=max_episode_steps, DT=1 / 10000, v_nominal=nomVoltPeak,\n                                  kP=kp_c, kI=ki_c, v_limit=vLimit, f_nom=nomFreq, mu=mu)\n        # runner = RunnerHardware(agent, env)\n        runner = RunnerHardwareGradient(agent, env)\n\n        runner.run(num_episodes, visualise=True, save_folder=save_folder)\n\n        print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df[:]))\n\n        print('\\n Experiment finished with best set: \\n')\n        print('\\n  Current-Kp&Ki and voltage-Kp&Ki = {}'.format(\n            agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n        print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n        print('\\n\\nBest experiment results are plotted in the following:')\n\n        df_len = pd.DataFrame({'lengthscale': lengthscale,\n                               'bounds': bounds,\n                               'balanced_load': balanced_load,\n                               'barrier_param_mu': mu,\n                               'J_min': j_min})\n\n        if save_results:\n            agent.history.df.to_csv(save_folder + '/_meas_result.csv')\n            df_len.to_csv(save_folder + '/_meas_params.csv')\n\n        # Show last performance plot\n        best_agent_plt = runner.run_data['last_agent_plt']\n        ax = best_agent_plt.axes[0]\n        ax.grid(which='both')\n        ax.set_axisbelow(True)\n\n        agent.params.reset()\n        ax.set_ylabel(r'$K_\\mathrm{i}\\,/\\,\\mathrm{(AV^{-1}s^{-1})}$')\n        ax.set_xlabel(r'$K_\\mathrm{p}\\,/\\,\\mathrm{(AV^{-1})}$')\n        ax.get_figure().axes[1].set_ylabel(r'$J$')\n        # plt.plot(bounds[0], [mutable_params['currentP'].val, mutable_params['currentP'].val], 'k-', zorder=1,\n        #         lw=4,\n        #         alpha=.5)\n        best_agent_plt.show()\n        if save_results:\n            best_agent_plt.savefig(save_folder + '/_meas_agent_plt.png')\n            best_agent_plt.savefig(save_folder + '/_meas_agent_plt.pdf')\n            best_agent_plt.savefig(save_folder + '/_meas_agent_plt.pgf')\n"
  },
  {
    "path": "experiments/model_validation/single_inverter_voltage_current_control_safe_opt_includingTB_4D.py",
    "content": "#####################################\n# Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters\n# Simulation setup: Single voltage forming inverter supplying an RL-load via an LC-filter\n# Controller: Cascaded PI-PI voltage and current controller gain parameters are optimized by SafeOpt\n\n\nimport logging\nimport os\nfrom functools import partial\n\nimport GPy\nimport gym\nimport matplotlib\nimport numpy as np\nimport pandas as pd\n\nfrom openmodelica_microgrid_gym.env.plotmanager import PlotManager\nfrom experiments.model_validation.env.rewards import Reward\n\nparams = {'backend': 'ps',\n          'text.latex.preamble': [r'\\usepackage{gensymb}'\n                                  r'\\usepackage{amsmath,amssymb,mathtools}'\n                                  r'\\newcommand{\\mlutil}{\\ensuremath{\\operatorname{ml-util}}}'\n                                  r'\\newcommand{\\mlacc}{\\ensuremath{\\operatorname{ml-acc}}}'],\n          'axes.labelsize': 8,  # fontsize for x and y labels (was 10)\n          'axes.titlesize': 8,\n          'font.size': 8,  # was 10\n          'legend.fontsize': 8,  # was 10\n          'xtick.labelsize': 8,\n          'ytick.labelsize': 8,\n          'text.usetex': True,\n          'figure.figsize': [3.9, 3.1],\n          'font.family': 'serif',\n          'lines.linewidth': 1\n          }\nmatplotlib.rcParams.update(params)\n\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, MultiPhaseDQ0PIPIController\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom experiments.model_validation.env.stochastic_components import Load\nfrom experiments.model_validation.env.testbench_voltage_ctrl import TestbenchEnvVoltage\nfrom experiments.model_validation.execution.monte_carlo_runner import MonteCarloRunner\nfrom experiments.model_validation.execution.runner_hardware import RunnerHardware, RunnerHardwareGradient\nfrom openmodelica_microgrid_gym.net import Network\nfrom openmodelica_microgrid_gym.util import FullHistory\n\ninclude_simulate = True\nshow_plots = False\nbalanced_load = False\ndo_measurement = False\nsave_results = True\n\ncurrent_directory = os.getcwd()\nsave_folder = os.path.join(current_directory, r'4Dtest1')\nos.makedirs(save_folder, exist_ok=True)\n\nnp.random.seed(1)\n\n# Simulation definitions\nnet = Network.load('../../net/net_single-inv-Paper_Loadstep.yaml')\ndelta_t = 1e-4  # simulation time step size / s\nundersample = 1\nmax_episode_steps = 2000  # number of simulation steps per episode\nnum_episodes = 60  # number of simulation episodes (i.e. SafeOpt iterations)\nn_MC = 10  # number of Monte-Carlo samples for simulation - samples device parameters (e.g. L,R, noise) from\nv_DC = 600  # DC-link voltage / V; will be set as model parameter in the FMU\nnomFreq = 60  # nominal grid frequency / Hz\nnomVoltPeak = 169.7  # 230 * 1.414  # nominal grid voltage / V\niLimit = 16  # inverter current limit / A\niNominal = 12  # nominal inverter current / A\nvNominal = 190  # nominal inverter current / A\nvLimit = vNominal * 1.5  # inverter current limit / A\nfunnelFactor = 0.02\nvFunnel = np.array([vNominal * funnelFactor, vNominal * funnelFactor, vNominal * funnelFactor])\nmu = 400  # factor for barrier function (see below)\nmu_cc = 80\nDroopGain = 0.0  # virtual droop gain for active power / W/Hz\nQDroopGain = 0.0  # virtual droop gain for reactive power / VAR/V\n\n# plant\nL_filter = 2.3e-3  # / H\nR_filter = 400e-3  # / Ohm\nC_filter = 10e-6  # / F\nR = 28  # nomVoltPeak / 7.5   # / Ohm\n\nphase_shift = 5\namp_dev = 1.1\n\n# Observer matrices\nA = np.array([[-R_filter, -1 / L_filter, 0],\n              [1 / C_filter, 0, -1 / C_filter],\n              [0, 0, 0]])\n\nB = np.array([[1 / L_filter, 0, 0]]).T\n\nC = np.array([[1, 0, 0],\n              [0, 1, 0]])\n\n# Observer values\nL_iL_iL = 2e3\nL_vc_iL = -435  # influence from delta_y_vc onto xdot = iL\nL_iL_vc = 100229\nL_vc_vc = 4000\nL_iL_io = -13.22\nL_vc_io = -80\n\nL = np.array([[L_iL_iL, L_vc_iL],\n              [L_iL_vc, L_vc_vc],\n              [L_iL_io, L_vc_io]])\n\nif __name__ == '__main__':\n\n    rew = Reward(i_limit=iLimit, i_nominal=iNominal, mu_c=mu_cc, mu_v=mu, max_episode_steps=max_episode_steps,\n                 v_limit=vLimit, v_nominal=vNominal,\n                 obs_dict=[[f'lc.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'],\n                           [f'lc.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0'],\n                           [f'master.CVV{k}' for k in 'dq0']])\n\n    #####################################\n    # Definitions for the GP\n    prior_mean = 0  # 2  # mean factor of the GP prior mean which is multiplied with the first performance of the initial set\n    noise_var = 0.001  # ** 2  # measurement noise sigma_omega\n    prior_var = 2  # prior variance of the GP\n\n    # Choose Kp and Ki (current and voltage controller) as mutable parameters (below) and define bounds and lengthscale\n    # for both of them\n    bounds = [(0.001, 0.07), (2, 150), (0.000, 0.045),\n              (4, 450)]  # bounds on the input variable current-Ki&Kp and voltage-Ki&Kp\n    lengthscale = [0.005, 25., .003,\n                   50.]  # length scale for the parameter variation [current-Ki&Kp and voltage-Ki&Kp] for the GP\n\n    # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times\n    # the initial performance: safe_threshold = 1.2 means: performance measurement for optimization are seen as\n    # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!)\n    # parameter set\n    safe_threshold = 0\n    j_min = -10\n\n    # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop\n    # expanding points eventually.\n    # The following variable is multiplied with the first performance of the initial set by the factor below:\n    explore_threshold = 0\n\n    # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of\n    # limit exceeded\n    abort_reward = 100 * j_min\n\n    # Definition of the kernel\n    kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True)\n\n    #####################################\n    # Definition of the controllers\n    # Choose Kp and Ki for the current and voltage controller as mutable parameters\n    # mutable_params = dict(currentP=MutableFloat(10e-3), currentI=MutableFloat(10), voltageP=MutableFloat(0.05),\n    #                      voltageI=MutableFloat(75))\n\n    # Vdc = 600 V\n\n    mutable_params = dict(currentP=MutableFloat(0.04), currentI=MutableFloat(11.8),\n                          voltageP=MutableFloat(0.0175), voltageI=MutableFloat(12))\n\n    voltage_dqp_iparams = PI_params(kP=mutable_params['voltageP'], kI=mutable_params['voltageI'],\n                                    limits=(-iLimit, iLimit))\n    current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'],\n                                    limits=(-1, 1))  # Best set from paper III-D\n\n    # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for the\n    # filter and the nominal frequency\n    # Droop controller used to calculate the virtual frequency drop due to load changes\n    droop_param = DroopParams(DroopGain, 0.005, nomFreq)\n\n    # Define the Q-droop parameters for the inverter of the reactive power VAR/Volt, delta_t (0.002) used for the\n    # filter and the nominal voltage\n    qdroop_param = DroopParams(QDroopGain, 0.002, nomVoltPeak)\n\n    # Define a voltage forming inverter using the PIPI and droop parameters from above\n    # Controller with observer\n    # ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, delta_t, droop_param, qdroop_param,\n    #                                   observer=[Lueneberger(*params) for params in\n    #                                             repeat((A, B, C, L, delta_t * undersample, v_DC / 2), 3)], undersampling=undersample,\n    #                                   name='master')\n\n    # Controller without observer\n    ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, delta_t, droop_param, qdroop_param,\n                                       undersampling=undersample,\n                                       name='master')\n\n    #####################################\n    # Definition of the optimization agent\n    # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example\n    # Arguments described above\n    # History is used to store results\n    agent = SafeOptAgent(mutable_params,\n                         abort_reward,\n                         j_min,\n                         kernel,\n                         dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean,\n                              safe_threshold=safe_threshold, explore_threshold=explore_threshold),\n                         [ctrl],\n                         dict(master=[[f'lc.inductor{k}.i' for k in '123'],\n                                      [f'lc.capacitor{k}.v' for k in '123']\n                                      ]),\n                         history=FullHistory()\n                         )\n\n    if include_simulate:\n        #####################################\n        # Definition of the environment using a FMU created by OpenModelica\n        # (https://www.openmodelica.org/)\n        # Using an inverter supplying a load\n        # - using the reward function described above as callable in the env\n        # - viz_cols used to choose which measurement values should be displayed.\n        #   Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide)\n        #   generated figures are stored to file\n        # - inputs to the models are the connection points to the inverters (see user guide for more details)\n        # - model outputs are the 3 currents through the inductors and the 3 voltages across the capacitors\n\n        # Defining unbalanced loads sampling from Gaussian distribution with sdt = 0.2*mean\n        # r_load = Load(R, 0.1 * R, balanced=balanced_load, tolerance=0.1)\n        # l_load = Load(L, 0.1 * L, balanced=balanced_load, tolerance=0.1)\n        # i_noise = Noise([0, 0, 0], [0.0822, 0.103, 0.136], 0.05, 0.2)\n\n        # r_filt = Load(R_filt, 0 * R_filt, balanced=balanced_load)\n        # l_filt = Load(L_filt, 0 * L_filt, balanced=balanced_load)\n        # c_filt = Load(C_filt, 0 * C_filt, balanced=balanced_load)\n        # r_load = Load(R, 0 * R, balanced=balanced_load)\n        # meas_noise = Noise([0, 0, 0, 0, 0, 0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 0.0, 0.0)\n\n        r_filt = Load(R_filter, 0.1 * R_filter, balanced=balanced_load)\n        l_filt = Load(L_filter, 0.1 * L_filter, balanced=balanced_load)\n        c_filt = Load(C_filter, 0.1 * C_filter, balanced=balanced_load)\n        r_load = Load(R, 0.1 * R, balanced=balanced_load)\n\n\n        # meas_noise = Noise([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n        #                   [0.45, 0.39, 0.42, 0.0023, 0.0015, 0.0018, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0.0, 0.5)\n\n        def reset_loads():\n            r_load.reset()\n            r_filt.reset()\n            l_filt.reset()\n            c_filt.reset()\n\n\n        plotter = PlotManager(agent, save_results=save_results, save_folder=save_folder,\n                              show_plots=show_plots)\n\n        env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                       reward_fun=rew.rew_fun_vc,\n                       viz_cols=[\n                           PlotTmpl([[f'lc.capacitor{i}.v' for i in '123'], [f'master.SPV{i}' for i in 'abc']],\n                                    callback=plotter.xylables_v_abc,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    ),\n                           PlotTmpl([[f'master.CVV{i}' for i in 'dq0'], [f'master.SPV{i}' for i in 'dq0']],\n                                    callback=plotter.xylables_v_dq0,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    ),\n                           PlotTmpl([[f'lc.inductor{i}.i' for i in '123'], [f'master.SPI{i}' for i in 'abc']],\n                                    callback=plotter.xylables_i_abc,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    ),\n                           PlotTmpl([[f'master.I_hat{i}' for i in 'abc'], [f'r_load.resistor{i}.i' for i in '123'], ],\n                                    callback=lambda fig: plotter.update_axes(fig, title='Simulation',\n                                                                             ylabel='$i_{\\mathrm{o estimate,abc}}\\,/\\,\\mathrm{A}$'),\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[['-*'], ['--*']]\n                                    ),\n                           PlotTmpl([[f'master.m{i}' for i in 'dq0']],\n                                    callback=lambda fig: plotter.update_axes(fig, title='Simulation',\n                                                                             ylabel='$m_{\\mathrm{dq0}}\\,/\\,\\mathrm{}$',\n                                                                             filename='Sim_m_dq0')\n                                    ),\n                           PlotTmpl([[f'master.CVi{i}' for i in 'dq0'], [f'master.SPI{i}' for i in 'dq0']],\n                                    callback=plotter.xylables_i_dq0,\n                                    color=[['b', 'r', 'g'], ['b', 'r', 'g']],\n                                    style=[[None], ['--']]\n                                    )\n                       ],\n                       log_level=logging.INFO,\n                       viz_mode='episode',\n                       max_episode_steps=max_episode_steps,\n                       model_params={'lc.resistor1.R': R_filter,\n                                     'lc.resistor2.R': R_filter,\n                                     'lc.resistor3.R': R_filter,\n                                     'lc.resistor4.R': 0.0000001,\n                                     'lc.resistor5.R': 0.0000001,\n                                     'lc.resistor6.R': 0.0000001,\n                                     'lc.inductor1.L': L_filter,\n                                     'lc.inductor2.L': L_filter,\n                                     'lc.inductor3.L': L_filter,\n                                     'lc.capacitor1.C': partial(c_filt.load_step, n=0),\n                                     'lc.capacitor2.C': partial(c_filt.load_step, n=1),\n                                     'lc.capacitor3.C': partial(c_filt.load_step, n=2),\n                                     'r_load.resistor1.R': partial(r_load.load_step, n=0),\n                                     'r_load.resistor2.R': partial(r_load.load_step, n=1),\n                                     'r_load.resistor3.R': partial(r_load.load_step, n=2),\n                                     },\n                       net=net,\n                       model_path='../../omg_grid/grid.paper_loadstep.fmu',\n                       history=FullHistory(),\n                       action_time_delay=1 * undersample\n                       )\n\n        runner = MonteCarloRunner(agent, env)\n\n        runner.run(num_episodes, n_mc=n_MC, visualise=True, prepare_mc_experiment=reset_loads,\n                   return_gradient_extend=True)\n        env.history.df.to_hdf('env_hist_obs2plt.hd5', 'hist')\n        # df2 = pd.read_hdf('env_hist_obs2plt.hd5', 'hist')\n        print(agent.unsafe)\n\n        df_len = pd.DataFrame({'lengthscale': lengthscale,\n                               'bounds': bounds,\n                               'balanced_load': balanced_load,\n                               'barrier_param_mu': mu,\n                               'J_min': j_min})\n\n        if save_results:\n            agent.history.df.to_csv(save_folder + '/_result.csv')\n            df_len.to_csv(save_folder + '/_params.csv')\n\n        print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4})))\n        print('\\n Experiment finished with best set: \\n')\n        print('\\n  Current-Ki&Kp and voltage-Ki&Kp = {}'.format(\n            agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n        print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n        print('\\n\\nBest experiment results are plotted in the following:')\n\n    if do_measurement:\n        #####################################\n        # Execution of the experiment\n        # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations)\n\n        env = TestbenchEnvVoltage(num_steps=max_episode_steps, DT=1 / 10000, v_nominal=nomVoltPeak,\n                                  v_limit=vLimit, f_nom=nomFreq, mu=mu)\n        runner = RunnerHardware(agent, env)\n        runner = RunnerHardwareGradient(agent, env)\n\n        runner.run(num_episodes, visualise=True, save_folder=save_folder)\n\n        print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df[:]))\n\n        print('\\n Experiment finished with best set: \\n')\n        print('\\n  Current-Kp&Ki and voltage-Kp&Ki = {}'.format(\n            agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n        print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n        print('\\n\\nBest experiment results are plotted in the following:')\n\n        df_len = pd.DataFrame({'lengthscale': lengthscale,\n                               'bounds': bounds,\n                               'balanced_load': balanced_load,\n                               'barrier_param_mu': mu,\n                               'J_min': j_min})\n\n        if save_results:\n            agent.history.df.to_csv(save_folder + '/_meas_result.csv')\n            df_len.to_csv(save_folder + '/_meas_params.csv')\n"
  },
  {
    "path": "experiments/testing_framework_control/metrics.py",
    "content": "from math import sqrt\n\nimport numpy as np\nfrom scipy.signal import argrelextrema\nfrom sklearn.metrics import mean_squared_error\n\n\n#####################################\n# Calculation of Metrics\n\nclass Metrics:\n    def __init__(self, quantity, ref_value: float, ts: float, max_episode_steps: int, position_steady_state: int = 0,\n                 position_settling_time: int = 0):\n        \"\"\"\n        :param quantity: analysed quantity from the history (e.g. current idq0)\n        :param ref_value: setpoint of the quantity\n        :param position_steady_state: step where quantity reached steady state\n        :param position_settling_time: step where quantity reached settling time\n        :param ts: absolute time resolution of the env\n        :param max_episode_steps: number of simulation steps per episode\n        \"\"\"\n\n        self.quantity = quantity\n        self.ts = ts\n        self.ref_value = ref_value\n        # upper bound --> important for settling time\n        self.upper_bound = 1.02 * ref_value\n        # lower bound --> important for settling time\n        self.lower_bound = 0.98 * ref_value\n\n        self.position_steady_state = position_steady_state\n        self.position_settling_time = position_settling_time\n        self.max_episode_steps = max_episode_steps\n\n        # creates interval before load steps are applied\n        self.interval_before_load_steps = self.quantity.iloc[0:position_steady_state]\n        self.max_quantity = 0\n        # important for command 'argrelextrema'\n        self.n = 5\n\n    def overshoot(self):\n        \"\"\"\n        calculation of overshoot\n        :return:\n        \"\"\"\n        # tries to find all maxima before load steps are implemented\n        self.interval_before_load_steps['max'] = \\\n            self.interval_before_load_steps.iloc[\n                argrelextrema(self.interval_before_load_steps.values, np.greater_equal, order=self.n)[0]]\n        # return the highest max\n        self.max_quantity = self.interval_before_load_steps['max'].max()\n        overshoot = (self.max_quantity / self.ref_value) - 1\n        if self.max_quantity > self.ref_value:\n            return round(overshoot, 4)\n\n    def rise_time(self):\n        \"\"\"\n        calculation of rise time\n        :return:\n        \"\"\"\n        # 10% of its final value\n        position_start_rise_time = self.quantity[self.quantity.iloc[:, 0] >= 0.1 * self.ref_value].index[0]\n        # 90 % of its final value\n        position_end_rise_time = self.quantity[self.quantity.iloc[:, 0] >= 0.9 * self.ref_value].index[0]\n        position_rise_time = position_end_rise_time - position_start_rise_time\n        rise_time_in_seconds = position_rise_time * self.ts\n        return round(rise_time_in_seconds, 4)\n\n    def settling_time(self):\n        \"\"\"\n        identification of settling time\n        :return:\n        \"\"\"\n        if self.position_settling_time == 0:\n            raise RuntimeError(\n                \"Steady State could not be reached. The controller need to be improved. PROGRAM EXECUTION STOP\")\n        # is calculated in the class LoadstepCallback\n        return self.position_settling_time * self.ts\n\n        # def settling_time_vd_droop(self):\n        \"\"\"\n        identification of settling time, only for vd in in tf_primarylevel_vdq_slavefreq.py\n        :return:\n        \"\"\"\n\n    #    interval_before_steady_state = self.quantity['master.CVVd'].iloc[0:self.position_steady_state]\n\n    # find the beginning of the last period settled period\n    #    is_settled = False\n    #    for index, data in interval_before_steady_state.items():\n    #        if self.lower_bound < data < self.upper_bound and not is_settled:\n    #            is_settled = True\n    #            self.position_settling_time_CVVd = index\n    #        else:\n    #            is_settled = False\n\n    #    if self.position_settling_time_CVVd == 0:\n    #        raise RuntimeError(\n    #            \"Steady State could not be reached. The controller need to be improved. PROGRAM EXECUTION STOP\")\n\n    #    return self.position_settling_time_CVVd * self.ts\n\n    def settling_time_vd_droop(\n            self):  # identification of settling time, only for vd in in tf_primarylevel_vdq_slavefreq.py\n        interval_before_steady_state = self.quantity['master.CVVd'].iloc[0:self.position_steady_state]\n        for index, row in interval_before_steady_state.iteritems():  # iteration\n            if row > self.lower_bound and row < self.upper_bound and self.settling_time_check == False:\n                self.settling_time_check = True\n                self.position_settling_time_CVVd = index\n            if row < self.lower_bound or row > self.upper_bound:\n                self.settling_time_check = False\n        if self.position_settling_time_CVVd == 0:\n            raise RuntimeError(\n                \"Steady State could not be reached. The controller need to be improved. PROGRAM EXECUTION STOP\")\n        settling_time_value = self.position_settling_time_CVVd * self.ts\n        return settling_time_value\n\n    def RMSE(self):\n        # converts and reshapes it into an array\n        Y_true = self.quantity.to_numpy().reshape(-1)\n        # drops nan\n        Y_true = Y_true[~np.isnan(Y_true)]\n        # creates an list with the set value of the voltage and the length of the real voltages (Y_true)\n        Y_pred = [self.ref_value] * (len(Y_true))\n        # converts this list into an array\n        Y_pred = np.array(Y_pred)\n        # returns the RMSE from sklearn\n        return round(sqrt(mean_squared_error(Y_true, Y_pred)), 4)\n\n    def steady_state_error(self):\n        \"\"\"\n        for a controller with an integral part, steady_state_error may be zero\n        :return:\n        \"\"\"\n        # the last value of the quantity is stored\n        last_value_quantity = self.quantity.iloc[self.max_episode_steps - 1]\n        # calculation of the steady-state-error\n        steady_state_error = np.abs(self.ref_value - last_value_quantity[0])\n        return round(steady_state_error, 4)\n\n    def absolute_peak(self):\n        \"\"\"\n        absolute peak is calculated\n        :return:\n        \"\"\"\n        max_quantity = self.quantity.abs().max()\n        return round(max_quantity[0], 4)\n"
  },
  {
    "path": "experiments/testing_framework_control/net.yaml",
    "content": "v_nom: 230*sqrt(2)\nfreq_nom: 50\nts: .5e-4\n\ncomponents:\n  inv1:\n    id: inverter1\n    #i_nom: 20\n    #i_lim: 30\n    #v_DC: 1000\n    cls: MasterInverter\n    in:\n      u: [i1p1, i1p2, i1p3]    # names of the inputs\n    out:\n      v: [lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v]\n      i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i]\n    # iref: [0,0,0]\n    # vref: [1,0,0]\n  inv2:\n    id: inverter2\n    cls: SlaveInverter\n    #pll:\n    #  kP: 10\n    #  kI: 200\n    in:\n      u: [i2p1, i2p2, i2p3]\n    out:\n      v: [lcl1.capacitor1.v, lcl1.capacitor2.v, lcl1.capacitor3.v]\n      i: [lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i]\n\n      #i: [.resistor1.R, .resistor2.R, .resistor3.R, .resistor1.i, .resistor2.i, .resistor3.i ]\n    #i_ref: [15,0,0]\n  load:\n    id: rl1\n    cls: Load\n    out:\n      i: [.resistor1.R, .resistor2.R, .resistor3.R, .inductor1.L, .inductor2.L, .inductor3.L]\n      #.inductor1.L, inductor2.L, inductor3.L\n\n"
  },
  {
    "path": "experiments/testing_framework_control/net_RL_load.yaml",
    "content": "v_nom: 230*sqrt(2)\nfreq_nom: 50\nts: .5e-4\n\ncomponents:\n  inv1:\n    id: inverter1\n    #i_nom: 20\n    #i_lim: 30\n    #v_DC: 1000\n    cls: MasterInverter\n    v_noise:\n      fun:\n        normal: # np.random.*\n          loc: 0\n          scale: 0.001\n    i_noise:\n      fun:\n        normal: # np.random.*\n          loc: 0\n          scale: 0.001\n      clip: # np.clip\n        a_min: -1\n        a_max: 1\n    in:\n      u: [ i1p1, i1p2, i1p3 ]    # names of the inputs\n    out:\n      v: [ lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v ]\n      i: [ lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i ]\n    # iref: [0,0,0]\n    # vref: [1,0,0]\n  inv2:\n    id: inverter2\n    cls: SlaveInverter\n    #pll:\n    #  kP: 10\n    #  kI: 200\n    in:\n      u: [ i2p1, i2p2, i2p3 ]\n    out:\n      v: [ lcl1.capacitor1.v, lcl1.capacitor2.v, lcl1.capacitor3.v ]\n      i: [ lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i ]\n    #i_ref: [15,0,0]\n  load:\n    id: rl1\n    cls: Load\n    out:\n      i: [ .inductor1.i, .inductor2.i, .inductor3.i ]\n      R: [ .resistor1.R, .resistor2.R, .resistor3.R ]\n      L: [ .inductor1.L, .inductor2.L, .inductor3.L ]\n\n"
  },
  {
    "path": "experiments/testing_framework_control/net_single-inv-curr.yaml",
    "content": "v_nom: 230*sqrt(2)\n#freq_nom: 50\nts: 1e-4\n\ncomponents:\n  inv1:\n    id: inverter1\n    #i_nom: 20\n    #i_lim: 30\n    v_DC: 1000\n    cls: MasterInverterCurrentSourcing\n    in:\n      u: [i1p1, i1p2, i1p3]    # names of the inputs\n    out:\n      v: [lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v]\n      i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i]\n    # iref: [0,0,0]\n    # vref: [1,0,0]\n  load:\n    id: rl1\n    cls: Load\n    out:\n      i: [.inductor1.L, .inductor2.L, .inductor3.L]\n      R: [ .resistor1.R, .resistor2.R, .resistor3.R ]\n\n"
  },
  {
    "path": "experiments/testing_framework_control/scoringmodel_innerlevel.py",
    "content": "########################################################################\n# Scoring Model of the inner level to quantify the controller performance\n# Reading the metrics dataframes that were saved in a pkl file\n# Comparison of the metrics\n# Each Metric is equally weighted --> could be edited by the user\n\n\nimport pandas as pd\nimport numpy as np\n\n#############Important###########################################################\n# Save the data frames of the metrics as pkl.....\n# .....at the corresponding positions in tf_innerlevel.....py\n# The positions are indicated there.\n\ndf_id_controller1 = pd.read_pickle(\"./df_metrics_id_controller1.pkl\")\ndf_iq_controller1 = pd.read_pickle(\"./df_metrics_iq_controller1.pkl\")\ndf_vd_controller1 = pd.read_pickle(\"./df_metrics_vd_controller1.pkl\")\ndf_vq_controller1 = pd.read_pickle(\"./df_metrics_vq_controller1.pkl\")\n\n###################################################################\ndf_id_controller2 = pd.read_pickle(\"./df_metrics_id_controller2.pkl\")\ndf_vd_controller2 = pd.read_pickle(\"./df_metrics_vd_controller2.pkl\")\ndf_iq_controller2 = pd.read_pickle(\"./df_metrics_iq_controller2.pkl\")\ndf_vq_controller2 = pd.read_pickle(\"./df_metrics_vq_controller2.pkl\")\n\n###################id Single Inverter Current Control##########################################\n\ncomparison_id = pd.concat([df_id_controller1, df_id_controller2], axis=1)\ncomparison_id.columns = ['Controller_1', 'Controller_2']\n# Weight_Order = Overshoot | Rise Time | Settling Time | RMSE | Steady State Error\ncomparison_id = comparison_id.assign(Weight=[1, 1, 1, 1, 1])  # Metrics are given weights\ncomparison_id = comparison_id.drop(comparison_id[(comparison_id == 'No overshoot').any(1)].index)  # drops if no os\ncomparison_id['Controller_1'] = comparison_id['Controller_1'].astype(float).round(4)\ncomparison_id['Controller_2'] = comparison_id['Controller_2'].astype(float).round(4)\ncomparison_id['Better Performer'] = np.where(comparison_id['Controller_1'] > comparison_id['Controller_2'],\n                                             'Controller_2',\n                                             (np.where(comparison_id['Controller_1'] == comparison_id['Controller_2'],\n                                                       'equal',\n                                                       'Controller_1')))  # Comparison of controllers\n\nController_1_better_performer = comparison_id.loc[comparison_id['Better Performer'] == 'Controller_1']\nController_2_better_performer = comparison_id.loc[comparison_id['Better Performer'] == 'Controller_2']\nController_1_points_id = Controller_1_better_performer['Weight'].sum()  # Scores of Controller 1 are summed up\nController_2_points_id = Controller_2_better_performer['Weight'].sum()  # Scores of Controller 2 are summed up\n\n###################iq Single Inverter Current Control##########################################\n\ncomparison_iq = pd.concat([df_iq_controller1, df_iq_controller2], axis=1)\ncomparison_iq.columns = ['Controller_1', 'Controller_2']\n# Weight_Order = RMSE | Steady-State-Error | Absolute Peak Value\ncomparison_iq = comparison_iq.assign(Weight=[1, 1, 1])  # Metrics are given weights\ncomparison_iq['Controller_1'] = comparison_iq['Controller_1'].astype(float).round(4)\ncomparison_iq['Controller_2'] = comparison_iq['Controller_2'].astype(float).round(4)\ncomparison_iq['Better Performer'] = np.where(comparison_iq['Controller_1'] > comparison_iq['Controller_2'],\n                                             'Controller_2',\n                                             (np.where(comparison_iq['Controller_1'] == comparison_iq['Controller_2'],\n                                                       'equal',\n                                                       'Controller_1')))  # Comparison of controllers\nController_1_better_performer = comparison_iq.loc[comparison_iq['Better Performer'] == 'Controller_1']\nController_2_better_performer = comparison_iq.loc[comparison_iq['Better Performer'] == 'Controller_2']\nController_1_points_iq = Controller_1_better_performer['Weight'].sum()  # Scores of Controller 1 are summed up\nController_2_points_iq = Controller_2_better_performer['Weight'].sum()  # Scores of Controller 2 are summed up\n\n###################Vd Cascaded Structure (Single Inverter Voltage Current Control)#######################\ncomparison_vd = pd.concat([df_vd_controller1, df_vd_controller2], axis=1)  # Df of C1 and C2 are merged\ncomparison_vd.columns = ['Controller_1', 'Controller_2']\n# Weight_Order = Overshoot | Rise Time | Settling Time | RMSE | Steady State Error\ncomparison_vd = comparison_vd.assign(Weight=[1, 1, 1, 1, 1])  # Metrics are given weights\ncomparison_vd = comparison_vd.drop(\n    comparison_vd[\n        (comparison_vd == 'No overshoot').any(1)].index)  # If no overshoot, the overshoot will not be considered\ncomparison_vd['Controller_1'] = comparison_vd['Controller_1'].astype(float).round(4)\ncomparison_vd['Controller_2'] = comparison_vd['Controller_2'].astype(float).round(4)\ncomparison_vd['Better Performer'] = np.where(comparison_vd['Controller_1'] > comparison_vd['Controller_2'],\n                                             'Controller_2',\n                                             (np.where(comparison_vd['Controller_1'] == comparison_vd['Controller_2'],\n                                                       'equal',\n                                                       'Controller_1')))  # Comparison of controllers\nController_1_better_performer = comparison_vd.loc[comparison_vd['Better Performer'] == 'Controller_1']\nController_2_better_performer = comparison_vd.loc[comparison_vd['Better Performer'] == 'Controller_2']\nController_1_points_vd = Controller_1_better_performer['Weight'].sum()  # Scores of Controller 1 are summed up\nController_2_points_vd = Controller_2_better_performer['Weight'].sum()  # Scores of Controller 2 are summed up\n\n###################Vq Cascaded Structure (Single Inverter Voltage Current Control)#######################\ncomparison_vq = pd.concat([df_vq_controller1, df_vq_controller2], axis=1)\ncomparison_vq.columns = ['Controller_1', 'Controller_2']\n# Weight_Order = RMSE | Steady-State-Error | absolute Peak Value\ncomparison_vq = comparison_vq.assign(Weight=[1, 1, 1])  # Metrics are given weights\ncomparison_vq['Controller_1'] = comparison_vq['Controller_1'].astype(float).round(4)\ncomparison_vq['Controller_2'] = comparison_vq['Controller_2'].astype(float).round(4)\ncomparison_vq['Better Performer'] = np.where(comparison_vq['Controller_1'] > comparison_vq['Controller_2'],\n                                             'Controller_2',\n                                             (np.where(comparison_vq['Controller_1'] == comparison_vq['Controller_2'],\n                                                       'equal', 'Controller_1')))  # Comparison of controllers\nController_1_better_performer = comparison_vq.loc[comparison_vq['Better Performer'] == 'Controller_1']\nController_2_better_performer = comparison_vq.loc[comparison_vq['Better Performer'] == 'Controller_2']\nController_1_points_vq = Controller_1_better_performer['Weight'].sum()  # Scores of Controller 1 are summed up\nController_2_points_vq = Controller_2_better_performer['Weight'].sum()  # Scores of Controller 2 are summed up\n\nprint()\nprint(\"###########################################################################################\")\nprint(\"Results for Vd - cascaded control structure (outer voltage control, inner current control)\")\nprint(\"###########################################################################################\")\nprint(comparison_vd)\nprint()\nprint(\"Controller1_Vd_Points: \", Controller_1_points_vd)\nprint(\"Controller2_Vd_Points: \", Controller_2_points_vd)\n\nprint()\nprint(\"#############################################################################################\")\nprint(\"Results for Vq - cascaded control structure (outer control: voltage, inner control: current)\")\nprint(\"#############################################################################################\")\nprint(comparison_vq)\nprint()\nprint(\"Controller1_Vq_Points: \", Controller_1_points_vq)\nprint(\"Controller2_Vq_Points: \", Controller_2_points_vq)\n\nprint()\nprint(\"#################################################\")\nprint(\"Results for id - Single Inverter Current Control\")\nprint(\"#################################################\")\nprint(comparison_id)\nprint()\nprint(\"Controller1_id_Points: \", Controller_1_points_id)\nprint(\"Controller2_id_Points: \", Controller_2_points_id)\n\nprint()\nprint(\"#################################################\")\nprint(\"Results for iq - Single Inverter Current Control\")\nprint(\"#################################################\")\nprint(comparison_iq)\nprint()\nprint(\"Controller1_iq_Points: \", Controller_1_points_iq)\nprint(\"Controller2_iq_Points: \", Controller_2_points_iq)\n\n###############Overall Results - Output ###########\n\n########Current Control (Idq)############\n\noverall_Controller_1_points_current_control = Controller_1_points_id + Controller_1_points_iq\noverall_Controller_2_points_current_control = Controller_2_points_id + Controller_2_points_iq\nprint()\nprint()\nprint()\nprint()\nprint()\nprint(\"Summary\")\nprint(\"##############################\")\nprint(\"Overall Results Current Control\")\nprint(\"##############################\")\nprint()\nprint(\"Controller_1_Points_overall: \", overall_Controller_1_points_current_control)\nprint(\"Controller_2_Points_overall: \", overall_Controller_2_points_current_control)\nprint()\nif overall_Controller_2_points_current_control < overall_Controller_1_points_current_control:\n    print(\"Controller 1 is the better performer for current control.The results still need to be\")\n    print(\"treated with caution, because depending on the purpose of the controller in the microgrid, \")\n    print(\"individual metrics may be more important than others. \")\nelif overall_Controller_2_points_current_control == overall_Controller_1_points_current_control:\n    print(\"No controller is the better performer for current control.The results still need to be\")\n    print(\"treated with caution, because depending on the purpose of the controller in the microgrid,\")\n    print(\"individual metrics may be more important than others.\")\nelse:\n    print(\"Controller 2 is the better performer for current control.The results still need to be\")\n    print(\"treated with caution, because depending on the purpose of the controller in the microgrid,\")\n    print(\"individual metrics may be more important than others.\")\n\n################Cascaded Control Structure (Vdq)###########\n\noverall_Controller_1_points_vd = Controller_1_points_vd + Controller_1_points_vq\noverall_Controller_2_points_vq = Controller_2_points_vd + Controller_2_points_vq\nprint()\nprint(\"Summary\")\nprint(\"####################################################\")\nprint(\"Overall Results Voltage Control (Cascaded Structure)\")\nprint(\"####################################################\")\nprint()\nprint(\"Controller_1_Points_overall: \", overall_Controller_1_points_vd)\nprint(\"Controller_2_Points_overall: \", overall_Controller_2_points_vq)\nprint()\nif overall_Controller_2_points_vq < overall_Controller_1_points_vd:\n    print(\"Controller 1 is the better performer for voltage control.The results still need to be\")\n    print(\"treated with caution,because depending on the purpose of the controller in the microgrid, \")\n    print(\"individual metrics may be more important than others. \")\nelif overall_Controller_2_points_vq == overall_Controller_1_points_vd:\n    print(\"No controller is the better performer for the voltage control.The results still need to be treated\")\n    print(\"with caution, because depending on the purpose of the controller in the microgrid,\")\n    print(\"individual metrics may be more important than others.\")\nelse:\n    print(\"Controller 2 is the better performer for voltage control.The results still need to be\")\n    print(\"treated with caution, because depending on the purpose of the controller in the microgrid,\")\n    print(\"individual metrics may be more important than others.\")\n"
  },
  {
    "path": "experiments/testing_framework_control/scoringmodel_primarylevel.py",
    "content": "########################################################################\n# Scoring Model of the primary level to quantify the controller performance\n# Reading the metrics dataframes that were saved in a pkl file\n# Comparison of the metrics\n# Each Metric is equally weighted --> could be edited by the user\nimport pandas as pd\nimport numpy as np\n\n#############Important###########################################################\n# Save the data frames of the metrics as pkl.....\n# .....at the corresponding positions in tf_primarylevel_vdq_slavefreq.py\n# The positions are indicated there.\n\n##########################################################################\ndf_vd_controller1 = pd.read_pickle(\"./df_metrics_vd_controller1_droop.pkl\")\ndf_vq_controller1 = pd.read_pickle(\"./df_metrics_vq_controller1_droop.pkl\")\ndf_slave_frequency_controller1 = pd.read_pickle(\"./df_metrics_slave_f_controller1_droop.pkl\")\n\n##########################################################################\n\ndf_vd_controller2 = pd.read_pickle(\"./df_metrics_vd_controller2_droop.pkl\")\ndf_vq_controller2 = pd.read_pickle(\"./df_metrics_vq_controller2_droop.pkl\")\ndf_slave_frequency_controller2 = pd.read_pickle(\"./df_metrics_slave_f_controller2_droop.pkl\")\n\n###########################################################################\n\n########Vd Two Inverter Droop##############################################\n\ncomparison_vd = pd.concat([df_vd_controller1, df_vd_controller2], axis=1)\ncomparison_vd.columns = ['Controller_1', 'Controller_2']\ncomparison_vd = comparison_vd.drop(comparison_vd[(comparison_vd == 'No overshoot').any(\n    1)].index)  # If no overshoot, the overshoot will not be considered\n# Weight_Order = Overshoot | Rise Time | Settling Time | RMSE | Steady State Error\ncomparison_vd = comparison_vd.assign(Weight=[1, 1, 1, 1, 1])  # Metrics are given weights\ncomparison_vd['Controller_1'] = comparison_vd['Controller_1'].astype(float).round(4)\ncomparison_vd['Controller_2'] = comparison_vd['Controller_2'].astype(float).round(4)\ncomparison_vd['Better Performer'] = np.where(comparison_vd['Controller_1'] > comparison_vd['Controller_2'],\n                                             'Controller_2',\n                                             (np.where(comparison_vd['Controller_1'] == comparison_vd['Controller_2'],\n                                                       'equal', 'Controller_1')))  # Comparison of controllers\nController_1_better_performer = comparison_vd.loc[comparison_vd['Better Performer'] == 'Controller_1']\nController_2_better_performer = comparison_vd.loc[comparison_vd['Better Performer'] == 'Controller_2']\nController_1_points_vd = Controller_1_better_performer['Weight'].sum()  # Scores of Controller 1 are summed up\nController_2_points_vd = Controller_2_better_performer['Weight'].sum()  # Scores of Controller 2 are summed up\n\nprint()\nprint(\"###############################\")\nprint(\"Results for Vd - Primary level\")\nprint(\"###############################\")\nprint(comparison_vd)\nprint()\nprint(\"Controller1_Vd_Points: \", Controller_1_points_vd)\nprint(\"Controller2_Vd_Points: \", Controller_2_points_vd)\n\n########Vq Two Inverter Droop##############################################\n\ncomparison_vq = pd.concat([df_vq_controller1, df_vq_controller2], axis=1)\ncomparison_vq.columns = ['Controller_1', 'Controller_2']\n# Weight_Order = RMSE | Steady-State-Error | Absolute Peak Value\ncomparison_vq = comparison_vq.assign(Weight=[1, 1, 1])  # Metrics are given weights\ncomparison_vq['Controller_1'] = comparison_vq['Controller_1'].astype(float).round(4)\ncomparison_vq['Controller_2'] = comparison_vq['Controller_2'].astype(float).round(4)\ncomparison_vq['Better Performer'] = np.where(comparison_vq['Controller_1'] > comparison_vq['Controller_2'],\n                                             'Controller_2',\n                                             (np.where(comparison_vq['Controller_1'] == comparison_vq['Controller_2'],\n                                                       'equal', 'Controller_1')))  # Comparison of controllers\nController_1_better_performer = comparison_vq.loc[comparison_vq['Better Performer'] == 'Controller_1']\nController_2_better_performer = comparison_vq.loc[comparison_vq['Better Performer'] == 'Controller_2']\nController_1_points_vq = Controller_1_better_performer['Weight'].sum()  # Scores of Controller 1 are summed up\nController_2_points_vq = Controller_2_better_performer['Weight'].sum()  # Scores of Controller 2 are summed up\n\nprint()\nprint(\"###############################\")\nprint(\"Results for Vq - Primary level\")\nprint(\"###############################\")\nprint(comparison_vq)\nprint()\nprint(\"Controller1_Vq_Points: \", Controller_1_points_vq)\nprint(\"Controller2_Vq_Points: \", Controller_2_points_vq)\n\n#######Slave Frequency Two Inverter Droop########\n\ncomparison_slave_frequency = pd.concat([df_slave_frequency_controller1, df_slave_frequency_controller2], axis=1)\ncomparison_slave_frequency.columns = ['Controller_1', 'Controller_2']\n# Weight_Order = Overshoot | Rise Time | Settling Time | RMSE | Steady State Error\ncomparison_slave_frequency = comparison_slave_frequency.assign(Weight=[1, 1, 1, 1, 1])  # Metrics are given weights\ncomparison_slave_frequency = comparison_slave_frequency.drop(comparison_slave_frequency[\n                                                                 (comparison_slave_frequency == 'No overshoot').any(\n                                                                     1)].index)  # If no os, row will be dropped\ncomparison_slave_frequency['Controller_1'] = comparison_slave_frequency['Controller_1'].astype(float).round(4)\ncomparison_slave_frequency['Controller_2'] = comparison_slave_frequency['Controller_2'].astype(float).round(4)\ncomparison_slave_frequency['Better Performer'] = np.where(\n    comparison_slave_frequency['Controller_1'] > comparison_slave_frequency['Controller_2'], 'Controller_2',\n    (np.where(comparison_slave_frequency['Controller_1'] == comparison_slave_frequency['Controller_2'],\n              'equal', 'Controller_1')))  # Comparison of controllers\nController_1_better_performer = comparison_slave_frequency.loc[\n    comparison_slave_frequency['Better Performer'] == 'Controller_1']\nController_2_better_performer = comparison_slave_frequency.loc[\n    comparison_slave_frequency['Better Performer'] == 'Controller_2']\nController_1_points_slave_frequency = Controller_1_better_performer[\n    'Weight'].sum()  # Scores of Controller 1 are summed up\nController_2_points_slave_frequency = Controller_2_better_performer[\n    'Weight'].sum()  # Scores of Controller 2 are summed up\n\nprint()\nprint(\"###########################\")\nprint(\"Results for Slave Frequency\")\nprint(\"###########################\")\nprint(comparison_slave_frequency)\nprint()\nprint(\"Controller1_SlaveFrequency_Points: \", Controller_1_points_slave_frequency)\nprint(\"Controller2_SlaveFrequency_Points: \", Controller_2_points_slave_frequency)\n\n######Overall Results######\n\noverall_Controller_1_points = Controller_1_points_vd + Controller_1_points_vq + Controller_1_points_slave_frequency\noverall_Controller_2_points = Controller_2_points_vd + Controller_2_points_vq + Controller_2_points_slave_frequency\nprint()\nprint()\nprint()\nprint(\"Summary\")\nprint(\"#################################\")\nprint(\"Overall Results for Primary level\")\nprint(\"#################################\")\nprint()\n\nprint()\nprint(\"Controller_1_Points_overall: \", overall_Controller_1_points)\nprint(\"Controller_2_Points_overall: \", overall_Controller_2_points)\nprint()\nif overall_Controller_2_points < overall_Controller_1_points:\n    print(\"Controller 1 is the better performer.The results still need to be treated with caution,\")\n    print(\"because depending on the purpose of the controller in the microgrid, \")\n    print(\"individual metrics may be more important than others. \")\nelif overall_Controller_2_points == overall_Controller_1_points:\n    print(\"No controller is better than the other.The results still need to be treated with caution,\")\n    print(\"because depending on the purpose of the controller in the microgrid,\")\n    print(\"individual metrics may be more important than others.\")\nelse:\n    print(\"Controller 2 is the better performer.The results still need to be treated with caution,\")\n    print(\"because depending on the purpose of the controller in the microgrid,\")\n    print(\"individual metrics may be more important than others.\")\n\n\n"
  },
  {
    "path": "experiments/testing_framework_control/tf_innerlevel_idq.py",
    "content": "#####################################\n# Example using a FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters\n# Simulation setup: Single inverter supplying 15 A d-current to an RL-load via a LC filter\n# Controller: PI current controller gain parameters are optimized by SafeOpt\n# Testing Framework for current control\n# Definition of Benchmark, Implementation of load steps, Measurement of control metrics\n\n\nimport logging\nfrom typing import List\n\nimport GPy\nimport gym\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfrom metrics import Metrics\n\npd.options.mode.chained_assignment = None  # default='warn'\n\nfrom openmodelica_microgrid_gym import Runner\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, MultiPhaseDQCurrentSourcingController\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom openmodelica_microgrid_gym.execution import Callback\nfrom openmodelica_microgrid_gym.net import Network\nfrom openmodelica_microgrid_gym.util import dq0_to_abc, nested_map, FullHistory\n\nfrom random import random\n\n# Choose which controller parameters should be adjusted by SafeOpt.\n# - Kp: 1D example: Only the proportional gain Kp of the PI controller is adjusted\n# - Ki: 1D example: Only the integral gain Ki of the PI controller is adjusted\n# - Kpi: 2D example: Kp and Ki are adjusted simultaneously\n\nadjust = 'Kpi'\n\n# Check if really only one simulation scenario was selected\nif adjust not in {'Kp', 'Ki', 'Kpi'}:\n    raise ValueError(\"Please set 'adjust' to one of the following values: 'Kp', 'Ki', 'Kpi'\")\n\n# Simulation definitions\nnet = Network.load('net_single-inv-curr.yaml')\nmax_episode_steps = 1200  # number of simulation steps per episode\nnum_episodes = 1  # number of simulation episodes (i.e. SafeOpt iterations)\niLimit = 30  # inverter current limit / A\niNominal = 20  # nominal inverter current / A\nmu = 2  # factor for barrier function (see below)\nid_ref = np.array([15, 0, 0])  # exemplary set point i.e. id = 15, iq = 0, i0 = 0 / A\niq_ref = 0  # iq = 0\nR = 20  # resistance value / Ohm\nL = 0.001  # resistance value / Henry\nts = 1e-4  # duration of episode / s\nload_step_resistance = 5  # load step / Ohm\nload_step_inductance = 0.0004  # sets the inductive load step / H\nmovement_1_resistor = -load_step_resistance  # first load step is negative\nmovement_2_resistor = (load_step_resistance if random() < 0.5\n                       else -1 * load_step_resistance) + movement_1_resistor  # randomly chosen load step\nmovement_1_inductance = -load_step_inductance  # first load step is negative\nmovement_2_inductance = (load_step_inductance if movement_2_resistor >= 0\n                         else -load_step_inductance) + movement_1_inductance  # same sign as resistive load step\n\n\n#####################################\n# Definition of Random Walk\n# Starting Value: R = 20 Ohm, L= 1 mH\n# Constant noise is created\n\ndef load_step_random_walk_resistor():\n    random_walk = []\n    random_walk.append(R)  # R = 20 Ohm\n    for i in range(1, max_episode_steps):\n        movement = -0.01 if random() < 0.5 else 0.01  # constant slight and random fluctuation of load\n        value = random_walk[i - 1] + movement\n        random_walk.append(value)\n    return random_walk\n\n\ndef load_step_inductance_random_walk():\n    random_walk_inductance = []\n    random_walk_inductance.append(L)  # L = 1 mH\n    for i in range(1, max_episode_steps):\n        movement = -0.000001 if random() < 0.5 else 0.000001  # constant slight and random fluctuation of load\n        value = random_walk_inductance[i - 1] + movement\n        random_walk_inductance.append(value)\n    return random_walk_inductance\n\n\nlist_resistor = load_step_random_walk_resistor()\nlist_inductance = load_step_inductance_random_walk()\n\n\nclass Reward:\n    def __init__(self):\n        self._idx = None\n\n    def set_idx(self, obs):\n        if self._idx is None:\n            self._idx = nested_map(\n                lambda n: obs.index(n),\n                [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'],\n                 [f'master.CVI{k}' for k in 'dq0']])\n\n    def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and setpoints to evaluate the\n        quality of the used parameters.\n        Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic\n        barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu.\n\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        self.set_idx(cols)\n        idx = self._idx\n\n        Iabc_master = data[idx[0]]  # 3 phase currents at LC inductors\n        phase = data[idx[1]]  # phase from the master controller needed for transformation\n\n        # setpoints\n        ISPdq0_master = data[idx[2]]  # setting dq reference\n        ISPabc_master = dq0_to_abc(ISPdq0_master, phase)  # convert dq set-points into three-phase abc coordinates\n\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        # plus barrier penalty for violating the current constraint\n        error = np.sum((np.abs((ISPabc_master - Iabc_master)) / iLimit) ** 0.5, axis=0) \\\n                + -np.sum(mu * np.log(1 - np.maximum(np.abs(Iabc_master) - iNominal, 0) / (iLimit - iNominal)), axis=0) \\\n                * max_episode_steps\n\n        return -error.squeeze()\n\n\n#########################################################################################\n# Implementation of Load Steps\n# It is checked, if quantity reaches steady state\n# Values are stored in a databuffer\n# If the databuffer fulfills several conditions, the first load steps will be implemented\n# Programming 'on the fly'\n\nclass LoadstepCallback(Callback):\n    def __init__(self):\n        self.steady_state_reached = False\n        self.settling_time = None\n        self.databuffer = None\n        self._idx = None\n        self.interval_check = False\n        self.steady_state_check = False\n        self.list_data = []\n        self.upper_bound = 1.02 * id_ref[0]\n        self.lower_bound = 0.98 * id_ref[0]\n        self.databuffer_settling_time = []\n\n    def set_idx(self, obs):\n        if self._idx is None:\n            self._idx = nested_map(\n                lambda n: obs.index(n),\n                [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'],\n                 [f'master.CVI{k}' for k in 'dq0']])\n\n    def reset(self):\n        self.databuffer = np.empty(20)  # creates a databuffer with a length of twenty\n\n    def __call__(self, cols, obs):\n        self.set_idx(cols)\n        self.databuffer[-1] = obs[self._idx[3][0]]  # last element is replaced with current value\n        self.list_data.append(obs[self._idx[3][0]])  # current value is also appended to the list_data\n        self.databuffer = np.roll(self.databuffer, -1)  # all values are shifted to the left by one\n\n        # condition: it is checked whether the current is outside of the interval\n        if (obs[self._idx[3][0]] < self.lower_bound or obs[\n            self._idx[3][0]] > self.upper_bound) and self.steady_state_reached == False:\n            self.interval_check = False\n\n        # pre-condition: checks if observation is within the specified bound --> settling time may be reached\n        if self.lower_bound < obs[self._idx[3][0]] < self.upper_bound:\n            if not self.interval_check:\n                global position_settling_time\n                position_settling_time = len(self.list_data)\n                self.interval_check = True\n            if self.databuffer.std() < 0.05 and np.round(self.databuffer.mean(), decimals=1) == id_ref[\n                0]:  # if the databuffer fulfills the conditions, steady state is reached\n                self.steady_state_reached = True\n                if not self.steady_state_check:\n                    global position_steady_state\n                    position_steady_state = len(self.list_data)  # position steady state is returned\n                    self.steady_state_check = True\n\n    # After steady state is reached, a fixed load step of + 5 Ohm is implemented\n    # After 3/4 of the simulation time, another random load step of +/- 5 Ohm is implemented\n    def load_step_resistance(self, t):\n        for i in range(1, len(list_resistor)):\n            if self.steady_state_reached == False and t <= ts * i + ts:  # no steady state, no load step\n                return list_resistor[i]  # for every step, it contains values that fluctuate a little bit\n            elif self.steady_state_reached == True and t <= ts * i + ts and i <= max_episode_steps * 0.75:\n                return list_resistor[i] + movement_1_resistor  # when steady state reached, first load step\n            elif self.steady_state_reached == True and t <= ts * i + ts and i > max_episode_steps * 0.75:  # 75 % ts\n                return list_resistor[i] + movement_2_resistor\n\n    # After steady state is reached, a fixed load step of + 0.4 mH is implemented\n    # After 3/4 of the simulation time, another random load step of +/- 0.4 mH is implemented\n    def load_step_inductance(self, t):\n        for i in range(1, len(list_inductance)):\n            if self.steady_state_reached == False and t <= ts * i + ts:  # no load step, no steady state\n                return list_inductance[i]  # for every step, it contains values that fluctuate a little bit\n            elif self.steady_state_reached == True and t <= ts * i + ts and i <= max_episode_steps * 0.75:\n                return list_inductance[i] + movement_1_inductance  # when steady state reached, first load step\n            elif self.steady_state_reached == True and t <= ts * i + ts and i > max_episode_steps * 0.75:  # 75 % ts\n                return list_inductance[i] + movement_2_inductance\n\n\nif __name__ == '__main__':\n    #####################################\n    # Definitions for the GP\n    prior_mean = 0  # mean factor of the GP prior mean which is multiplied with the first performance of the init set\n    noise_var = 0.001 ** 2  # measurement noise sigma_omega\n    prior_var = 0.1  # prior variance of the GP\n\n    bounds = None\n    lengthscale = None\n    if adjust == 'Kp':\n        bounds = [(0.00, 0.03)]  # bounds on the input variable Kp\n        lengthscale = [.01]  # length scale for the parameter variation [Kp] for the GP\n\n    # For 1D example, if Ki should be adjusted\n    if adjust == 'Ki':\n        bounds = [(0, 300)]  # bounds on the input variable Ki\n        lengthscale = [50.]  # length scale for the parameter variation [Ki] for the GP\n\n    # For 2D example, choose Kp and Ki as mutable parameters (below) and define bounds and lengthscale for both\n    if adjust == 'Kpi':\n        bounds = [(0.0, 0.07), (0, 300)]\n        lengthscale = [.02, 50.]\n\n    # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times\n    # the initial performance: safe_threshold = 1.2 means. Performance measurement for optimization are seen as\n    # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!)\n    # parameter set\n    safe_threshold = 0\n    j_min = -2\n    # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop\n    # expanding points eventually.\n    # The following variable is multiplied with the first performance of the initial set by the factor below:\n    explore_threshold = 0  ##2\n\n    # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of\n    # limit exceeded\n    abort_reward = 10 * j_min  # 10\n\n    # Definition of the kernel\n    kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True)\n\n    #####################################\n    # Definition of the controllers\n    mutable_params = None\n    current_dqp_iparams = None\n    if adjust == 'Kp':\n        # mutable_params = parameter (Kp gain of the current controller of the inverter) to be optimized using\n        # the SafeOpt algorithm\n        mutable_params = dict(currentP=MutableFloat(5e-3))\n\n        # Define the PI parameters for the current controller of the inverter\n        current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=115, limits=(-1, 1))\n\n    # For 1D example, if Ki should be adjusted\n    elif adjust == 'Ki':\n        mutable_params = dict(currentI=MutableFloat(10))\n        current_dqp_iparams = PI_params(kP=10e-3, kI=mutable_params['currentI'], limits=(-1, 1))\n\n    # For 2D example, choose Kp and Ki as mutable parameters\n    elif adjust == 'Kpi':\n        mutable_params = dict(currentP=MutableFloat(15e-3), currentI=MutableFloat(10))  # setP= 10 e^-3 and setQ=10\n        current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'], limits=(-1, 1))\n\n    # Define a current sourcing inverter as master inverter using the pi and droop parameters from above\n    ctrl = MultiPhaseDQCurrentSourcingController(current_dqp_iparams, ts_sim=net.ts, f_nom=net.freq_nom,\n                                                 ts_ctrl=1e-4, name='master')\n\n    #####################################\n    # Definition of the optimization agent\n    # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example\n    # Arguments described above\n    # History is used to store results\n    agent = SafeOptAgent(mutable_params,\n                         abort_reward,\n                         j_min, kernel,\n                         dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean,\n                              safe_threshold=safe_threshold, explore_threshold=explore_threshold),\n                         [ctrl],\n                         dict(master=[[f'lc1.inductor{k}.i' for k in '123'],\n                                      id_ref]),\n                         history=FullHistory()\n                         )\n\n\n    #####################################\n    # Definition of the environment using a FMU created by OpenModelica\n    # (https://www.openmodelica.org/)\n    # Using an inverter supplying a load\n    # - using the reward function described above as callable in the env\n    # - viz_cols used to choose which measurement values should be displayed (here, only the 3 currents across the\n    #   inductors of the inverters are plotted. Labels and grid is adjusted using the PlotTmpl (For more information,\n    #   see UserGuide)\n    # - inputs to the models are the connection points to the inverters (see user guide for more details)\n    # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors\n\n    def xylables(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$i_{\\mathrm{abc}}\\,/\\,\\mathrm{A}$')\n        ax.grid(which='both')\n\n\n    def xylables_R(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$R_{\\mathrm{123}}\\,/\\,\\mathrm{\\u03A9}$')\n        ax.grid(which='both')\n\n\n    def xylables_L(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$L_{\\mathrm{123}}\\,/\\,\\mathrm{H}$')\n        ax.grid(which='both')\n\n\n    def xylables_i_dq0(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$i_{\\mathrm{dq0}}\\,/\\,\\mathrm{i}$')\n        ax.grid(which='both')\n\n\n    callback = LoadstepCallback()\n\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   reward_fun=Reward().rew_fun,\n                   viz_cols=[\n                       PlotTmpl([f'lc1.inductor{i}.i' for i in '123'],\n                                callback=xylables\n                                ),\n                       PlotTmpl([f'rl1.resistor{i}.R' for i in '123'],\n                                callback=xylables_R\n                                ),\n                       PlotTmpl([f'master.CVI{s}' for s in 'dq0'],\n                                ),\n                       PlotTmpl([f'rl1.inductor{i}.L' for i in '123'],\n                                callback=xylables_L\n                                )\n                   ],\n                   log_level=logging.INFO,\n                   viz_mode='episode',\n                   model_params={'rl1.resistor1.R': callback.load_step_resistance,  # see LoadstepCallback(Callback)\n                                 'rl1.resistor2.R': callback.load_step_resistance,\n                                 'rl1.resistor3.R': callback.load_step_resistance,\n                                 'rl1.inductor1.L': callback.load_step_inductance,\n                                 'rl1.inductor2.L': callback.load_step_inductance,\n                                 'rl1.inductor3.L': callback.load_step_inductance\n                                 },\n\n                   max_episode_steps=max_episode_steps,\n                   net=net,\n                   model_path='../../omg_grid/grid.network_singleInverter.fmu',\n                   history=FullHistory()\n                   )\n\n    #####################################\n    # Execution of the experiment\n    # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations)\n    runner = Runner(agent, env, callback)\n\n    runner.run(num_episodes, visualise=True)\n\n    print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df[:]))\n\n    print('\\n Experiment finished with best set: \\n')\n    print('\\n  {} = {}'.format(adjust, agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n    print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n    print('\\n\\nBest experiment results are plotted in the following:')\n\n    # Show best episode measurment (current) plot\n    best_env_plt = runner.run_data['best_env_plt']\n    if best_env_plt:\n        ax = best_env_plt[0].axes[0]\n        ax.set_title('Best Episode')\n        best_env_plt[0].show()\n        best_env_plt[0].savefig('best_env_plt.png')\n\n    # Show last performance plot\n    best_agent_plt = runner.run_data['last_agent_plt']\n    if best_agent_plt:\n        ax = best_agent_plt.axes[0]\n        ax.grid(which='both')\n        ax.set_axisbelow(True)\n\n        if adjust == 'Ki':\n            ax.set_xlabel(r'$K_\\mathrm{i}\\,/\\,\\mathrm{(VA^{-1}s^{-1})}$')\n            ax.set_ylabel(r'$J$')\n        elif adjust == 'Kp':\n            ax.set_xlabel(r'$K_\\mathrm{p}\\,/\\,\\mathrm{(VA^{-1})}$')\n            ax.set_ylabel(r'$J$')\n        elif adjust == 'Kpi':\n            agent.params.reset()\n            ax.set_xlabel(r'$K_\\mathrm{i}\\,/\\,\\mathrm{(VA^{-1}s^{-1})}$')\n            ax.set_ylabel(r'$K_\\mathrm{p}\\,/\\,\\mathrm{(VA^{-1})}$')\n            ax.get_figure().axes[1].set_ylabel(r'$J$')\n            plt.plot(bounds[0], [mutable_params['currentP'].val, mutable_params['currentP'].val], 'k-', zorder=1, lw=4,\n                     alpha=.5)\n        best_agent_plt.show()\n        best_agent_plt.savefig('agent_plt.png')\n\n#####################################\n# Calculation of Metrics\n# Here, the d- term of idq is analysed\n\ndf_master_CVId = env.history.df[['master.CVId']]\n\ncurrent_controller_metrics_id = Metrics(df_master_CVId, id_ref[0], ts, max_episode_steps,\n                                        position_steady_state=position_steady_state,\n                                        position_settling_time=position_settling_time)\n\nd = {'Overshoot': [current_controller_metrics_id.overshoot()],\n     'Rise Time/s ': [current_controller_metrics_id.rise_time()],\n     'Settling Time/s ': [current_controller_metrics_id.settling_time()],\n     'Root Mean Squared Error/A': [current_controller_metrics_id.RMSE()],\n     'Steady State Error/A': [current_controller_metrics_id.steady_state_error()]}\n\ndf_metrics_id = pd.DataFrame(data=d).T\ndf_metrics_id.columns = ['Value']\nprint()\nprint('Metrics of id')\nprint(df_metrics_id)\n\n######IMPORTANT FOR THE SCORING MODEL INNER LEVEL##############################\n# Use the following code, to create a pkl-File in which the Dataframe is stored\n# df_metrics_id.to_pickle(\"./df_metrics_id_controller1.pkl\")\n# Maybe you need to replace 'controller1.pkl' with 'controller2.pkl'\n\n\n#####################################\n# Calculation of Metrics\n# Here, the q- term of idq is analysed\n\ndf_master_CVIq = env.history.df[['master.CVIq']]\n\ncurrent_controller_metrics_iq = Metrics(df_master_CVIq, iq_ref, ts, max_episode_steps)\n\nd = {'Root Mean Squared Error/A': [current_controller_metrics_iq.RMSE()],\n     'Steady State Error/A': [current_controller_metrics_iq.steady_state_error()],\n     'absolute Peak Value/A': [current_controller_metrics_iq.absolute_peak()]}\n\ndf_metrics_iq = pd.DataFrame(data=d).T\ndf_metrics_iq.columns = ['Value']\nprint()\nprint('Metrics of iq')\nprint(df_metrics_iq)\n\n######IMPORTANT FOR THE SCORING MODEL INNER LEVEL##############################\n# Use the following code, to create a pkl-File in which the Dataframe is stored\n# df_metrics_id.to_pickle(\"./df_metrics_iq_controller1.pkl\")\n# Maybe you need to replace 'controller1.pkl' with 'controller2.pkl'\n"
  },
  {
    "path": "experiments/testing_framework_control/tf_innerlevel_vdq.py",
    "content": "#####################################\n# Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters\n# Simulation setup: Single voltage forming inverter supplying an RL-load via an LC-filter\n# Controller: Cascaded PI-PI voltage and current controller gain parameters are optimized by SafeOpt\n# Testing Framework for voltage control\n# Definition of Benchmark, Implementation of load steps, Measurement of control metrics\n\n\nimport logging\nimport os\nfrom random import random\nfrom time import strftime, gmtime\nfrom typing import List\n\nimport GPy\nimport gym\nimport numpy as np\nimport pandas as pd\n\npd.options.mode.chained_assignment = None  # default='warn'\n\nfrom openmodelica_microgrid_gym import Runner\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, MultiPhaseDQ0PIPIController\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom openmodelica_microgrid_gym.execution import Callback\nfrom openmodelica_microgrid_gym.net import Network\nfrom openmodelica_microgrid_gym.util import dq0_to_abc, nested_map, FullHistory\n\n# Simulation definitions\nnet = Network.load('net_single-inv-curr.yaml')\nmax_episode_steps = 1200  # number of simulation steps per episode\nnum_episodes = 1  # number of simulation episodes (i.e. SafeOpt iterations)\niLimit = 30  # inverter current limit / A\niNominal = 20  # nominal inverter current / A\nmu = 2  # factor for barrier function (see below)\nDroopGain = 0  # virtual droop gain for active power / W/Hz 40000\nQDroopGain = 0  # virtual droop gain for reactive power / VAR/V 1000\nvd_ref = np.array([325, 0, 0])  # exemplary set point i.e. vd = 325, vq = 0, v0 = 0 / A\nvq_ref = 0  # vq = 0\nR = 20  # resistance value / Ohm\nL = 0.001  # resistance value / Henry\nts = 1e-4  # duration of episode / s\nload_step_resistance = 5  # load step / Ohm\nload_step_inductance = 0.0004  # sets the inductive load step / H\nmovement_1_resistor = load_step_resistance  # first load step is negative\nmovement_2_resistor = (load_step_resistance if random() < 0.5\n                       else -1 * load_step_resistance) + movement_1_resistor  # randomly chosen load step\nmovement_1_inductance = load_step_inductance  # first load step is negative\nmovement_2_inductance = (load_step_inductance if movement_2_resistor >= 0\n                         else -load_step_inductance) + movement_1_inductance  # same sign as resistive load step\n\n\n#####################################\n# Definition of Random Walk\n# Starting Value: R = 20 Ohm, L= 1 mH\n# Constant noise is created\n\ndef load_step_random_walk_resistor():\n    random_walk = []\n    random_walk.append(R)  # R = 20 Ohm\n    for i in range(1, max_episode_steps):\n        movement = -0.01 if random() < 0.5 else 0.01  # constant slight and random fluctuation of load\n        value = random_walk[i - 1] + movement\n        random_walk.append(value)\n    return random_walk\n\n\ndef load_step_inductance_random_walk():\n    random_walk_inductance = []\n    random_walk_inductance.append(L)  # L = 1 mH\n    for i in range(1, max_episode_steps):\n        movement = -0.000001 if random() < 0.5 else 0.000001  # constant slight and random fluctuation of load\n        value = random_walk_inductance[i - 1] + movement\n        random_walk_inductance.append(value)\n    return random_walk_inductance\n\n\nlist_resistor = load_step_random_walk_resistor()\nlist_inductance = load_step_inductance_random_walk()\n\n# Files saves results and  resulting plots to the folder saves_VI_control_safeopt in the current directory\ncurrent_directory = os.getcwd()\nsave_folder = os.path.join(current_directory, r'saves_VI_control_safeopt')\nos.makedirs(save_folder, exist_ok=True)\n\n\nclass Reward:\n    def __init__(self):\n        self._idx = None\n\n    def set_idx(self, obs):  # [f'lc1.inductor{k}.i' for k in '123']\n        if self._idx is None:\n            self._idx = nested_map(\n                lambda n: obs.index(n),\n                [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'],\n                 [f'lc1.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0']])\n\n    def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality\n        of the used parameters.\n        Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a\n        logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using\n        parameter mu.\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        self.set_idx(cols)\n        idx = self._idx\n\n        iabc_master = data[idx[0]]  # 3 phase currents at LC inductors\n        phase = data[idx[1]]  # phase from the master controller needed for transformation\n        vabc_master = data[idx[3]]  # 3 phase currents at LC inductors\n\n        # set points (sp)\n        isp_dq0_master = data[idx[2]]  # setting dq current reference\n        isp_abc_master = dq0_to_abc(isp_dq0_master, phase)  # convert dq set-points into three-phase abc coordinates\n        vsp_dq0_master = data[idx[4]]  # setting dq voltage reference\n        vsp_abc_master = dq0_to_abc(vsp_dq0_master, phase)  # convert dq set-points into three-phase abc coordinates\n\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        # plus barrier penalty for violating the current constraint\n        error = np.sum((np.abs((isp_abc_master - iabc_master)) / iLimit) ** 0.5, axis=0) \\\n                + -np.sum(mu * np.log(1 - np.maximum(np.abs(iabc_master) - iNominal, 0) / (iLimit - iNominal)), axis=0) \\\n                + np.sum((np.abs((vsp_abc_master - vabc_master)) / net.v_nom) ** 0.5, axis=0)\n\n        return -error.squeeze()\n\n\n#########################################################################################\n# Implementation of Load Steps\n# It is checked, if quantity reaches steady state\n# Values are stored in a databuffer\n# If the databuffer fulfills several conditions, the first load steps will be implemented\n# Programming 'on the fly'\n\nclass LoadstepCallback(Callback):\n    def __init__(self):\n        self.steady_state_reached = False\n        self.settling_time = 0\n        self.databuffer = None\n        self._idx = None\n        self.interval_check = False\n        self.steady_state_check = False\n        self.list_data = []\n        self.upper_bound = 1.02 * vd_ref[0]\n        self.lower_bound = 0.98 * vd_ref[0]\n\n    def set_idx(self, obs):\n        if self._idx is None:\n            self._idx = nested_map(\n                lambda n: obs.index(n),\n                [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'],\n                 [f'lc1.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0'],\n                 [f'master.CVV{i}' for i in 'dq0']])\n\n    def reset(self):\n        self.databuffer = np.empty(10)  # creates a databuffer with a length of ten\n\n    def __call__(self, cols, obs):\n        self.set_idx(cols)\n        self.databuffer[-1] = obs[self._idx[5][0]]  # all values are shifted to the left by one\n        self.databuffer = np.roll(self.databuffer, -1)  # all values are shifted to the left by one\n        self.list_data.append(obs[self._idx[5][0]])  # current value is appended to the list\n\n        # condition: it is checked whether the current is outside of the interval --> settling time may not be reached\n        if (obs[self._idx[5][0]] < self.lower_bound or obs[\n            self._idx[5][0]] > self.upper_bound) and not self.steady_state_reached:\n            self.interval_check = False\n\n        # pre-condition: checks if observation is within the specified interval --> settling time may be reached\n        if self.lower_bound < obs[self._idx[5][0]] < self.upper_bound:\n            if not self.interval_check:\n                global position_settling_time\n                position_settling_time = len(self.list_data)\n                self.interval_check = True\n            if self.databuffer.std() < 0.1 and np.round(self.databuffer.mean()) == \\\n                    vd_ref[0]:  # if the databuffer fulfills the conditions, steady state is reached\n                self.steady_state_reached = True\n                if not self.steady_state_check:\n                    global position_steady_state\n                    position_steady_state = len(self.list_data)  # stores the steady state position\n                    self.steady_state_check = True\n\n    # After steady state is reached, a fixed load step of + 5 Ohm is implemented\n    # After 3/4 of the simulation time, another random load step of +/- 5 Ohm is implemented\n    def load_step_resistance(self, t):\n        for i in range(1, len(list_resistor)):\n            if self.steady_state_reached == False and t <= ts * i + ts:  # no steady state, no load step\n                return list_resistor[i]  # for every step, it contains values that fluctuate a little bit\n            elif self.steady_state_reached == True and t <= ts * i + ts and i <= max_episode_steps * 0.75:\n                return list_resistor[i] + movement_1_resistor  # when steady state reached, first load step\n            elif self.steady_state_reached == True and t <= ts * i + ts and i > max_episode_steps * 0.75:  # 75 % ts\n                return list_resistor[i] + movement_2_resistor\n\n    # After steady state is reached, a fixed load step of + 0.4 mH is implemented\n    # After 3/4 of the simulation time, another random load step of +/- 0.4 mH is implemented\n    def load_step_inductance(self, t):\n        for i in range(1, len(list_inductance)):\n            if self.steady_state_reached == False and t <= ts * i + ts:  # no load step, no steady state\n                return list_inductance[i]  # for every step, it contains values that fluctuate a little bit\n            elif self.steady_state_reached == True and t <= ts * i + ts and i <= max_episode_steps * 0.75:\n                return list_inductance[i] + movement_1_inductance  # when steady state reached, first load step\n            elif self.steady_state_reached == True and t <= ts * i + ts and i > max_episode_steps * 0.75:  # 75 % ts\n                return list_inductance[i] + movement_2_inductance\n\n\nif __name__ == '__main__':\n    #####################################\n    # Definitions for the GP\n    prior_mean = 0  # mean factor of the GP prior mean which is multiplied with the first performance of the init set\n    noise_var = 0.001 ** 2  # measurement noise sigma_omega\n    prior_var = 2  # prior variance of the GP\n\n    # Choose Kp and Ki (current and voltage controller) as mutable parameters (below) and define bounds and lengthscale\n    # for both of them\n    bounds = [(0.0, 0.03), (0, 300), (0.0, 0.03),\n              (0, 300)]  # bounds on the input variable current-Ki&Kp and voltage-Ki&Kp\n    lengthscale = [.005, 50., .005,\n                   50.]  # length scale for the parameter variation [current-Ki&Kp and voltage-Ki&Kp] for the GP\n\n    # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times\n    # the initial performance: safe_threshold = 1.2 means: performance measurement for optimization are seen as\n    # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!)\n    # parameter set\n    safe_threshold = 0.5\n    # minimal allowed performance (depending on episode return)\n    j_min = -2\n\n    # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop\n    # expanding points eventually.\n    # The following variable is multiplied with the first performance of the initial set by the factor below:\n    explore_threshold = 0\n\n    # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of\n    # limit exceeded\n    abort_reward = -10 * j_min\n\n    # Definition of the kernel\n    kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True)\n\n    #####################################\n    # Definition of the controllers\n    # Choose Kp and Ki for the current and voltage controller as mutable parameters\n    mutable_params = dict(currentP=MutableFloat(10e-3), currentI=MutableFloat(10), voltageP=MutableFloat(25e-3),\n                          voltageI=MutableFloat(60))\n\n    voltage_dqp_iparams = PI_params(kP=mutable_params['voltageP'], kI=mutable_params['voltageI'],\n                                    limits=(-iLimit, iLimit))\n    current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'], limits=(-1, 1))\n\n    # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for\n    # the filter and the nominal frequency\n    # Droop controller used to calculate the virtual frequency drop due to load changes\n    droop_param = DroopParams(DroopGain, 0.005, net.freq_nom)\n\n    # Define the Q-droop parameters for the inverter of the reactive power VAR/Volt, delta_t (0.002) used for the\n    # filter and the nominal voltage\n    qdroop_param = DroopParams(QDroopGain, 0.002, net.v_nom)\n\n    # Define a voltage forming inverter using the PIPI and droop parameters from above\n    # ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, net.ts, droop_param, qdroop_param,\n    # undersampling=2, name='master')\n    ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param,\n                                       ts_sim=net.ts,\n                                       ts_ctrl=2 * net.ts, name='master')\n    #####################################\n    # Definition of the optimization agent\n    # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example\n    agent = SafeOptAgent(mutable_params,\n                         abort_reward,\n                         j_min,\n                         kernel,\n                         dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean,\n                              safe_threshold=safe_threshold, explore_threshold=explore_threshold),\n                         ctrls=[ctrl],\n                         obs_template=dict(master=[[f'lc1.inductor{k}.i' for k in '123'],\n                                                   [f'lc1.capacitor{k}.v' for k in '123'],\n                                                   ]),\n                         history=FullHistory()\n                         )\n\n\n    # Arguments described above\n    # History is used to store results\n\n    #####################################\n    # Definition of the environment using a FMU created by OpenModelica\n    # (https://www.openmodelica.org/)\n    # Using an inverter supplying a load\n    # - using the reward function described above as callable in the env\n    # - viz_cols used to choose which measurement values should be displayed.\n    #   Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide)\n    #   generated figures are stored to file\n    # - inputs to the models are the connection points to the inverters (see user guide for more details)\n    # - model outputs are the 3 currents through the inductors and the 3 voltages across the capacitors\n\n    def xylables_i(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$i_{\\mathrm{abc}}\\,/\\,\\mathrm{A}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/Inductor_currents' + time + '.pdf')\n\n\n    def xylables_v_abc(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$v_{\\mathrm{abc}}\\,/\\,\\mathrm{V}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/abc_voltage' + time + '.pdf')\n\n\n    def xylables_R(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')  # zeit\n        ax.set_ylabel('$R_{\\mathrm{123}}\\,/\\,\\mathrm{\\u03A9}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/Resistor_Course_Load_Step' + time + '.pdf')\n\n\n    def xylables_i_dq0(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$i_{\\mathrm{dq0}}\\,/\\,\\mathrm{i}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/dq0_current' + time + '.pdf')\n\n\n    def xylables_v_dq0(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$v_{\\mathrm{dq0}}\\,/\\,\\mathrm{V}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/dq0_voltage' + time + '.pdf')\n\n\n    def xylables_L(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$L_{\\mathrm{123}}\\,/\\,\\mathrm{H}$')\n        ax.grid(which='both')\n\n\n    callback = LoadstepCallback()\n\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   reward_fun=Reward().rew_fun,\n                   viz_cols=[\n                       PlotTmpl([f'lc1.inductor{i}.i' for i in '123'],\n                                callback=xylables_i\n                                ),\n                       PlotTmpl([f'lc1.capacitor{i}.v' for i in '123'],\n                                callback=xylables_v_abc\n                                ),\n                       PlotTmpl([f'rl1.resistor{i}.R' for i in '123'],\n                                callback=xylables_R\n                                ),\n                       PlotTmpl([f'master.CVi{s}' for s in 'dq0'],\n                                callback=xylables_i_dq0\n                                ),\n                       PlotTmpl([f'master.CVV{i}' for i in 'dq0'],\n                                callback=xylables_v_dq0\n                                ),\n                       PlotTmpl([f'rl1.inductor{i}.L' for i in '123'],\n                                callback=xylables_L\n                                )\n                   ],\n                   log_level=logging.INFO,\n                   viz_mode='episode',\n                   model_params={'rl1.resistor1.R': callback.load_step_resistance,\n                                 'rl1.resistor2.R': callback.load_step_resistance,\n                                 'rl1.resistor3.R': callback.load_step_resistance,\n                                 'rl1.inductor1.L': callback.load_step_inductance,\n                                 'rl1.inductor2.L': callback.load_step_inductance,\n                                 'rl1.inductor3.L': callback.load_step_inductance\n                                 },\n                   max_episode_steps=max_episode_steps,\n                   net=net,\n                   model_path='../../omg_grid/grid.network_singleInverter.fmu',\n                   history=FullHistory()\n                   )\n\n    #####################################\n    # Execution of the experiment\n    # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations)\n    runner = Runner(agent, env, callback)\n\n    runner.run(num_episodes, visualise=True)\n\n    #####################################\n    # Performance results and parameters as well as plots are stored in folder pipi_signleInvALT\n    agent.history.df.to_csv(save_folder + '/result.csv')\n\n    print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4})))\n    print('\\n Experiment finished with best set: \\n')\n    print('\\n  Current-Ki&Kp and voltage-Ki&Kp = {}'.format(\n        agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n    print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n    print('\\n\\nBest experiment results are plotted in the following:')\n\n    # Show best episode measurment (current) plot\n    best_env_plt = runner.run_data['best_env_plt']\n    for ii in range(len(best_env_plt)):\n        ax = best_env_plt[ii].axes[0]\n        ax.set_title('Best Episode')\n        best_env_plt[ii].show()\n        # best_env_plt[0].savefig('best_env_plt.png')\n\n#####################################\n# Calculation of Metrics\n# Here, the d- term of vdq is analysed\n\ndf_master_CVVd = env.history.df[['master.CVVd']]\n\nfrom metrics import Metrics\n\nvoltage_controller_metrics_vd = Metrics(df_master_CVVd, vd_ref[0], ts, max_episode_steps,\n                                        position_steady_state=position_steady_state,\n                                        position_settling_time=position_settling_time)\n\nd = {'Overshoot': [voltage_controller_metrics_vd.overshoot()],\n     'Rise Time/s ': [voltage_controller_metrics_vd.rise_time()],\n     'Settling Time/s ': [voltage_controller_metrics_vd.settling_time()],\n     'Root Mean Squared Error/V': [voltage_controller_metrics_vd.RMSE()],\n     'Steady State Error/V': [voltage_controller_metrics_vd.steady_state_error()]}\n\nprint()\ndf_metrics_vd = pd.DataFrame(data=d).T\ndf_metrics_vd.columns = ['Value']\nprint('Metrics of Vd')\nprint(df_metrics_vd)\n\n######IMPORTANT FOR THE SCORING MODEL INNER LEVEL##############################\n# Use the following code, to create a pkl-File in which the Dataframe is stored\n# df_metrics_vd.to_pickle(\"./df_metrics_vd_controller1.pkl\")\n# Maybe you need to replace 'controller1.pkl' with 'controller2.pkl'\n\n\n#####################################\n# Calculation of Metrics\n# Here, the q- term of vdq is analysed\n\ndf_master_CVVq = env.history.df[['master.CVVq']]\n\nfrom metrics import Metrics\n\nvoltage_controller_metrics_vq = Metrics(df_master_CVVq, vq_ref, ts, max_episode_steps)\n\nd = {'Root Mean Squared Error/V': [voltage_controller_metrics_vq.RMSE()],\n     'Steady State Error/V': [voltage_controller_metrics_vq.steady_state_error()],\n     'absolute Peak Value/V': [voltage_controller_metrics_vq.absolute_peak()]}\n\nprint()\ndf_metrics_vq = pd.DataFrame(data=d).T\ndf_metrics_vq.columns = ['Value']\nprint('Metrics of Vq')\nprint(df_metrics_vq)\n\n######IMPORTANT FOR THE SCORING MODEL INNER LEVEL##############################\n# Use the following code, to create a pkl-File in which the Dataframe is stored\n# df_metrics_vq.to_pickle(\"./df_metrics_vq_controller1.pkl\")\n# Maybe you need to replace 'controller1.pkl' with 'controller2.pkl'\n"
  },
  {
    "path": "experiments/testing_framework_control/tf_primarylevel_vdq_slavefreq.py",
    "content": "#####################################\n# Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters\n# Simulation setup: Single voltage forming inverter supplying an RL-load via an LC-filter\n# Controller: Cascaded PI-PI voltage and current controller gain parameters are optimized by SafeOpt\n# Testing Framework for primary level (frequency, voltage)\n# Definition of Benchmark, Implementation of load steps, Measurement of control metrics\n\n\nimport logging\nimport os\nfrom random import random\nfrom time import strftime, gmtime\nfrom typing import List\n\nimport GPy\nimport gym\nimport numpy as np\nimport pandas as pd\nfrom scipy.stats import linregress\n\nfrom openmodelica_microgrid_gym import Runner\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat\nfrom openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, \\\n    MultiPhaseDQ0PIPIController, PLLParams, InverseDroopParams, MultiPhaseDQCurrentController\nfrom openmodelica_microgrid_gym.env import PlotTmpl\nfrom openmodelica_microgrid_gym.execution import Callback\nfrom openmodelica_microgrid_gym.util import nested_map, FullHistory\n\npd.options.mode.chained_assignment = None  # default='warn'\n\n# Simulation definitions\nts = .5e-4  # simulation time step size / s\nmax_episode_steps = 3000  # number of simulation steps per episode\nnum_episodes = 1  # number of simulation episodes (i.e. SafeOpt iterations)\nv_DC = 1000  # DC-link voltage / V; will be set as model parameter in the FMU\nnomFreq = 50  # nominal grid frequency / Hz\nnomVoltPeak = 230 * 1.414  # nominal grid voltage / V\niLimit = 30  # inverter current limit / A\niNominal = 20  # nominal inverter current / A\nmu = 2  # factor for barrier function (see below)\nDroopGain = 40000.0  # virtual droop gain for active power / W/Hz\nQDroopGain = 1000.0  # virtual droop gain for reactive power / VA/V\nvd_ref = np.array([325, 0, 0])  # exemplary set point i.e. vd = 325, vq = 0, v0 = 0 / A\nvq_ref = 0  # vq = 0\nR = 20  # resistance value / Ohm\nL = 0.001  # resistance value / Henry\nload_step_resistance = 7.5  # load step / Ohm\nload_step_inductance = 0.015  # sets the inductive load step / H\nmovement_1_resistor = load_step_resistance  # first load step is negative\nmovement_2_resistor = (load_step_resistance if random() < 0.5\n                       else -1 * load_step_resistance) + movement_1_resistor  # randomly chosen load step\nmovement_1_inductance = load_step_inductance  # first load step is negative\nmovement_2_inductance = (load_step_inductance if movement_2_resistor >= 0\n                         else -load_step_inductance) + movement_1_inductance  # same sign as resistive load step\n\n# Files saves results and  resulting plots to the folder saves_VI_control_safeopt in the current directory\ncurrent_directory = os.getcwd()\nsave_folder = os.path.join(current_directory, r'saves_droop_control_safeopt')\nos.makedirs(save_folder, exist_ok=True)\n\n\n#####################################\n# Definition of Random Walk\n# Starting Value: R = 20 Ohm, L= 1 mH\n# Constant noise is created\n\ndef load_step_random_walk_resistor():\n    random_walk = [R]\n    for i in range(1, max_episode_steps):\n        movement = -0.01 if random() < 0.5 else 0.01  # constant slight and random fluctuation of load\n        value = random_walk[i - 1] + movement\n        random_walk.append(value)\n    return random_walk\n\n\ndef load_step_inductance_random_walk():\n    random_walk_inductance = [L]\n    for i in range(1, max_episode_steps):\n        movement = -0.000001 if random() < 0.5 else 0.000001  # constant slight and random fluctuation of load\n        value = random_walk_inductance[i - 1] + movement\n        random_walk_inductance.append(value)\n    return random_walk_inductance\n\n\nlist_resistor = load_step_random_walk_resistor()\nlist_inductance = load_step_inductance_random_walk()\n\n\nclass Reward:\n    def __init__(self):\n        self._idx = None\n\n    def set_idx(self, obs):\n        if self._idx is None:\n            self._idx = nested_map(\n                lambda n: obs.index(n),\n                [[f'slave.freq'],\n                 [f'master.CVV{s}' for s in 'dq0']])\n\n    def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:\n        \"\"\"\n        Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of\n        the used parameters.\n        Takes current measurement and set-points so calculate the mean-root control error\n\n        :param cols: list of variable names of the data\n        :param data: observation data from the environment (ControlVariables, e.g. currents and voltages)\n        :return: Error as negative reward\n        \"\"\"\n        self.set_idx(cols)\n        idx = self._idx\n        freq = data[idx[0]]\n\n        vdq0_master = data[idx[1]]  # 3 phase voltages at LC capacitor\n\n        # control error = mean-root-error (MRE) of reference minus measurement\n        # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides\n        #  better, i.e. more significant,  gradients)\n        error = np.sum((np.abs((nomFreq - freq)) / nomFreq) ** 0.5, axis=0) + \\\n                np.sum((np.abs(([nomVoltPeak, 0, 0] - vdq0_master)) / nomVoltPeak) ** 0.5, axis=0)\n\n        return -error.squeeze()\n\n\n#########################################################################################\n# Implementation of Load Steps\n# It is checked, if quantity reaches steady state\n# Values are stored in a databuffer\n# If the databuffer fulfills several conditions, the first load steps will be implemented\n# Programming 'on the fly'\n\nclass LoadstepCallback(Callback):\n    def __init__(self):\n        self.steady_state_reached = False\n        self.settling_time = None\n        self.databuffer_droop = None\n        self._idx = None\n        self.interval_check = False\n        self.steady_state_check = False\n        self.list_data = []\n        self.list_abcissa = []\n        self.upper_bound = 1.02 * nomFreq\n        self.lower_bound = 0.98 * nomFreq\n        self.databuffer_settling_time = 0\n        self.databuffer_length = 150\n        self.slope_info = None\n\n    def set_idx(self, obs):\n        if self._idx is None:\n            self._idx = nested_map(\n                lambda n: obs.index(n),\n                [[f'slave.freq'],\n                 [f'master.CVV{s}' for s in 'dq0']])\n\n    def reset(self):\n        self.databuffer_droop = np.empty(self.databuffer_length)  # creates a databuffer with a length of ten\n\n    def __call__(self, cols, obs):\n        self.set_idx(cols)\n        self.databuffer_droop[-1] = obs[self._idx[0][0]]\n        self.list_abcissa.append(len(self.list_data))\n        self.list_data.append(obs[self._idx[0][0]])  # the current value is appended to the list\n\n        # only when a certain number of values is available, slopeinfo is created --> used for conditions below\n        if len(self.list_data) > self.databuffer_length:\n            del self.list_abcissa[0]\n            self.slope_info = linregress(self.list_abcissa, self.databuffer_droop)\n\n        self.databuffer_droop = np.roll(self.databuffer_droop, -1)  # all values are shifted to the left by one\n\n        # condition: it is checked whether the current is outside of the interval --> settling time may not be reached\n        if (obs[self._idx[0][0]] < self.lower_bound or obs[\n            self._idx[0][0]] > self.upper_bound) and not self.steady_state_reached:\n            self.interval_check = False\n\n        # pre-condition: checks if observation is within the specified interval --> settling time may be reached\n        if self.lower_bound < obs[self._idx[0][0]] < self.upper_bound:\n            if not self.interval_check:\n                global position_settling_time\n                position_settling_time = len(self.list_data)\n                self.interval_check = True\n            if self.databuffer_droop.std() < 0.003 and np.abs(self.slope_info[0]) < 0.00001 and np.round(\n                    self.databuffer_droop.mean()) == nomFreq:  # if databuffer fulfills the conditions-->steady state\n                self.steady_state_reached = True\n                if not self.steady_state_check:\n                    global position_steady_state\n                    position_steady_state = len(self.list_data)  # stores the steady state position\n                    self.steady_state_check = True\n\n    # After steady state is reached, a fixed load step of + 7.5 Ohm is implemented\n    # After 3/4 of the simulation time, another random load step of +/- 7.5 Ohm is implemented\n    def load_step_resistance(self, t):\n        for i in range(1, len(list_resistor)):\n            if not self.steady_state_reached and t <= ts * i + ts:  # no steady state, no load step\n                return list_resistor[i]  # for every step, it contains values that fluctuate a little bit\n            elif self.steady_state_reached and t <= ts * i + ts and i <= max_episode_steps * 0.75:\n                return list_resistor[i] + movement_1_resistor  # when steady state reached, first load step\n            elif self.steady_state_reached and t <= ts * i + ts and i > max_episode_steps * 0.75:  # 75 % ts\n                return list_resistor[i] + movement_2_resistor\n\n    # After steady state is reached, a fixed load step of + 150 mH is implemented\n    # After 3/4 of the simulation time, another random load step of +/- 150 mH is implemented\n    def load_step_inductance(self, t):\n        for i in range(1, len(list_inductance)):\n            if not self.steady_state_reached and t <= ts * i + ts:  # no load step, no steady state\n                return list_inductance[i]  # for every step, it contains values that fluctuate a little bit\n            elif self.steady_state_reached and t <= ts * i + ts and i <= max_episode_steps * 0.75:\n                return list_inductance[i] + movement_1_inductance  # when steady state reached, first load step\n            elif self.steady_state_reached and t <= ts * i + ts and i > max_episode_steps * 0.75:  # 75 % ts\n                return list_inductance[i] + movement_2_inductance\n\n\nif __name__ == '__main__':\n    ctrl = []  # Empty dict which shall include all controllers\n\n    #####################################\n    # Definitions for the GP\n    prior_mean = 2  # mean factor of the GP prior mean which is multiplied with the first performance of the init set\n    noise_var = 0.001 ** 2  # measurement noise sigma_omega\n    prior_var = 0.2  # prior variance of the GP\n\n    # Choose all droop params as mutable parameters (below) and define bounds and lengthscale for both of them\n    bounds = [(0, 100000), (0, 100000), (0, 3000), (0, 100)]  # bounds on the input variable\n    lengthscale = [10000, 10000, 300., 10.]  # length scale for the parameter variation for the GP\n\n    # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times\n    # the initial performance: safe_threshold = 1.2 means. Performance measurement for optimization are seen as\n    # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!)\n    # parameter set\n    safe_threshold = 0.5\n    # minimal allowed performance (depending on episode return)\n    j_min = -2\n    # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop\n    # expanding points eventually.\n    # The following variable is multiplied with the first performance of the initial set by the factor below:\n    explore_threshold = 0\n\n    # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of\n    # limit exceeded\n    abort_reward = -10 * j_min\n\n    # Definition of the kernel\n    kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True)\n\n    #####################################\n    # Definition of the controllers\n\n    # Choose all droop parameter as mutable parameters\n    mutable_params = dict(pDroop_master=MutableFloat(30000.0), pDroop_slave=MutableFloat(30000.0),\n                          qDroop_master=MutableFloat(QDroopGain), qDroop_slave=MutableFloat(10))\n\n    # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for\n    # the filter and the nominal frequency\n    # Droop controller used to calculate the virtual frequency drop due to load changes\n    droop_param_master = DroopParams(mutable_params['pDroop_master'], 0.005, nomFreq)\n    # droop parameter used to react on frequency drop\n    droop_param_slave = InverseDroopParams(mutable_params['pDroop_slave'], ts, nomFreq, tau_filt=0.04)\n    # Droop characteristic for the reactive power VAR/Volt Var.s/Volt\n    # qDroop parameter used for virtual voltage drop\n    qdroop_param_master = DroopParams(mutable_params['qDroop_master'], 0.002, nomVoltPeak)\n    # Droop characteristic for the reactive power VAR/Volt Var.s/Volt\n    qdroop_param_slave = InverseDroopParams(mutable_params['qDroop_slave'], ts, nomVoltPeak, tau_filt=0.01)\n\n    ###############\n    # define Master\n    voltage_dqp_iparams = PI_params(kP=0.025, kI=60, limits=(-iLimit, iLimit))  # kp=0.025\n    # Current control PI gain parameters for the voltage sourcing inverter\n    current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1))  # kp=0.012\n\n    # Define a current sourcing inverter as master inverter using the pi and droop parameters from above\n    ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param_master,\n                                            qdroop_param_master, ts_sim=ts, ts_ctrl=2 * ts, name='master'))\n\n    ###############\n    # define slave\n    current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1))  #\n    # PI gain parameters for the PLL in the current forming inverter\n    pll_params = PLLParams(kP=10, kI=200, limits=(-10000, 10000), f_nom=nomFreq)\n    # Droop characteristic for the active power Watts/Hz, W.s/Hz\n\n    # Add to dict\n    ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, iLimit, droop_param_slave,\n                                              qdroop_param_slave, ts_sim=ts, name='slave'))\n\n    #####################################\n    # Definition of the optimization agent\n    # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example\n    # Arguments described above\n    # History is used to store results\n    agent = SafeOptAgent(mutable_params,\n                         abort_reward,\n                         j_min,\n                         kernel,\n                         dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean,\n                              safe_threshold=safe_threshold, explore_threshold=explore_threshold),\n                         ctrl,\n                         {'master': [[f'lc1.inductor{k}.i' for k in '123'],\n                                     [f'lc1.capacitor{k}.v' for k in '123'],\n                                     ],\n                          'slave': [[f'lcl1.inductor{k}.i' for k in '123'],\n                                    [f'lcl1.capacitor{k}.v' for k in '123'],\n                                    np.zeros(3)]},\n                         history=FullHistory()\n                         )\n\n\n    #####################################\n    # Definition of the environment using a FMU created by OpenModelica\n    # (https://www.openmodelica.org/)\n    # Using two inverters supplying a load\n    # - using the reward function described above as callable in the env\n    # - viz_cols used to choose which measurement values should be displayed\n    #   Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide)\n    #   figures are stored to folder\n    # - inputs to the models are the connection points to the inverters (see user guide for more details)\n    # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors for each\n    #   inverter and the 3 currents through the load\n\n    def xylables_i(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$i_{\\mathrm{abc}}\\,/\\,\\mathrm{A}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/Inductor_currents' + time + '.pdf')\n\n\n    def xylables_v_abc(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$v_{\\mathrm{abc}}\\,/\\,\\mathrm{V}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/abc_voltage' + time + '.pdf')\n\n\n    def xylables_v_dq0(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$v_{\\mathrm{dq0}}\\,/\\,\\mathrm{V}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/dq0_voltage' + time + '.pdf')\n\n\n    def xylables_P_master(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$P_{\\mathrm{master}}\\,/\\,\\mathrm{W}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/P_master' + time + '.pdf')\n\n\n    def xylables_P_slave(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$P_{\\mathrm{slave}}\\,/\\,\\mathrm{W}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/P_slave' + time + '.pdf')\n\n\n    def xylables_freq(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$f_{\\mathrm{slave}}\\,/\\,\\mathrm{Hz}$')\n        ax.grid(which='both')\n        time = strftime(\"%Y-%m-%d %H_%M_%S\", gmtime())\n        fig.savefig(save_folder + '/f_slave' + time + '.pdf')\n        fig.show()\n\n\n    def xylables_R(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')\n        ax.set_ylabel('$R_{\\mathrm{123}}\\,/\\,\\mathrm{\\u03A9}$')\n        ax.grid(which='both')\n\n\n    def xylables_L(fig):\n        ax = fig.gca()\n        ax.set_xlabel(r'$t\\,/\\,\\mathrm{s}$')  # zeit\n        ax.set_ylabel('$L_{\\mathrm{123}}\\,/\\,\\mathrm{H}$')\n        ax.grid(which='both')\n\n\n    callback = LoadstepCallback()\n\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   reward_fun=Reward().rew_fun,\n                   viz_cols=[\n                       PlotTmpl([f'slave.freq'],\n                                callback=xylables_freq\n                                ),\n                       PlotTmpl([f'master.CVV{i}' for i in 'dq0'],\n                                callback=xylables_v_dq0\n                                ),\n                       PlotTmpl([f'rl1.resistor{i}.R' for i in '123'],  # Plot Widerstand RL\n                                callback=xylables_R\n                                ),\n                       PlotTmpl([f'rl1.inductor{i}.L' for i in '123'],  # Plot Widerstand RL\n                                callback=xylables_L\n                                ),\n                   ],\n                   log_level=logging.INFO,\n                   viz_mode='episode',\n                   max_episode_steps=max_episode_steps,\n                   model_params={'rl1.resistor1.R': callback.load_step_resistance,\n                                 'rl1.resistor2.R': callback.load_step_resistance,\n                                 'rl1.resistor3.R': callback.load_step_resistance,\n                                 'rl1.inductor1.L': callback.load_step_inductance,\n                                 'rl1.inductor2.L': callback.load_step_inductance,\n                                 'rl1.inductor3.L': callback.load_step_inductance\n                                 },\n                   model_path='../../omg_grid/grid.network.fmu',\n                   net='net_RL_load.yaml',\n                   history=FullHistory()\n                   )\n\n    #####################################\n    # Execution of the experiment\n    # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations)\n    runner = Runner(agent, env, callback)\n\n    runner.run(num_episodes, visualise=True)\n\n    #####################################\n    # Performance results and Parameters as well as plots are stored in folder saves_droop\n    agent.history.df.to_csv(save_folder + '/result.csv')\n\n    print('\\n Experiment finished with best set: \\n\\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4})))\n    print('\\n Experiment finished with best set: \\n')\n    print('\\n  [pDroop_master, pDroop_slave, qDroop_master, qDroop_slave]= {}'.format(\n        agent.history.df.at[np.argmax(agent.history.df['J']), 'Params']))\n    print('  Resulting in a performance of J = {}'.format(np.max(agent.history.df['J'])))\n    print('\\n\\nBest experiment results are plotted in the following:')\n    print()\n\n#####################################\n# Calculation of Metrics\n# Here, the d- term of vd is analysed\n\ndf_master_CVVd = env.history.df[['master.CVVd']]\nfrom metrics import Metrics\n\nvoltage_controller_metrics_vd = Metrics(df_master_CVVd, vd_ref[0], ts, max_episode_steps,\n                                        position_steady_state=position_steady_state,\n                                        position_settling_time=position_settling_time)\n\nd = {'Overshoot': [voltage_controller_metrics_vd.overshoot()],\n     'Rise Time/s ': [voltage_controller_metrics_vd.rise_time()],\n     'Settling Time/s ': [voltage_controller_metrics_vd.settling_time_vd_droop()],\n     'Root Mean Squared Error/V': [voltage_controller_metrics_vd.RMSE()],\n     'Steady State Error/V': [voltage_controller_metrics_vd.steady_state_error()]}\n\nprint(\"\\n\")\nprint()\ndf_metrics_vd = pd.DataFrame(data=d).T\ndf_metrics_vd.columns = ['Value']\nprint('Metrics of Vd')\nprint(df_metrics_vd)\n\n######IMPORTANT FOR THE SCORING MODEL PRIMARY LEVEL##############################\n# Use the following code, to create a pkl-File in which the Dataframe is stored\n# df_metrics_vd.to_pickle(\"./df_metrics_vd_controller1_droop.pkl\")\n# Maybe you need to replace 'controller1_droop.pkl' with 'controller2_droop.pkl'\n\n\n#####################################\n# Calculation of Metrics\n# Here, the q-term of vdq is analysed\ndf_master_CVVq = env.history.df[['master.CVVq']]\n\nfrom metrics import Metrics\n\nvoltage_controller_metrics_vq = Metrics(df_master_CVVq, vq_ref, ts, max_episode_steps,\n                                        position_steady_state=position_steady_state,\n                                        position_settling_time=position_settling_time)\n\nd = {'Root Mean Squared Error/V': [voltage_controller_metrics_vq.RMSE()],\n     'Steady State Error/V': [voltage_controller_metrics_vq.steady_state_error()],\n     'Peak Value/V': [voltage_controller_metrics_vq.absolute_peak()]}\n\nprint(\"\\n\")\nprint()\ndf_metrics_vq = pd.DataFrame(data=d).T\ndf_metrics_vq.columns = ['Value']\nprint('Metrics of Vq')\nprint(df_metrics_vq)\n\n######IMPORTANT FOR THE SCORING MODEL PRIMARY LEVEL##############################\n# Use the following code, to create a pkl-File in which the Dataframe is stored\n# df_metrics_vq.to_pickle(\"./df_metrics_vq_controller1_droop.pkl\")\n# Maybe you need to replace 'controller1_droop.pkl' with 'controller2_droop.pkl'\n\n\n#####################################\n# Calculation of Metrics\n# Here, the slave frequency is analysed\n\ndf_slave_frequency = env.history.df[['slave.freq']]\nfrom metrics import Metrics\n\nfrequency_controller_metrics = Metrics(df_slave_frequency, nomFreq, ts, max_episode_steps,\n                                       position_steady_state=position_steady_state,\n                                       position_settling_time=position_settling_time)\n\nd = {'Overshoot': [frequency_controller_metrics.overshoot()],\n     'Rise Time/s ': [frequency_controller_metrics.rise_time()],\n     'Settling Time/s ': [frequency_controller_metrics.settling_time()],\n     'Root Mean Squared Error/Hz': [frequency_controller_metrics.RMSE()],\n     'Steady State Error/Hz': [frequency_controller_metrics.steady_state_error()]}\n\nprint(\"\\n\")\nprint()\ndf_metrics_slave_f = pd.DataFrame(data=d).T\ndf_metrics_slave_f.columns = ['Value']\nprint('Metrics of Slave Frequency')\nprint(df_metrics_slave_f)\n\n######IMPORTANT FOR THE SCORING MODEL PRIMARY LEVEL##############################\n# Use the following code, to create a pkl-File in which the Dataframe is stored\n# df_metrics_slave_f.to_pickle(\"./df_metrics_slave_f_controller1_droop.pkl\")\n# Maybe you need to replace 'controller1.pkl' with 'controller2.pkl'\n"
  },
  {
    "path": "net/net.yaml",
    "content": "v_nom: 230*sqrt(2)\nfreq_nom: 50\nts: .5e-4\n\ncomponents:\n  inv1:\n    id: inverter1\n    #i_nom: 20\n    #i_lim: 30\n    #v_DC: 1000\n    cls: MasterInverter\n    v_noise:\n      fun:\n        normal: # np.random.*\n          loc: 0\n          scale: 0.001\n    i_noise:\n      fun:\n        normal: # np.random.*\n          loc: 0\n          scale: 0.001\n      clip: # np.clip\n        a_min: -1\n        a_max: 1\n    in:\n      u: [ i1p1, i1p2, i1p3 ]    # names of the inputs\n    out:\n      v: [ lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v ]\n      i: [ lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i ]\n    # iref: [0,0,0]\n    # vref: [1,0,0]\n  inv2:\n    id: inverter2\n    cls: SlaveInverter\n    #pll:\n    #  kP: 10\n    #  kI: 200\n    in:\n      u: [ i2p1, i2p2, i2p3 ]\n    out:\n      v: [ lcl1.capacitor1.v, lcl1.capacitor2.v, lcl1.capacitor3.v ]\n      i: [ lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i ]\n    #i_ref: [15,0,0]\n  load:\n    id: rl1\n    cls: Load\n    out:\n      i: [ .inductor1.i, .inductor2.i, .inductor3.i ]\n      R: [ .resistor1.R, .resistor2.R, .resistor3.R ]\n\n"
  },
  {
    "path": "net/net_dupinputs.yaml",
    "content": "v_nom: 230\n#freq_nom: 50\nts: .5e-4\n\ncomponents:\n  inv1:\n    #i_nom: 20\n    #i_lim: 30\n    #v_DC: 1000\n    id: inverter1\n    cls: MasterInverter\n    in:\n      u: [i1p1, i1p2, i1p3]    # names of the inputs\n    out:\n      v: [lc1.capacitor1.v,lc1.capacitor2.v, lc1.capacitor3.v]\n      i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i]\n    # iref: [0,0,0]\n    # vref: [1,0,0]\n  inv2:\n    id: inverter2\n    cls: SlaveInverter\n    #pll:\n    #  kP: 10\n    #  kI: 200\n    in:\n      u: [i1p1, i1p2, i1p3]    # names of the inputs\n    out:\n      v: [lcl1.capacitor1.v,lcl1.capacitor2.v, lcl1.capacitor3.v]\n      i: [lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i]\n    i_ref: [15,0,0]\n  load:\n    id: rl1\n    cls: Load\n    out:\n      i: [.inductor1.i, .inductor2.i, .inductor3.i]\n"
  },
  {
    "path": "net/net_single-inv-Paper_Loadstep.yaml",
    "content": "v_nom: 169.7\nfreq_nom: 60\nts: 1e-4\n\ncomponents:\n  inv1:\n    id: inverter1\n    #i_nom: 20\n    #i_lim: 30\n    v_DC: 600\n    v_noise:\n      fun:\n        normal: # np.random.*\n          loc: 0\n          scale: 0.4\n        clip:\n          a_min: 0.3\n          a_max: 0.5\n      i_noise:\n        fun:\n          normal: # np.random.*\n            loc: 0\n            scale: 0.0018\n        clip: # np.clip\n          a_min: 0.0005\n          a_max: 0.32\n    cls: MasterInverter\n    in:\n      u: [ i1p1, i1p2, i1p3 ]    # names of the inputs\n    out:\n      v: [ lc.capacitor1.v, lc.capacitor2.v, lc.capacitor3.v ]\n      i: [ lc.inductor1.i, lc.inductor2.i, lc.inductor3.i ]\n\n    # iref: [0,0,0]\n    # vref: [1,0,0]\n\n  load:\n    id: r_load\n    cls: Load\n    out:\n      i: [ .resistor1.R, .resistor2.R, .resistor3.R, .resistor1.i, .resistor2.i, .resistor3.i ]\n"
  },
  {
    "path": "net/net_single-inv-curr.yaml",
    "content": "v_nom: 230*sqrt(2)\n#freq_nom: 50\nts: .5e-4\n\ncomponents:\n  inv1:\n    id: inverter1\n    #i_nom: 20\n    #i_lim: 30\n    v_DC: 1000\n    cls: MasterInverterCurrentSourcing\n    in:\n      u: [ i1p1, i1p2, i1p3 ]    # names of the inputs\n    out:\n      v: [ lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v ]\n      i: [ lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i ]\n    i_ref: [ 15,0,0 ]\n    # v_ref: [1,0,0]\n\n"
  },
  {
    "path": "net/net_single-inv-curr_Paper_SC.yaml",
    "content": "v_nom: 230*sqrt(2)\nfreq_nom: 60\nts: 1e-4\n\ncomponents:\n  inv1:\n    id: inverter1\n    #i_nom: 20\n    #i_lim: 30\n    v_DC: 600\n    i_noise:\n      fun:\n        normal: # np.random.*\n          loc: 0\n          scale: 0.0018\n      clip: # np.clip\n        a_min: 0.0005\n        a_max: 0.32\n    cls: MasterInverterCurrentSourcing\n    in:\n      u: [ i1p1, i1p2, i1p3 ]    # names of the inputs\n    out:\n      v: [ lc.capacitor1.v, lc.capacitor2.v, lc.capacitor3.v ]\n      i: [ lc.inductor1.i, lc.inductor2.i, lc.inductor3.i ]\n\n    # iref: [0,0,0]\n    # vref: [1,0,0]\n\n"
  },
  {
    "path": "net/net_single-inv-volt.yaml",
    "content": "v_nom: 230*sqrt(2)\nfreq_nom: 50\nts: .5e-4\n\ncomponents:\n  inv1:\n    id: inverter1\n    #i_nom: 20\n    #i_lim: 30\n    v_DC: 1000\n    cls: MasterInverterCurrentSourcing\n    in:\n      u: [ i1p1, i1p2, i1p3 ]    # names of the inputs\n    out:\n      v: [ lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v ]\n      i: [ lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i ]\n"
  },
  {
    "path": "net/net_singleinverter.yaml",
    "content": "v_nom: 230\n#freq_nom: 50\nts: .5e-4\n\ncomponents:\n  inv1:\n    cls: MasterInverter\n    id: inverter1\n    #i_nom: 20\n    #i_lim: 30\n    #v_DC: 1000\n    in:\n      u: [i1p1, i1p2, i1p3]    # names of the inputs\n    out:\n      v: [lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v]\n      i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i]\n    # iref: [0,0,0]\n    # vref: [1,0,0]\n  load:\n    id: rl1\n    cls: Load\n    out:\n      i: [.inductor1.i, .inductor2.i, .inductor3.i]\n"
  },
  {
    "path": "net/net_static_droop_controller.yaml",
    "content": "# Simulation definitions\nts : 0.5e-4\n\nfreq_nom: 50  # grid frequency / Hz\nv_nom: 325.22 # nominal grid voltage / V\n\n\ncomponents:\n  inv1:\n    cls: MasterInverter\n    id: inverter1\n    i_lim: 30  # current limit / A\n    i_nom: 20  # nominal current / A\n    in:\n      u: [i1p1,i1p2,i1p3]\n    out:\n      v: [lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v]\n      i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i]\n\n\n  inv2:\n    cls: SlaveInverter\n    id: inverter2\n    i_lim: 30  # current limit / A\n    i_nom: 20  # nominal current / A\n    in:\n      u: [i2p1,i2p2,i2p3]\n    out:\n      v: [lcl1.capacitor1.v, lcl1.capacitor2.v, lcl1.capacitor3.v]\n      i: [lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i]\n\n  load:\n    id: rl1\n    cls: Load\n    out:\n      i: [.inductor1.i, .inductor2.i, .inductor3.i]\n"
  },
  {
    "path": "net/net_test.yaml",
    "content": "v_nom: 230*sqrt(2)\nfreq_nom: 50\nts: 1e-4\n\ncomponents:\n  inv1:\n    id: inverter1\n    #i_nom: 20\n    #i_lim: 30\n    v_DC: 1000\n    cls: MasterInverter\n    in:\n      u: [i1p1, i1p2, i1p3]    # names of the inputs\n    out:\n      v: [lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v]\n      i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i]\n    # iref: [0,0,0]\n    # vref: [1,0,0]\n  inv2:\n    id: inverter2\n    cls: SlaveInverter\n    #pll:\n    #  kP: 10\n    #  kI: 200\n    in:\n      u: [i2p1, i2p2, i2p3]\n    out:\n      v: [lcl1.capacitor1.v, lcl1.capacitor2.v, lcl1.capacitor3.v]\n      i: [lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i]\n    #i_ref: [15,0,0]"
  },
  {
    "path": "net/net_valid.yaml",
    "content": "v_nom: 230\n#freq_nom: 50\nts: .5e-4\n\ncomponents:\n  inv1:\n    #i_nom: 20\n    #i_lim: 30\n    #v_DC: 1000\n    id: inverter1\n    cls: MasterInverter\n    in:\n      u: [i1p1, i1p2, i1p3]    # names of the inputs\n    out:\n      v: [lc1.capacitor1.v,lc1.capacitor2.v, lc1.capacitor3.v]\n      i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i]\n    # iref: [0,0,0]\n    # vref: [1,0,0]\n  inv2:\n    id: inverter2\n    cls: SlaveInverter\n    #pll:\n    #  kP: 10\n    #  kI: 200\n    in:\n      u: [i2p1, i2p2, i2p3]\n    out:\n      v: [lcl1.capacitor1.v,lcl1.capacitor2.v, lcl1.capacitor3.v]\n      i: [lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i]\n    i_ref: [15,0,0]\n  load:\n    id: rl1\n    cls: Load\n    out:\n      i: [.inductor1.i, .inductor2.i, .inductor3.i]\n"
  },
  {
    "path": "omg_grid/ActiveLoads/ActiveLoad.mo",
    "content": "within omg_grid.ActiveLoads;\n\nmodel ActiveLoad\n  parameter SI.Power p_ref(start = 5000);\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.PLLs.PLL_DQ pll_dq annotation(\n    Placement(visible = true, transformation(origin = {-50, 76}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Transformations.ABC2DQ_Currents abc2dq_current annotation(\n    Placement(visible = true, transformation(origin = {-58, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Inverters.Inverter inverter annotation(\n    Placement(visible = true, transformation(origin = {-4, -12}, extent = {{-10, -10}, {10, 10}}, rotation = 90)));\n  Modelica.Blocks.Continuous.LimPID PID2(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation(\n    Placement(visible = true, transformation(origin = {34, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Transformations.DQ2ABC dq2abc annotation(\n    Placement(visible = true, transformation(origin = {68, -66}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.LimPID PID1(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation(\n    Placement(visible = true, transformation(origin = {34, -78}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.LimPID pid(Td = 0, Ti = 0.006, k = 0.023, limitsAtInit = true, yMax = 30) annotation(\n    Placement(visible = true, transformation(origin = {-38, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.LimPID PID(Td = 0, Ti = 0.006, k = 0.023, limitsAtInit = true, yMax = 30) annotation(\n    Placement(visible = true, transformation(origin = {-46, -80}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression(y = 1000 / 325) annotation(\n    Placement(visible = true, transformation(origin = {-84, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression2(y = 1000 / 325) annotation(\n    Placement(visible = true, transformation(origin = {-82, -72}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nomg_grid.Filter.IdealFilter.LC lc(L1 = 0.004, L2 = 0.004, L3 = 0.004)  annotation(\n    Placement(visible = true, transformation(origin = {-24, 8}, extent = {{-10, -10}, {10, 10}}, rotation = 180)));\nModelica.Blocks.Sources.RealExpression realExpression1(y = 1) annotation(\n    Placement(visible = true, transformation(origin = {-4, -58}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nModelica.Blocks.Sources.RealExpression realExpression3(y = 0) annotation(\n    Placement(visible = true, transformation(origin = {-6, -72}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nomg_grid.Components.StartValues startvalues(startTime = 0.1)  annotation(\n    Placement(visible = true, transformation(origin = {38, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 180)));\nomg_grid.Components.StartValues startvalues1(startTime = 0.1)  annotation(\n    Placement(visible = true, transformation(origin = {38, 34}, extent = {{-10, -10}, {10, 10}}, rotation = 180)));\nomg_grid.Components.StartValues startvalues2(startTime = 0.1)  annotation(\n    Placement(visible = true, transformation(origin = {38, 66}, extent = {{-10, -10}, {10, 10}}, rotation = 180)));\nequation\n  connect(pin1, pll_dq.c) annotation(\n    Line(points = {{-100, -60}, {-80, -60}, {-80, 70}, {-60, 70}}, color = {0, 0, 255}));\n  connect(pin2, pll_dq.b) annotation(\n    Line(points = {{-100, 0}, {-84, 0}, {-84, 76}, {-60, 76}}, color = {0, 0, 255}));\n  connect(pin3, pll_dq.a) annotation(\n    Line(points = {{-100, 60}, {-88, 60}, {-88, 82}, {-60, 82}}, color = {0, 0, 255}));\n  connect(pin1, abc2dq_current.c) annotation(\n    Line(points = {{-100, -60}, {-80, -60}, {-80, -6}, {-68, -6}, {-68, -6}}, color = {0, 0, 255}));\n  connect(pin2, abc2dq_current.b) annotation(\n    Line(points = {{-100, 0}, {-68, 0}, {-68, 0}, {-68, 0}}, color = {0, 0, 255}));\n  connect(pin3, abc2dq_current.a) annotation(\n    Line(points = {{-100, 60}, {-88, 60}, {-88, 6}, {-68, 6}, {-68, 6}}, color = {0, 0, 255}));\n  connect(pll_d.theta, abc2dq_current.theta) annotation(\n    Line(points = {{-38, 82}, {-30, 82}, {-30, 24}, {-58, 24}, {-58, 12}, {-58, 12}}, color = {0, 0, 127}));\n  connect(PID2.y, dq2abc.d) annotation(\n    Line(points = {{46, -48}, {52, -48}, {52, -62}, {58, -62}}, color = {0, 0, 127}));\n  connect(PID1.y, dq2abc.q) annotation(\n    Line(points = {{46, -78}, {52, -78}, {52, -70}, {58, -70}}, color = {0, 0, 127}));\n  connect(abc2dq_current.d, pid.u_m) annotation(\n    Line(points = {{-62, -10}, {-62, -10}, {-62, -64}, {-38, -64}, {-38, -60}, {-38, -60}}, color = {0, 0, 127}));\n  connect(abc2dq_current.q, PID.u_m) annotation(\n    Line(points = {{-54, -10}, {-54, -10}, {-54, -16}, {-64, -16}, {-64, -96}, {-46, -96}, {-46, -92}, {-46, -92}}, color = {0, 0, 127}));\nconnect(realExpression2.y, pid.u_s) annotation(\n    Line(points = {{-70, -72}, {-68, -72}, {-68, -48}, {-50, -48}, {-50, -48}}, color = {0, 0, 127}));\nconnect(realExpression.y, PID.u_s) annotation(\n    Line(points = {{-72, -86}, {-70, -86}, {-70, -80}, {-58, -80}, {-58, -80}}, color = {0, 0, 127}));\n  connect(pll_dq.d, PID2.u_m) annotation(\n    Line(points = {{-38, 76}, {16, 76}, {16, -64}, {34, -64}, {34, -60}, {34, -60}}, color = {0, 0, 127}));\n  connect(pll_dq.q, PID1.u_m) annotation(\n    Line(points = {{-38, 70}, {14, 70}, {14, -94}, {34, -94}, {34, -90}, {34, -90}}, color = {0, 0, 127}));\n  connect(pll_dq.theta, dq2abc.theta) annotation(\n    Line(points = {{-38, 82}, {64, 82}, {64, -54}, {64, -54}}, color = {0, 0, 127}));\nconnect(abc2dq_current.pin1, lc.pin4) annotation(\n    Line(points = {{-48, -6}, {-44, -6}, {-44, 14}, {-34, 14}}, color = {0, 0, 255}));\nconnect(abc2dq_current.pin2, lc.pin5) annotation(\n    Line(points = {{-48, 0}, {-42, 0}, {-42, 8}, {-34, 8}}, color = {0, 0, 255}));\nconnect(abc2dq_current.pin3, lc.pin6) annotation(\n    Line(points = {{-48, 6}, {-38, 6}, {-38, 2}, {-34, 2}, {-34, 2}}, color = {0, 0, 255}));\nconnect(lc.pin3, inverter.pin3) annotation(\n    Line(points = {{-14, 2}, {-10, 2}, {-10, -2}, {-10, -2}}, color = {0, 0, 255}));\nconnect(lc.pin2, inverter.pin2) annotation(\n    Line(points = {{-14, 8}, {-4, 8}, {-4, -2}, {-4, -2}}, color = {0, 0, 255}));\nconnect(lc.pin1, inverter.pin1) annotation(\n    Line(points = {{-14, 14}, {2, 14}, {2, -2}, {2, -2}}, color = {0, 0, 255}));\nconnect(realExpression1.y, PID2.u_s) annotation(\n    Line(points = {{8, -58}, {8, -58}, {8, -48}, {22, -48}, {22, -48}}, color = {0, 0, 127}));\nconnect(realExpression3.y, PID1.u_s) annotation(\n    Line(points = {{6, -72}, {16, -72}, {16, -78}, {20, -78}, {20, -78}, {22, -78}}, color = {0, 0, 127}));\nconnect(dq2abc.c, startvalues2.u) annotation(\n    Line(points = {{78, -72}, {92, -72}, {92, 66}, {50, 66}, {50, 66}}, color = {0, 0, 127}));\nconnect(inverter.u1, startvalues2.y) annotation(\n    Line(points = {{2, -22}, {2, -22}, {2, -30}, {18, -30}, {18, 66}, {28, 66}, {28, 66}}, color = {0, 0, 127}));\nconnect(dq2abc.b, startvalues1.u) annotation(\n    Line(points = {{78, -66}, {86, -66}, {86, 34}, {52, 34}, {52, 34}, {50, 34}}, color = {0, 0, 127}));\nconnect(dq2abc.a, startvalues.u) annotation(\n    Line(points = {{78, -60}, {80, -60}, {80, 0}, {50, 0}, {50, 0}}, color = {0, 0, 127}));\nconnect(startvalues1.y, inverter.u2) annotation(\n    Line(points = {{28, 34}, {20, 34}, {20, -32}, {-4, -32}, {-4, -22}, {-4, -22}}, color = {0, 0, 127}));\nconnect(inverter.u3, startvalues.y) annotation(\n    Line(points = {{-10, -22}, {-10, -22}, {-10, -34}, {22, -34}, {22, 0}, {28, 0}, {28, 0}}, color = {0, 0, 127}));\nend ActiveLoad;\n"
  },
  {
    "path": "omg_grid/ActiveLoads/package.mo",
    "content": "within omg_grid;\npackage ActiveLoads \n//extends Modelica.Icons.Package;\n\nannotation (Icon(coordinateSystem(preserveAspectRatio=false,\n                extent={{-100,-100},{100,100}}), graphics={\n          Line(\n            origin={7,47},\n            points={{-84,-6},{-52,-6}}),\n          Rectangle(\n            origin={59,53},\n            fillColor = {255,255,255},\n            fillPattern = FillPattern.Solid,\n            extent={{-104,-63},{-64,7}}),\n          Rectangle(\n            origin={146,34},\n            fillColor = {255,255,255},\n            fillPattern = FillPattern.Solid,\n            extent={{-104,-63},{-64,7}}),\n          Line(\n            origin={7,15},\n            points={{-84,-6},{-52,-6}}),\n          Line(\n            origin={79,30},\n            points={{-84,-6},{-37,-6}}),\n        Line(\n          points={{42,-12},{17,-12},{17,-54},{-71,-54}})}));\nend ActiveLoads;\n"
  },
  {
    "path": "omg_grid/ActiveLoads/package.order",
    "content": "ActiveLoad\n"
  },
  {
    "path": "omg_grid/Components/PhaseAngle.mo",
    "content": "within omg_grid.Components;\n\nmodel PhaseAngle\n  parameter Real freq(start = 50);\n  Modelica.Blocks.Math.Gain deg2rad(k = 2 * 3.1416) annotation(\n    Placement(visible = true, transformation(origin = {14, 0}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Sources.Constant f_nom(k = freq) annotation(\n    Placement(visible = true, transformation(origin = {-18, 0}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Continuous.Integrator f2theta(y_start = 0) annotation(\n    Placement(visible = true, transformation(origin = {0, 0}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealOutput theta annotation(\n    Placement(visible = true, transformation(origin = {108, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {108, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(f_nom.y, f2theta.u) annotation(\n    Line(points = {{-14, 0}, {-5, 0}}, color = {0, 0, 127}));\n  connect(f2theta.y, deg2rad.u) annotation(\n    Line(points = {{4, 0}, {9, 0}}, color = {0, 0, 127}));\n  connect(deg2rad.y, theta) annotation(\n    Line(points = {{18, 0}, {108, 0}}, color = {0, 0, 127}));\nend PhaseAngle;\n"
  },
  {
    "path": "omg_grid/Components/StartValues.mo",
    "content": "within omg_grid.Components;\n\nblock StartValues \"Output the input signal filtered with a low pass Butterworth filter of any order\"\n  extends Modelica.Blocks.Interfaces.SISO;\n  Real z;\n  Real compare;\n  Real value;\n  parameter Real startTime(start = 0.02);\n  Modelica.Blocks.Sources.RealExpression realExpression(y = 1) annotation(\n    Placement(visible = true, transformation(origin = {-62, 36}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.Integrator integrator annotation(\n    Placement(visible = true, transformation(origin = {-16, 36}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  z = (integrator.y - startTime) * 100000;\n  compare = max(0, z);\n  value = min(compare, 1);\n  y = u * value;\n  connect(realExpression.y, integrator.u) annotation(\n    Line(points = {{-50, 36}, {-28, 36}, {-28, 36}, {-28, 36}}, color = {0, 0, 127}));\n  annotation(\n    Icon(coordinateSystem(preserveAspectRatio = true, extent = {{-100, -100}, {100, 100}}), graphics = {Rectangle(extent = {{-70, 30}, {70, -30}}, lineColor = {0, 0, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Line(points = {{-90, 0}, {-70, 0}}, color = {0, 0, 255}), Line(points = {{70, 0}, {90, 0}}, color = {0, 0, 255}), Line(visible = useHeatPort, points = {{0, -100}, {0, -30}}, color = {127, 0, 0}, pattern = LinePattern.Dot), Text(extent = {{-150, 90}, {150, 50}}, textString = \"%name\", lineColor = {0, 0, 255})}));\nend StartValues;\n"
  },
  {
    "path": "omg_grid/Components/package.mo",
    "content": "within omg_grid;\npackage Components \n\n\nextends Modelica.Icons.UtilitiesPackage;\nend Components;\n"
  },
  {
    "path": "omg_grid/Components/package.order",
    "content": "PhaseAngle\nStartValues\n"
  },
  {
    "path": "omg_grid/Examples/ControlledNetworkSC.mo",
    "content": "within omg_grid.Examples;\n\nmodel ControlledNetworkSC\n  omg_grid.Inverters.Inverter inverter1(v_DC = 5000)  annotation(\n    Placement(visible = true, transformation(origin = {-82, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LC lc1(L1 = 0.001, L2 = 0.001, L3 = 0.001) annotation(\n    Placement(visible = true, transformation(origin = {-26, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.PLLs.PLL_DQ pll_dq annotation(\n    Placement(visible = true, transformation(origin = {2, 62}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Transformations.ABC2DQ_Currents abc2dq_current annotation(\n    Placement(visible = true, transformation(origin = {-54, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression(y = -350) annotation(\n    Placement(visible = true, transformation(origin = {2, 90}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression1(y = 230 * 1.41427) annotation(\n    Placement(visible = true, transformation(origin = {5, 77}, extent = {{-13, -11}, {13, 11}}, rotation = 0)));\n  omg_grid.Transformations.DQ2ABC dq2abc annotation(\n    Placement(visible = true, transformation(origin = {36, -18}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.LimPID pid(Td = 0, Ti = 0.006, k = 0.3, limitsAtInit = true, yMax = 150) annotation(\n    Placement(visible = true, transformation(origin = {78, 86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.LimPID PID(Td = 0, Ti = 0.06, k = 0.3, limitsAtInit = true, yMax = 50) annotation(\n    Placement(visible = true, transformation(origin = {44, 74}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.LimPID PID1(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation(\n    Placement(visible = true, transformation(origin = {-34, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.LimPID PID2(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation(\n    Placement(visible = true, transformation(origin = {-34, -4}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Components.PhaseAngle angle annotation(\n    Placement(visible = true, transformation(origin = {19, 5}, extent = {{-5, -5}, {5, 5}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground annotation(\n    Placement(visible = true, transformation(origin = {18, 24}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(dq2abc.a, inverter1.u3) annotation(\n    Line(points = {{46, -12}, {78, -12}, {78, -66}, {-116, -66}, {-116, 36}, {-92, 36}}, color = {0, 0, 127}));\n  connect(dq2abc.b, inverter1.u2) annotation(\n    Line(points = {{46, -18}, {72, -18}, {72, -60}, {-110, -60}, {-110, 30}, {-92, 30}}, color = {0, 0, 127}));\n  connect(inverter1.u1, dq2abc.c) annotation(\n    Line(points = {{-92, 24}, {-104, 24}, {-104, -54}, {66, -54}, {66, -24}, {46, -24}}, color = {0, 0, 127}));\n  connect(pll_dq.d, PID.u_m) annotation(\n    Line(points = {{14, 62}, {32, 62}, {32, 60}, {40, 60}, {40, 58}, {44, 58}, {44, 62}, {44, 62}}, color = {0, 0, 127}));\n  connect(realExpression1.y, PID.u_s) annotation(\n    Line(points = {{20, 78}, {30, 78}, {30, 74}, {32, 74}}, color = {0, 0, 127}));\n  connect(pll_dq.q, pid.u_m) annotation(\n    Line(points = {{14, 56}, {78, 56}, {78, 72}, {78, 72}, {78, 74}}, color = {0, 0, 127}));\n  connect(realExpression.y, pid.u_s) annotation(\n    Line(points = {{14, 90}, {58, 90}, {58, 86}, {66, 86}, {66, 86}}, color = {0, 0, 127}));\n  connect(abc2dq_current.d, PID2.u_m) annotation(\n    Line(points = {{-58, 19}, {-58, -20}, {-34, -20}, {-34, -16}}, color = {0, 0, 127}));\n  connect(abc2dq_current.q, PID1.u_m) annotation(\n    Line(points = {{-50, 19}, {-50, -13.5}, {-34, -13.5}, {-34, -46}}, color = {0, 0, 127}));\n  connect(pll_dq.theta, abc2dq_current.theta) annotation(\n    Line(points = {{14, 68}, {22, 68}, {22, 48}, {-54, 48}, {-54, 41}}, color = {0, 0, 127}));\n  connect(pid.y, PID1.u_s) annotation(\n    Line(points = {{90, 86}, {94, 86}, {94, 14}, {-60, 14}, {-60, -34}, {-46, -34}}, color = {0, 0, 127}));\n  connect(PID2.u_s, PID.y) annotation(\n    Line(points = {{-46, -4}, {-56, -4}, {-56, 12}, {60, 12}, {60, 74}, {56, 74}, {56, 74}}, color = {0, 0, 127}));\n  connect(PID2.y, dq2abc.d) annotation(\n    Line(points = {{-22, -4}, {16, -4}, {16, -14}, {26, -14}, {26, -14}}, color = {0, 0, 127}));\n  connect(PID1.y, dq2abc.q) annotation(\n    Line(points = {{-22, -34}, {24, -34}, {24, -22}, {24, -22}, {24, -22}, {26, -22}}, color = {0, 0, 127}));\n  connect(angle.theta, dq2abc.theta) annotation(\n    Line(points = {{24, 6}, {32, 6}, {32, -6}, {32, -6}}, color = {0, 0, 127}));\n  connect(lc1.pin4, pll_dq.c) annotation(\n    Line(points = {{-16, 24}, {-12, 24}, {-12, 56}, {-8, 56}}, color = {0, 0, 255}));\n  connect(lc1.pin5, pll_dq.b) annotation(\n    Line(points = {{-16, 30}, {-14, 30}, {-14, 62}, {-8, 62}}, color = {0, 0, 255}));\n  connect(lc1.pin6, pll_dq.a) annotation(\n    Line(points = {{-16, 36}, {-16, 68}, {-8, 68}}, color = {0, 0, 255}));\n  connect(inverter1.pin3, abc2dq_current.a) annotation(\n    Line(points = {{-72, 36}, {-66, 36}, {-66, 36}, {-64, 36}}, color = {0, 0, 255}));\n  connect(inverter1.pin2, abc2dq_current.b) annotation(\n    Line(points = {{-72, 30}, {-64, 30}, {-64, 30}, {-64, 30}}, color = {0, 0, 255}));\n  connect(inverter1.pin1, abc2dq_current.c) annotation(\n    Line(points = {{-72, 24}, {-64, 24}, {-64, 24}, {-64, 24}}, color = {0, 0, 255}));\n  connect(abc2dq_current.pin3, lc1.pin3) annotation(\n    Line(points = {{-44, 36}, {-36, 36}, {-36, 36}, {-36, 36}}, color = {0, 0, 255}));\n  connect(abc2dq_current.pin2, lc1.pin2) annotation(\n    Line(points = {{-44, 30}, {-36, 30}, {-36, 30}, {-36, 30}}, color = {0, 0, 255}));\n  connect(abc2dq_current.pin1, lc1.pin1) annotation(\n    Line(points = {{-44, 24}, {-36, 24}, {-36, 24}, {-36, 24}}, color = {0, 0, 255}));\n  connect(lc1.pin4, ground.p) annotation(\n    Line(points = {{-16, 24}, {-2, 24}, {-2, 34}, {18, 34}, {18, 34}}, color = {0, 0, 255}));\n  connect(lc1.pin5, ground.p) annotation(\n    Line(points = {{-16, 30}, {-2, 30}, {-2, 34}, {18, 34}, {18, 34}}, color = {0, 0, 255}));\n  connect(lc1.pin6, ground.p) annotation(\n    Line(points = {{-16, 36}, {18, 36}, {18, 34}, {18, 34}}, color = {0, 0, 255}));\n  annotation(\n    Diagram);\nend ControlledNetworkSC;\n"
  },
  {
    "path": "omg_grid/Examples/ControlledNetworkSingleInverter.mo",
    "content": "within omg_grid.Examples;\n\nmodel ControlledNetworkSingleInverter\n  omg_grid.Inverters.Inverter inverter1 annotation(\n    Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LC lc1(L1 = 0.001, L2 = 0.001, L3 = 0.001) annotation(\n    Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Loads.RL rl1(L1 = 0.0005, L2 = 0.0005, L3 = 0.0005) annotation(\n    Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.PLLs.PLL_DQ pll_dq annotation(\n    Placement(visible = true, transformation(origin = {2, 62}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Transformations.ABC2DQ_Currents abc2dq_current annotation(\n    Placement(visible = true, transformation(origin = {0, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression(y = -350) annotation(\n    Placement(visible = true, transformation(origin = {2, 90}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression1(y = 230 * 1.41427) annotation(\n    Placement(visible = true, transformation(origin = {5, 77}, extent = {{-13, -11}, {13, 11}}, rotation = 0)));\n  omg_grid.Transformations.DQ2ABC dq2abc annotation(\n    Placement(visible = true, transformation(origin = {36, -18}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.LimPID pid(Td = 0, Ti = 0.006, k = 0.3, limitsAtInit = true, yMax = 150) annotation(\n    Placement(visible = true, transformation(origin = {78, 86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.LimPID PID(Td = 0, Ti = 0.06, k = 0.3, limitsAtInit = true, yMax = 50) annotation(\n    Placement(visible = true, transformation(origin = {44, 74}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.LimPID PID1(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation(\n    Placement(visible = true, transformation(origin = {-34, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Continuous.LimPID PID2(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation(\n    Placement(visible = true, transformation(origin = {-34, -4}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Components.PhaseAngle angle annotation(\n    Placement(visible = true, transformation(origin = {19, 5}, extent = {{-5, -5}, {5, 5}}, rotation = 0)));\nequation\n  connect(inverter1.pin3, lc1.pin3) annotation(\n    Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255}));\n  connect(inverter1.pin2, lc1.pin2) annotation(\n    Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255}));\n  connect(inverter1.pin1, lc1.pin1) annotation(\n    Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255}));\n  connect(lc1.pin6, abc2dq_current.a) annotation(\n    Line(points = {{-20, 36}, {-10, 36}, {-10, 36}, {-10, 36}}, color = {0, 0, 255}));\n  connect(lc1.pin5, abc2dq_current.b) annotation(\n    Line(points = {{-20, 30}, {-10, 30}, {-10, 30}, {-10, 30}}, color = {0, 0, 255}));\n  connect(lc1.pin4, abc2dq_current.c) annotation(\n    Line(points = {{-20, 24}, {-10, 24}, {-10, 24}, {-10, 24}}, color = {0, 0, 255}));\n  connect(abc2dq_current.pin3, rl1.pin3) annotation(\n    Line(points = {{10, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255}));\n  connect(abc2dq_current.pin2, rl1.pin2) annotation(\n    Line(points = {{10, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255}));\n  connect(abc2dq_current.pin1, rl1.pin1) annotation(\n    Line(points = {{10, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255}));\n  connect(lc1.pin6, pll_dq.a) annotation(\n    Line(points = {{-20, 36}, {-16, 36}, {-16, 68}, {-8, 68}}, color = {0, 0, 255}));\n  connect(lc1.pin5, pll_dq.b) annotation(\n    Line(points = {{-20, 30}, {-14, 30}, {-14, 62}, {-8, 62}, {-8, 62}, {-8, 62}}, color = {0, 0, 255}));\n  connect(lc1.pin4, pll_dq.c) annotation(\n    Line(points = {{-20, 24}, {-12, 24}, {-12, 56}, {-8, 56}, {-8, 56}, {-8, 56}}, color = {0, 0, 255}));\n  connect(dq2abc.a, inverter1.u3) annotation(\n    Line(points = {{46, -12}, {78, -12}, {78, -66}, {-116, -66}, {-116, 36}, {-80, 36}, {-80, 36}}, color = {0, 0, 127}));\n  connect(dq2abc.b, inverter1.u2) annotation(\n    Line(points = {{46, -18}, {72, -18}, {72, -58}, {72, -58}, {72, -60}, {-110, -60}, {-110, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n  connect(inverter1.u1, dq2abc.c) annotation(\n    Line(points = {{-80, 24}, {-102, 24}, {-102, 24}, {-104, 24}, {-104, -54}, {66, -54}, {66, -24}, {46, -24}, {46, -24}}, color = {0, 0, 127}));\n  connect(pll_dq.d, PID.u_m) annotation(\n    Line(points = {{14, 62}, {32, 62}, {32, 60}, {40, 60}, {40, 58}, {44, 58}, {44, 62}, {44, 62}}, color = {0, 0, 127}));\n  connect(realExpression1.y, PID.u_s) annotation(\n    Line(points = {{20, 78}, {30, 78}, {30, 74}, {32, 74}}, color = {0, 0, 127}));\n  connect(pll_dq.q, pid.u_m) annotation(\n    Line(points = {{14, 56}, {78, 56}, {78, 72}, {78, 72}, {78, 74}}, color = {0, 0, 127}));\n  connect(realExpression.y, pid.u_s) annotation(\n    Line(points = {{14, 90}, {58, 90}, {58, 86}, {66, 86}, {66, 86}}, color = {0, 0, 127}));\n  connect(abc2dq_current.d, PID2.u_m) annotation(\n    Line(points = {{-4, 20}, {-4, 20}, {-4, 16}, {-58, 16}, {-58, -20}, {-34, -20}, {-34, -16}, {-34, -16}}, color = {0, 0, 127}));\n  connect(abc2dq_current.q, PID1.u_m) annotation(\n    Line(points = {{4, 20}, {4, 20}, {4, 10}, {-20, 10}, {-20, -50}, {-34, -50}, {-34, -46}, {-34, -46}, {-34, -46}}, color = {0, 0, 127}));\n  connect(pll_dq.theta, abc2dq_current.theta) annotation(\n    Line(points = {{14, 68}, {22, 68}, {22, 48}, {0, 48}, {0, 42}, {0, 42}, {0, 42}}, color = {0, 0, 127}));\n  connect(pid.y, PID1.u_s) annotation(\n    Line(points = {{90, 86}, {94, 86}, {94, 14}, {-60, 14}, {-60, -34}, {-46, -34}}, color = {0, 0, 127}));\n  connect(PID2.u_s, PID.y) annotation(\n    Line(points = {{-46, -4}, {-56, -4}, {-56, 12}, {60, 12}, {60, 74}, {56, 74}, {56, 74}}, color = {0, 0, 127}));\n  connect(PID2.y, dq2abc.d) annotation(\n    Line(points = {{-22, -4}, {16, -4}, {16, -14}, {26, -14}, {26, -14}}, color = {0, 0, 127}));\n  connect(PID1.y, dq2abc.q) annotation(\n    Line(points = {{-22, -34}, {24, -34}, {24, -22}, {24, -22}, {24, -22}, {26, -22}}, color = {0, 0, 127}));\n  connect(angle.theta, dq2abc.theta) annotation(\n    Line(points = {{24, 6}, {32, 6}, {32, -6}, {32, -6}}, color = {0, 0, 127}));\n  annotation(\n    Diagram);\nend ControlledNetworkSingleInverter;\n"
  },
  {
    "path": "omg_grid/Examples/NetworkSineTest.mo",
    "content": "within omg_grid.Examples;\n\nmodel NetworkSineTest\n\n  Modelica.Blocks.Sources.Sine sine(amplitude = 230, freqHz = 50) annotation(\n    Placement(visible = true, transformation(origin = {-76, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine1(amplitude = 230, freqHz = 50, phase = 2.0944) annotation(\n    Placement(visible = true, transformation(origin = {-76, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine2(amplitude = 230, freqHz = 50, phase = 4.18879) annotation(\n    Placement(visible = true, transformation(origin = {-76, -16}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine3(amplitude = 230, freqHz = 50) annotation(\n    Placement(visible = true, transformation(origin = {-76, 14}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine4(amplitude = 230, freqHz = 50, phase = 2.0944) annotation(\n    Placement(visible = true, transformation(origin = {-76, 48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine5(amplitude = 230, freqHz = 50, phase = 4.18879) annotation(\n    Placement(visible = true, transformation(origin = {-76, 80}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nomg_grid.Grids.Network network1 annotation(\n    Placement(visible = true, transformation(origin = {38, 2}, extent = {{-46, -46}, {46, 46}}, rotation = 0)));\nequation\n  connect(sine5.y, network1.i1p3) annotation(\n    Line(points = {{-64, 80}, {-34, 80}, {-34, 22}, {-10, 22}, {-10, 22}}, color = {0, 0, 127}));\nconnect(sine4.y, network1.i1p2) annotation(\n    Line(points = {{-64, 48}, {-40, 48}, {-40, 16}, {-10, 16}, {-10, 16}}, color = {0, 0, 127}));\nconnect(sine3.y, network1.i1p1) annotation(\n    Line(points = {{-64, 14}, {-34, 14}, {-34, 10}, {-10, 10}, {-10, 10}}, color = {0, 0, 127}));\nconnect(sine2.y, network1.i2p3) annotation(\n    Line(points = {{-64, -16}, {-64, -16}, {-64, -6}, {-10, -6}, {-10, -6}}, color = {0, 0, 127}));\nconnect(sine1.y, network1.i2p2) annotation(\n    Line(points = {{-64, -48}, {-50, -48}, {-50, -12}, {-10, -12}, {-10, -12}, {-10, -12}}, color = {0, 0, 127}));\nconnect(sine.y, network1.i2p1) annotation(\n    Line(points = {{-64, -82}, {-32, -82}, {-32, -18}, {-10, -18}, {-10, -18}}, color = {0, 0, 127}));\nend NetworkSineTest;\n"
  },
  {
    "path": "omg_grid/Examples/PLL_Test.mo",
    "content": "within omg_grid.Examples;\n\nmodel PLL_Test\n\n  omg_grid.Transformations.ABC2AlphaBeta abc2AlphaBeta annotation(\n    Placement(visible = true, transformation(origin = {-14, 4}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine(amplitude = 230 * 1.414, freqHz = 50) annotation(\n    Placement(visible = true, transformation(origin = {-90, 34}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine1(amplitude = 230 * 1.414, freqHz = 50, phase = -2.0944) annotation(\n    Placement(visible = true, transformation(origin = {-88, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine2(amplitude = 230 * 1.414, freqHz = 50, phase = -4.18879) annotation(\n    Placement(visible = true, transformation(origin = {-88, -26}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Inverters.Inverter inverter annotation(\n    Placement(visible = true, transformation(origin = {-14, 58}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.PLLs.PLL pll annotation(\n    Placement(visible = true, transformation(origin = {28, 56}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(abc2AlphaBeta.a, sine.y) annotation(\n    Line(points = {{-24, 8}, {-44, 8}, {-44, 34}, {-78, 34}, {-78, 34}}, color = {0, 0, 127}));\n  connect(abc2AlphaBeta.b, sine1.y) annotation(\n    Line(points = {{-24, 6}, {-77, 6}}, color = {0, 0, 127}));\n  connect(sine2.y, abc2AlphaBeta.c) annotation(\n    Line(points = {{-76, -26}, {-44, -26}, {-44, 2}, {-24, 2}, {-24, 2}}, color = {0, 0, 127}));\n  connect(inverter.u3, sine.y) annotation(\n    Line(points = {{-24, 64}, {-70, 64}, {-70, 34}, {-78, 34}}, color = {0, 0, 127}));\n  connect(inverter.u2, sine1.y) annotation(\n    Line(points = {{-24, 58}, {-60, 58}, {-60, 6}, {-76, 6}}, color = {0, 0, 127}));\n  connect(inverter.u1, sine2.y) annotation(\n    Line(points = {{-24, 52}, {-54, 52}, {-54, -26}, {-76, -26}}, color = {0, 0, 127}));\n  connect(pll.a, inverter.pin3) annotation(\n    Line(points = {{18, 60}, {4, 60}, {4, 64}, {-4, 64}, {-4, 64}}, color = {0, 0, 255}));\n  connect(pll.b, inverter.pin2) annotation(\n    Line(points = {{18, 58}, {-4, 58}, {-4, 58}, {-4, 58}}, color = {0, 0, 255}));\n  connect(pll.c, inverter.pin1) annotation(\n    Line(points = {{18, 54}, {4, 54}, {4, 52}, {-4, 52}, {-4, 52}}, color = {0, 0, 255}));\nend PLL_Test;\n"
  },
  {
    "path": "omg_grid/Examples/package.mo",
    "content": "within omg_grid;\n\npackage Examples \nextends Modelica.Icons.ExamplesPackage;\n\nend Examples;\n"
  },
  {
    "path": "omg_grid/Examples/package.order",
    "content": "NetworkSineTest\nControlledNetworkSingleInverter\nPLL_Test\nControlledNetworkSC\n"
  },
  {
    "path": "omg_grid/Filter/IdealFilter/L.mo",
    "content": "within omg_grid.Filter.IdealFilter;\n\nmodel L\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(inductor3.n, pin6) annotation(\n    Line(points = {{-50, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(inductor2.n, pin5) annotation(\n    Line(points = {{-50, 44}, {78, 44}, {78, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(inductor1.n, pin4) annotation(\n    Line(points = {{-50, 20}, {60, 20}, {60, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(pin1, inductor1.p) annotation(\n    Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n  connect(pin3, inductor3.p) annotation(\n    Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255}));\n  connect(pin2, inductor2.p) annotation(\n    Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255}));\nend L;\n"
  },
  {
    "path": "omg_grid/Filter/IdealFilter/LC.mo",
    "content": "within omg_grid.Filter.IdealFilter;\n\nmodel LC\n  parameter SI.Capacitance C1 = 0.00001;\n  parameter SI.Capacitance C2 = 0.00001;\n  parameter SI.Capacitance C3 = 0.00001;\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nequation\n  connect(inductor1.n, pin4) annotation(\n    Line(points = {{-50, 20}, {54, 20}, {54, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(inductor2.n, pin5) annotation(\n    Line(points = {{-50, 44}, {68, 44}, {68, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(inductor3.n, pin6) annotation(\n    Line(points = {{-50, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(inductor3.n, capacitor3.p) annotation(\n    Line(points = {{-50, 70}, {-8, 70}, {-8, -26}, {-8, -26}}, color = {0, 0, 255}));\n  connect(inductor2.n, capacitor2.p) annotation(\n    Line(points = {{-50, 44}, {12, 44}, {12, -26}, {12, -26}}, color = {0, 0, 255}));\n  connect(inductor1.n, capacitor1.p) annotation(\n    Line(points = {{-50, 20}, {32, 20}, {32, -26}, {32, -26}}, color = {0, 0, 255}));\n  connect(capacitor3.n, capacitor2.n) annotation(\n    Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255}));\n  connect(capacitor2.n, ground1.p) annotation(\n    Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255}));\n  connect(capacitor2.n, capacitor1.n) annotation(\n    Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255}));\n  connect(pin1, inductor1.p) annotation(\n    Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n  connect(pin3, inductor3.p) annotation(\n    Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255}));\n  connect(pin2, inductor2.p) annotation(\n    Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255}));\nend LC;\n\n"
  },
  {
    "path": "omg_grid/Filter/IdealFilter/LCL.mo",
    "content": "within omg_grid.Filter.IdealFilter;\n\nmodel LCL\n  parameter SI.Capacitance C1 = 0.00001;\n  parameter SI.Capacitance C2 = 0.00001;\n  parameter SI.Capacitance C3 = 0.00001;\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  parameter SI.Inductance L4 = 0.001;\n  parameter SI.Inductance L5 = 0.001;\n  parameter SI.Inductance L6 = 0.001;\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation(\n    Placement(visible = true, transformation(origin = {68, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation(\n    Placement(visible = true, transformation(origin = {74, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation(\n    Placement(visible = true, transformation(origin = {64, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(inductor2.n, inductor5.p) annotation(\n    Line(points = {{-50, 44}, {-50, 44}, {-50, 44}, {64, 44}}, color = {0, 0, 255}));\n  connect(inductor2.n, capacitor2.p) annotation(\n    Line(points = {{-50, 44}, {12, 44}, {12, -26}, {12, -26}}, color = {0, 0, 255}));\n  connect(inductor1.n, inductor4.p) annotation(\n    Line(points = {{-50, 20}, {-50, 20}, {-50, 20}, {58, 20}}, color = {0, 0, 255}));\n  connect(inductor1.n, capacitor1.p) annotation(\n    Line(points = {{-50, 20}, {32, 20}, {32, -26}, {32, -26}}, color = {0, 0, 255}));\n  connect(inductor3.n, capacitor3.p) annotation(\n    Line(points = {{-50, 70}, {-8, 70}, {-8, -26}, {-8, -26}}, color = {0, 0, 255}));\n  connect(inductor3.n, inductor6.p) annotation(\n    Line(points = {{-50, 70}, {54, 70}, {54, 70}, {54, 70}}, color = {0, 0, 255}));\n  connect(inductor4.n, pin4) annotation(\n    Line(points = {{78, 20}, {80, 20}, {80, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(inductor6.n, pin6) annotation(\n    Line(points = {{74, 70}, {84, 70}, {84, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(pin1, inductor1.p) annotation(\n    Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n  connect(pin3, inductor3.p) annotation(\n    Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255}));\n  connect(inductor5.n, pin5) annotation(\n    Line(points = {{84, 44}, {88, 44}, {88, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(pin2, inductor2.p) annotation(\n    Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255}));\n  connect(capacitor2.n, ground1.p) annotation(\n    Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255}));\n  connect(capacitor2.n, capacitor1.n) annotation(\n    Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255}));\n  connect(capacitor3.n, capacitor2.n) annotation(\n    Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255}));\nend LCL;\n"
  },
  {
    "path": "omg_grid/Filter/IdealFilter/LCLC.mo",
    "content": "within omg_grid.Filter.IdealFilter;\n\nmodel LCLC\n  parameter SI.Capacitance C1 = 0.00001;\n  parameter SI.Capacitance C2 = 0.00001;\n  parameter SI.Capacitance C3 = 0.00001;\n  parameter SI.Capacitance C4 = 0.00001;\n  parameter SI.Capacitance C5 = 0.00001;\n  parameter SI.Capacitance C6 = 0.00001;\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  parameter SI.Inductance L4 = 0.001;\n  parameter SI.Inductance L5 = 0.001;\n  parameter SI.Inductance L6 = 0.001;\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-82, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {-82, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {-84, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {-2, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {-22, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {-42, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {16, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation(\n    Placement(visible = true, transformation(origin = {34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation(\n    Placement(visible = true, transformation(origin = {34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation(\n    Placement(visible = true, transformation(origin = {36, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation(\n    Placement(visible = true, transformation(origin = {72, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation(\n    Placement(visible = true, transformation(origin = {52, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation(\n    Placement(visible = true, transformation(origin = {32, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nequation\n  connect(inductor4.n, capacitor4.p) annotation(\n    Line(points = {{44, 20}, {72, 20}, {72, -28}, {72, -28}}, color = {0, 0, 255}));\n  connect(inductor6.n, capacitor6.p) annotation(\n    Line(points = {{46, 70}, {64, 70}, {64, 4}, {32, 4}, {32, -28}}, color = {0, 0, 255}));\n  connect(capacitor3.p, inductor3.n) annotation(\n    Line(points = {{-42, -28}, {-42, -28}, {-42, 70}, {-74, 70}, {-74, 70}}, color = {0, 0, 255}));\n  connect(capacitor2.p, inductor2.n) annotation(\n    Line(points = {{-22, -28}, {-22, -28}, {-22, 44}, {-72, 44}, {-72, 44}}, color = {0, 0, 255}));\n  connect(inductor5.n, capacitor5.p) annotation(\n    Line(points = {{44, 44}, {52, 44}, {52, -28}, {52, -28}}, color = {0, 0, 255}));\n  connect(inductor3.n, inductor6.p) annotation(\n    Line(points = {{-74, 70}, {26, 70}, {26, 70}, {26, 70}}, color = {0, 0, 255}));\n  connect(inductor2.n, inductor5.p) annotation(\n    Line(points = {{-72, 44}, {-72, 44}, {-72, 44}, {24, 44}}, color = {0, 0, 255}));\n  connect(inductor1.n, inductor4.p) annotation(\n    Line(points = {{-72, 20}, {24, 20}, {24, 20}, {24, 20}}, color = {0, 0, 255}));\n  connect(inductor1.n, capacitor1.p) annotation(\n    Line(points = {{-72, 20}, {-2, 20}, {-2, -28}, {-2, -28}}, color = {0, 0, 255}));\n  connect(inductor4.n, pin4) annotation(\n    Line(points = {{44, 20}, {92, 20}, {92, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(inductor6.n, pin6) annotation(\n    Line(points = {{46, 70}, {76, 70}, {76, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(capacitor6.n, ground1.p) annotation(\n    Line(points = {{32, -48}, {16, -48}, {16, -60}}, color = {0, 0, 255}));\n  connect(capacitor6.n, capacitor5.n) annotation(\n    Line(points = {{32, -48}, {52, -48}, {52, -48}, {52, -48}}, color = {0, 0, 255}));\n  connect(capacitor5.n, capacitor4.n) annotation(\n    Line(points = {{52, -48}, {72, -48}, {72, -48}, {72, -48}}, color = {0, 0, 255}));\n  connect(capacitor1.n, ground1.p) annotation(\n    Line(points = {{-2, -48}, {16, -48}, {16, -60}, {16, -60}}, color = {0, 0, 255}));\n  connect(inductor5.n, pin5) annotation(\n    Line(points = {{44, 44}, {94, 44}, {94, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(capacitor3.n, capacitor2.n) annotation(\n    Line(points = {{-42, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255}));\n  connect(capacitor1.n, capacitor2.n) annotation(\n    Line(points = {{-2, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255}));\n  connect(pin3, inductor3.p) annotation(\n    Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-94, 70}}, color = {0, 0, 255}));\n  connect(pin2, inductor2.p) annotation(\n    Line(points = {{-100, 0}, {-95, 0}, {-95, 44}, {-92, 44}}, color = {0, 0, 255}));\n  connect(pin1, inductor1.p) annotation(\n    Line(points = {{-100, -60}, {-93, -60}, {-93, 20}, {-92, 20}}, color = {0, 0, 255}));\nend LCLC;\n"
  },
  {
    "path": "omg_grid/Filter/IdealFilter/PI.mo",
    "content": "within omg_grid.Filter.IdealFilter;\n\nmodel PI\n  parameter SI.Capacitance C1 = 0.00001;\n  parameter SI.Capacitance C2 = 0.00001;\n  parameter SI.Capacitance C3 = 0.00001;\n  parameter SI.Capacitance C4 = 0.00001;\n  parameter SI.Capacitance C5 = 0.00001;\n  parameter SI.Capacitance C6 = 0.00001;\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {2, -16}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {2, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {0, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {-70, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {-48, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {-26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {0, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation(\n    Placement(visible = true, transformation(origin = {26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation(\n    Placement(visible = true, transformation(origin = {46, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation(\n    Placement(visible = true, transformation(origin = {66, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nequation\n  connect(inductor3.n, capacitor6.p) annotation(\n    Line(points = {{10, 60}, {66, 60}, {66, -28}, {66, -28}}, color = {0, 0, 255}));\n  connect(capacitor3.p, pin3) annotation(\n    Line(points = {{-26, -28}, {-26, -28}, {-26, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255}));\n  connect(inductor1.n, pin4) annotation(\n    Line(points = {{12, -16}, {70, -16}, {70, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(inductor1.n, capacitor4.p) annotation(\n    Line(points = {{12, -16}, {26, -16}, {26, -28}}, color = {0, 0, 255}));\n  connect(pin1, inductor1.p) annotation(\n    Line(points = {{-100, -60}, {-80, -60}, {-80, -16}, {-8, -16}}, color = {0, 0, 255}));\n  connect(inductor3.n, pin6) annotation(\n    Line(points = {{10, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(pin3, inductor3.p) annotation(\n    Line(points = {{-100, 60}, {-10, 60}}, color = {0, 0, 255}));\n  connect(inductor2.n, pin5) annotation(\n    Line(points = {{12, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(inductor2.n, capacitor5.p) annotation(\n    Line(points = {{12, 0}, {46, 0}, {46, -28}}, color = {0, 0, 255}));\n  connect(inductor2.p, pin2) annotation(\n    Line(points = {{-8, 0}, {-100, 0}}, color = {0, 0, 255}));\n  connect(capacitor1.p, pin1) annotation(\n    Line(points = {{-70, -28}, {-80, -28}, {-80, -60}, {-100, -60}}, color = {0, 0, 255}));\n  connect(capacitor2.p, pin2) annotation(\n    Line(points = {{-48, -28}, {-48, -28}, {-48, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255}));\n  connect(capacitor3.n, ground1.p) annotation(\n    Line(points = {{-26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255}));\n  connect(capacitor4.n, ground1.p) annotation(\n    Line(points = {{26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255}));\n  connect(capacitor2.n, capacitor3.n) annotation(\n    Line(points = {{-48, -48}, {-26, -48}, {-26, -48}, {-26, -48}}, color = {0, 0, 255}));\n  connect(capacitor1.n, capacitor2.n) annotation(\n    Line(points = {{-70, -48}, {-48, -48}, {-48, -48}, {-48, -48}}, color = {0, 0, 255}));\n  connect(capacitor5.n, capacitor4.n) annotation(\n    Line(points = {{46, -48}, {26, -48}, {26, -48}, {26, -48}}, color = {0, 0, 255}));\n  connect(capacitor6.n, capacitor5.n) annotation(\n    Line(points = {{66, -48}, {46, -48}, {46, -48}, {46, -48}}, color = {0, 0, 255}));\nend PI;\n"
  },
  {
    "path": "omg_grid/Filter/IdealFilter/package.mo",
    "content": "within omg_grid.Filter;\n\npackage IdealFilter\nend IdealFilter;\n"
  },
  {
    "path": "omg_grid/Filter/IdealFilter/package.order",
    "content": "LC\nLCL\nPI\nLCLC\nL\n"
  },
  {
    "path": "omg_grid/Filter/LossesFilter/L.mo",
    "content": "within omg_grid.Filter.LossesFilter;\n\nmodel L\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  parameter SI.Resistance R1 = 0.01;\n  parameter SI.Resistance R2 = 0.01;\n  parameter SI.Resistance R3 = 0.01;\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation(\n    Placement(visible = true, transformation(origin = {-32, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation(\n    Placement(visible = true, transformation(origin = {-32, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation(\n    Placement(visible = true, transformation(origin = {-32, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(resistor1.n, pin4) annotation(\n    Line(points = {{-22, 20}, {14, 20}, {14, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(resistor2.n, pin5) annotation(\n    Line(points = {{-22, 44}, {70, 44}, {70, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(resistor3.n, pin6) annotation(\n    Line(points = {{-22, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(inductor1.n, resistor1.p) annotation(\n    Line(points = {{-50, 20}, {-50, 20}, {-50, 20}, {-42, 20}}, color = {0, 0, 255}));\n  connect(inductor2.n, resistor2.p) annotation(\n    Line(points = {{-50, 44}, {-42, 44}, {-42, 44}, {-42, 44}}, color = {0, 0, 255}));\n  connect(inductor3.n, resistor3.p) annotation(\n    Line(points = {{-50, 70}, {-42, 70}, {-42, 70}, {-42, 70}}, color = {0, 0, 255}));\n  connect(pin1, inductor1.p) annotation(\n    Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n  connect(pin3, inductor3.p) annotation(\n    Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255}));\n  connect(pin2, inductor2.p) annotation(\n    Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255}));\nend L;\n"
  },
  {
    "path": "omg_grid/Filter/LossesFilter/LC.mo",
    "content": "within omg_grid.Filter.LossesFilter;\n\nmodel LC\n  parameter SI.Capacitance C1 = 0.00001;\n  parameter SI.Capacitance C2 = 0.00001;\n  parameter SI.Capacitance C3 = 0.00001;\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  parameter SI.Resistance R1 = 0.01;\n  parameter SI.Resistance R2 = 0.01;\n  parameter SI.Resistance R3 = 0.01;\n  parameter SI.Resistance R4 = 0.01;\n  parameter SI.Resistance R5 = 0.01;\n  parameter SI.Resistance R6 = 0.01;\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation(\n    Placement(visible = true, transformation(origin = {-34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation(\n    Placement(visible = true, transformation(origin = {-34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation(\n    Placement(visible = true, transformation(origin = {-26, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor4(R = R4) annotation(\n    Placement(visible = true, transformation(origin = {32, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor5(R = R5) annotation(\n    Placement(visible = true, transformation(origin = {12, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor6(R = R6) annotation(\n    Placement(visible = true, transformation(origin = {-8, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nequation\n  connect(resistor3.n, resistor6.p) annotation(\n    Line(points = {{-16, 70}, {-8, 70}, {-8, 2}}, color = {0, 0, 255}));\n  connect(resistor3.n, pin6) annotation(\n    Line(points = {{-16, 70}, {80, 70}, {80, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(inductor3.n, resistor3.p) annotation(\n    Line(points = {{-50, 70}, {-36, 70}}, color = {0, 0, 255}));\n  connect(pin4, resistor1.n) annotation(\n    Line(points = {{100, -60}, {62, -60}, {62, 20}, {-24, 20}, {-24, 20}}, color = {0, 0, 255}));\n  connect(resistor4.n, capacitor1.p) annotation(\n    Line(points = {{32, -18}, {32, -18}, {32, -26}, {32, -26}}, color = {0, 0, 255}));\n  connect(resistor5.n, capacitor2.p) annotation(\n    Line(points = {{12, -18}, {12, -18}, {12, -26}, {12, -26}}, color = {0, 0, 255}));\n  connect(resistor6.n, capacitor3.p) annotation(\n    Line(points = {{-8, -18}, {-8, -18}, {-8, -26}, {-8, -26}}, color = {0, 0, 255}));\n  connect(pin5, resistor2.n) annotation(\n    Line(points = {{100, 0}, {78, 0}, {78, 44}, {-24, 44}, {-24, 44}}, color = {0, 0, 255}));\n  connect(resistor2.n, resistor5.p) annotation(\n    Line(points = {{-24, 44}, {12, 44}, {12, 2}, {12, 2}}, color = {0, 0, 255}));\n  connect(resistor1.n, resistor4.p) annotation(\n    Line(points = {{-24, 20}, {32, 20}, {32, 2}, {32, 2}}, color = {0, 0, 255}));\n  connect(inductor1.n, resistor1.p) annotation(\n    Line(points = {{-50, 20}, {-44, 20}, {-44, 20}, {-44, 20}}, color = {0, 0, 255}));\n  connect(inductor2.n, resistor2.p) annotation(\n    Line(points = {{-50, 44}, {-44, 44}, {-44, 44}, {-44, 44}}, color = {0, 0, 255}));\n  connect(capacitor3.n, capacitor2.n) annotation(\n    Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255}));\n  connect(capacitor2.n, ground1.p) annotation(\n    Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255}));\n  connect(capacitor2.n, capacitor1.n) annotation(\n    Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255}));\n  connect(pin1, inductor1.p) annotation(\n    Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n  connect(pin3, inductor3.p) annotation(\n    Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255}));\n  connect(pin2, inductor2.p) annotation(\n    Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255}));\nend LC;\n\n"
  },
  {
    "path": "omg_grid/Filter/LossesFilter/LCL.mo",
    "content": "within omg_grid.Filter.LossesFilter;\n\nmodel LCL\n  parameter SI.Capacitance C1 = 0.00001;\n  parameter SI.Capacitance C2 = 0.00001;\n  parameter SI.Capacitance C3 = 0.00001;\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  parameter SI.Inductance L4 = 0.001;\n  parameter SI.Inductance L5 = 0.001;\n  parameter SI.Inductance L6 = 0.001;\n  parameter SI.Resistance R1 = 0.01;\n  parameter SI.Resistance R2 = 0.01;\n  parameter SI.Resistance R3 = 0.01;\n  parameter SI.Resistance R4 = 0.01;\n  parameter SI.Resistance R5 = 0.01;\n  parameter SI.Resistance R6 = 0.01;\n  parameter SI.Resistance R7 = 0.01;\n  parameter SI.Resistance R8 = 0.01;\n  parameter SI.Resistance R9 = 0.01;\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {-64, 58}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {-72, 86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {38, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {-28, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation(\n    Placement(visible = true, transformation(origin = {68, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation(\n    Placement(visible = true, transformation(origin = {70, 40}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation(\n    Placement(visible = true, transformation(origin = {74, 62}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor1 annotation(\n    Placement(visible = true, transformation(origin = {-36, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor2 annotation(\n    Placement(visible = true, transformation(origin = {-32, 48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor3 annotation(\n    Placement(visible = true, transformation(origin = {-30, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor4 annotation(\n    Placement(visible = true, transformation(origin = {42, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor5 annotation(\n    Placement(visible = true, transformation(origin = {8, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor6 annotation(\n    Placement(visible = true, transformation(origin = {-22, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor7 annotation(\n    Placement(visible = true, transformation(origin = {32, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor8 annotation(\n    Placement(visible = true, transformation(origin = {40, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor9 annotation(\n    Placement(visible = true, transformation(origin = {34, 68}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(resistor2.n, resistor5.p) annotation(\n    Line(points = {{-22, 48}, {8, 48}, {8, 0}}, color = {0, 0, 255}));\n  connect(resistor8.p, resistor2.n) annotation(\n    Line(points = {{30, 44}, {2, 44}, {2, 48}, {-22, 48}}, color = {0, 0, 255}));\n  connect(resistor2.p, inductor2.n) annotation(\n    Line(points = {{-42, 48}, {-50, 48}, {-50, 58}, {-54, 58}}, color = {0, 0, 255}));\n  connect(pin2, inductor2.p) annotation(\n    Line(points = {{-100, 0}, {-91, 0}, {-91, 58}, {-74, 58}}, color = {0, 0, 255}));\n  connect(pin3, inductor3.p) annotation(\n    Line(points = {{-100, 60}, {-93, 60}, {-93, 86}, {-82, 86}}, color = {0, 0, 255}));\n  connect(resistor3.p, inductor3.n) annotation(\n    Line(points = {{-40, 82}, {-47, 82}, {-47, 86}, {-62, 86}}, color = {0, 0, 255}));\n  connect(resistor3.n, resistor9.p) annotation(\n    Line(points = {{-20, 82}, {3, 82}, {3, 68}, {24, 68}}, color = {0, 0, 255}));\n  connect(resistor6.p, resistor3.n) annotation(\n    Line(points = {{-22, 2}, {-22, 41}, {-20, 41}, {-20, 82}}, color = {0, 0, 255}));\n  connect(inductor6.n, pin6) annotation(\n    Line(points = {{84, 62}, {84, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(resistor9.n, inductor6.p) annotation(\n    Line(points = {{44, 68}, {55, 68}, {55, 62}, {64, 62}}, color = {0, 0, 255}));\n  connect(inductor5.n, pin5) annotation(\n    Line(points = {{80, 40}, {88, 40}, {88, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(resistor8.n, inductor5.p) annotation(\n    Line(points = {{50, 44}, {55, 44}, {55, 40}, {60, 40}}, color = {0, 0, 255}));\n  connect(resistor7.n, inductor4.p) annotation(\n    Line(points = {{42, 30}, {54, 30}, {54, 6}, {58, 6}}, color = {0, 0, 255}));\n  connect(resistor4.p, resistor7.p) annotation(\n    Line(points = {{42, 0}, {42, 14.5}, {22, 14.5}, {22, 30}}, color = {0, 0, 255}));\n  connect(resistor1.n, resistor7.p) annotation(\n    Line(points = {{-26, 20}, {2, 20}, {2, 30}, {22, 30}}, color = {0, 0, 255}));\n  connect(inductor4.n, pin4) annotation(\n    Line(points = {{78, 6}, {80, 6}, {80, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(capacitor2.n, capacitor1.n) annotation(\n    Line(points = {{12, -46}, {23, -46}, {23, -56}, {38, -56}}, color = {0, 0, 255}));\n  connect(resistor4.n, capacitor1.p) annotation(\n    Line(points = {{42, -20}, {42, -24}, {38, -24}, {38, -36}}, color = {0, 0, 255}));\n  connect(resistor5.n, capacitor2.p) annotation(\n    Line(points = {{8, -20}, {8, -24}, {12, -24}, {12, -26}}, color = {0, 0, 255}));\n  connect(capacitor3.n, capacitor2.n) annotation(\n    Line(points = {{-28, -46}, {12, -46}}, color = {0, 0, 255}));\n  connect(resistor6.n, capacitor3.p) annotation(\n    Line(points = {{-22, -18}, {-22, -24}, {-28, -24}, {-28, -26}}, color = {0, 0, 255}));\n  connect(resistor1.p, inductor1.n) annotation(\n    Line(points = {{-46, 20}, {-50, 20}, {-50, 20}, {-50, 20}}, color = {0, 0, 255}));\n  connect(pin1, inductor1.p) annotation(\n    Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n  connect(capacitor2.n, ground1.p) annotation(\n    Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255}));\nend LCL;\n"
  },
  {
    "path": "omg_grid/Filter/LossesFilter/LCLC.mo",
    "content": "within omg_grid.Filter.LossesFilter;\n\nmodel LCLC\n  parameter SI.Capacitance C1 = 0.00001;\n  parameter SI.Capacitance C2 = 0.00001;\n  parameter SI.Capacitance C3 = 0.00001;\n  parameter SI.Capacitance C4 = 0.00001;\n  parameter SI.Capacitance C5 = 0.00001;\n  parameter SI.Capacitance C6 = 0.00001;\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  parameter SI.Inductance L4 = 0.001;\n  parameter SI.Inductance L5 = 0.001;\n  parameter SI.Inductance L6 = 0.001;\n  parameter SI.Resistance R1 = 0.01;\n  parameter SI.Resistance R2 = 0.01;\n  parameter SI.Resistance R3 = 0.01;\n  parameter SI.Resistance R4 = 0.01;\n  parameter SI.Resistance R5 = 0.01;\n  parameter SI.Resistance R6 = 0.01;\n  parameter SI.Resistance R7 = 0.01;\n  parameter SI.Resistance R8 = 0.01;\n  parameter SI.Resistance R9 = 0.01;\n  parameter SI.Resistance R10 = 0.01;\n  parameter SI.Resistance R11 = 0.01;\n  parameter SI.Resistance R12 = 0.01;\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-82, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {-82, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {-84, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {-2, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {-22, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {-42, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {16, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation(\n    Placement(visible = true, transformation(origin = {34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation(\n    Placement(visible = true, transformation(origin = {34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation(\n    Placement(visible = true, transformation(origin = {36, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation(\n    Placement(visible = true, transformation(origin = {72, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation(\n    Placement(visible = true, transformation(origin = {52, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation(\n    Placement(visible = true, transformation(origin = {32, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation(\n    Placement(visible = true, transformation(origin = {-56, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation(\n    Placement(visible = true, transformation(origin = {-56, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation(\n    Placement(visible = true, transformation(origin = {-56, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor4(R = R4) annotation(\n    Placement(visible = true, transformation(origin = {-2, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor5(R = R5) annotation(\n    Placement(visible = true, transformation(origin = {-22, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor6(R = R6) annotation(\n    Placement(visible = true, transformation(origin = {-42, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor7(R = R7) annotation(\n    Placement(visible = true, transformation(origin = {10, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor8(R = R8) annotation(\n    Placement(visible = true, transformation(origin = {10, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor9(R = R9) annotation(\n    Placement(visible = true, transformation(origin = {10, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor10(R = R10) annotation(\n    Placement(visible = true, transformation(origin = {72, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor11(R = R11) annotation(\n    Placement(visible = true, transformation(origin = {52, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor12(R = R12) annotation(\n    Placement(visible = true, transformation(origin = {32, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nequation\n  connect(resistor10.p, inductor4.n) annotation(\n    Line(points = {{72, -4}, {72, -4}, {72, 20}, {44, 20}, {44, 20}}, color = {0, 0, 255}));\n  connect(inductor5.n, resistor11.p) annotation(\n    Line(points = {{44, 44}, {54, 44}, {54, 44}, {52, 44}, {52, -4}, {52, -4}}, color = {0, 0, 255}));\n  connect(resistor12.p, inductor6.n) annotation(\n    Line(points = {{32, -4}, {32, -4}, {32, 10}, {58, 10}, {58, 70}, {46, 70}, {46, 70}}, color = {0, 0, 255}));\n  connect(resistor11.n, capacitor5.p) annotation(\n    Line(points = {{52, -24}, {52, -24}, {52, -28}, {52, -28}}, color = {0, 0, 255}));\n  connect(resistor12.n, capacitor6.p) annotation(\n    Line(points = {{32, -24}, {32, -24}, {32, -28}, {32, -28}}, color = {0, 0, 255}));\n  connect(resistor1.n, resistor4.p) annotation(\n    Line(points = {{-46, 20}, {-2, 20}, {-2, -4}, {-2, -4}, {-2, -4}}, color = {0, 0, 255}));\n  connect(resistor2.n, resistor5.p) annotation(\n    Line(points = {{-46, 44}, {-22, 44}, {-22, -4}, {-22, -4}, {-22, -4}}, color = {0, 0, 255}));\n  connect(resistor6.p, resistor3.n) annotation(\n    Line(points = {{-42, -4}, {-42, -4}, {-42, 70}, {-46, 70}, {-46, 70}}, color = {0, 0, 255}));\n  connect(resistor1.n, resistor7.p) annotation(\n    Line(points = {{-46, 20}, {-46, 20}, {-46, 20}, {0, 20}}, color = {0, 0, 255}));\n  connect(resistor2.n, resistor8.p) annotation(\n    Line(points = {{-46, 44}, {-46, 44}, {-46, 44}, {0, 44}}, color = {0, 0, 255}));\n  connect(resistor3.n, resistor9.p) annotation(\n    Line(points = {{-46, 70}, {0, 70}, {0, 70}, {0, 70}}, color = {0, 0, 255}));\n  connect(resistor7.n, inductor4.p) annotation(\n    Line(points = {{20, 20}, {24, 20}, {24, 20}, {24, 20}}, color = {0, 0, 255}));\n  connect(resistor6.n, capacitor3.p) annotation(\n    Line(points = {{-42, -24}, {-42, -24}, {-42, -28}, {-42, -28}}, color = {0, 0, 255}));\n  connect(resistor5.n, capacitor2.p) annotation(\n    Line(points = {{-22, -24}, {-22, -24}, {-22, -24}, {-22, -28}}, color = {0, 0, 255}));\n  connect(resistor4.n, capacitor1.p) annotation(\n    Line(points = {{-2, -24}, {-2, -24}, {-2, -28}, {-2, -28}}, color = {0, 0, 255}));\n  connect(resistor10.n, capacitor4.p) annotation(\n    Line(points = {{72, -24}, {72, -24}, {72, -28}, {72, -28}}, color = {0, 0, 255}));\n  connect(resistor8.n, inductor5.p) annotation(\n    Line(points = {{20, 44}, {24, 44}}, color = {0, 0, 255}));\n  connect(resistor9.n, inductor6.p) annotation(\n    Line(points = {{20, 70}, {26, 70}, {26, 70}, {26, 70}}, color = {0, 0, 255}));\n  connect(inductor1.n, resistor1.p) annotation(\n    Line(points = {{-72, 20}, {-66, 20}, {-66, 20}, {-66, 20}}, color = {0, 0, 255}));\n  connect(inductor3.n, resistor3.p) annotation(\n    Line(points = {{-74, 70}, {-74, 70}, {-74, 70}, {-66, 70}}, color = {0, 0, 255}));\n  connect(inductor2.n, resistor2.p) annotation(\n    Line(points = {{-72, 44}, {-66, 44}, {-66, 44}, {-66, 44}}, color = {0, 0, 255}));\n  connect(inductor4.n, pin4) annotation(\n    Line(points = {{44, 20}, {92, 20}, {92, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(inductor6.n, pin6) annotation(\n    Line(points = {{46, 70}, {76, 70}, {76, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(capacitor6.n, ground1.p) annotation(\n    Line(points = {{32, -48}, {16, -48}, {16, -60}}, color = {0, 0, 255}));\n  connect(capacitor6.n, capacitor5.n) annotation(\n    Line(points = {{32, -48}, {52, -48}, {52, -48}, {52, -48}}, color = {0, 0, 255}));\n  connect(capacitor5.n, capacitor4.n) annotation(\n    Line(points = {{52, -48}, {72, -48}, {72, -48}, {72, -48}}, color = {0, 0, 255}));\n  connect(capacitor1.n, ground1.p) annotation(\n    Line(points = {{-2, -48}, {16, -48}, {16, -60}, {16, -60}}, color = {0, 0, 255}));\n  connect(inductor5.n, pin5) annotation(\n    Line(points = {{44, 44}, {94, 44}, {94, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(capacitor3.n, capacitor2.n) annotation(\n    Line(points = {{-42, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255}));\n  connect(capacitor1.n, capacitor2.n) annotation(\n    Line(points = {{-2, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255}));\n  connect(pin3, inductor3.p) annotation(\n    Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-94, 70}}, color = {0, 0, 255}));\n  connect(pin2, inductor2.p) annotation(\n    Line(points = {{-100, 0}, {-95, 0}, {-95, 44}, {-92, 44}}, color = {0, 0, 255}));\n  connect(pin1, inductor1.p) annotation(\n    Line(points = {{-100, -60}, {-93, -60}, {-93, 20}, {-92, 20}}, color = {0, 0, 255}));\nend LCLC;\n"
  },
  {
    "path": "omg_grid/Filter/LossesFilter/PI.mo",
    "content": "within omg_grid.Filter.LossesFilter;\n\nmodel PI\n  parameter SI.Capacitance C1 = 0.00001;\n  parameter SI.Capacitance C2 = 0.00001;\n  parameter SI.Capacitance C3 = 0.00001;\n  parameter SI.Capacitance C4 = 0.00001;\n  parameter SI.Capacitance C5 = 0.00001;\n  parameter SI.Capacitance C6 = 0.00001;\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  parameter SI.Resistance R1 = 0.01;\n  parameter SI.Resistance R2 = 0.01;\n  parameter SI.Resistance R3 = 0.01;\n  parameter SI.Resistance R4 = 0.01;\n  parameter SI.Resistance R5 = 0.01;\n  parameter SI.Resistance R6 = 0.01;\n  parameter SI.Resistance R7 = 0.01;\n  parameter SI.Resistance R8 = 0.01;\n  parameter SI.Resistance R9 = 0.01;\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-14, 28}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {-14, 52}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {-14, 78}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {-70, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {-48, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {-26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {0, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation(\n    Placement(visible = true, transformation(origin = {26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation(\n    Placement(visible = true, transformation(origin = {46, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation(\n    Placement(visible = true, transformation(origin = {64, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation(\n    Placement(visible = true, transformation(origin = {-70, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation(\n    Placement(visible = true, transformation(origin = {-48, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation(\n    Placement(visible = true, transformation(origin = {-26, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor4(R = R4) annotation(\n    Placement(visible = true, transformation(origin = {10, 28}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor5(R = R5) annotation(\n    Placement(visible = true, transformation(origin = {10, 52}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor6(R = R6) annotation(\n    Placement(visible = true, transformation(origin = {10, 78}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor7(R = R7) annotation(\n    Placement(visible = true, transformation(origin = {26, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor8(R = R8) annotation(\n    Placement(visible = true, transformation(origin = {46, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor9(R = R9) annotation(\n    Placement(visible = true, transformation(origin = {64, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nequation\n  connect(inductor1.n, resistor4.p) annotation(\n    Line(points = {{-4, 28}, {0, 28}, {0, 28}, {0, 28}}, color = {0, 0, 255}));\n  connect(inductor2.n, resistor5.p) annotation(\n    Line(points = {{-4, 52}, {-4, 52}, {-4, 52}, {0, 52}}, color = {0, 0, 255}));\n  connect(inductor3.n, resistor6.p) annotation(\n    Line(points = {{-4, 78}, {0, 78}, {0, 78}, {0, 78}}, color = {0, 0, 255}));\n  connect(resistor3.p, pin3) annotation(\n    Line(points = {{-26, 2}, {-26, 2}, {-26, 18}, {-36, 18}, {-36, 78}, {-90, 78}, {-90, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255}));\n  connect(resistor2.p, pin2) annotation(\n    Line(points = {{-48, 2}, {-48, 2}, {-48, 52}, {-84, 52}, {-84, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255}));\n  connect(resistor1.p, pin1) annotation(\n    Line(points = {{-70, 2}, {-80, 2}, {-80, -60}, {-98, -60}, {-98, -60}, {-100, -60}}, color = {0, 0, 255}));\n  connect(resistor1.n, capacitor1.p) annotation(\n    Line(points = {{-70, -18}, {-70, -18}, {-70, -28}, {-70, -28}}, color = {0, 0, 255}));\n  connect(resistor2.n, capacitor2.p) annotation(\n    Line(points = {{-48, -18}, {-48, -18}, {-48, -28}, {-48, -28}}, color = {0, 0, 255}));\n  connect(resistor3.n, capacitor3.p) annotation(\n    Line(points = {{-26, -18}, {-26, -18}, {-26, -28}, {-26, -28}}, color = {0, 0, 255}));\n  connect(resistor9.n, capacitor6.p) annotation(\n    Line(points = {{64, -18}, {64, -18}, {64, -28}, {64, -28}}, color = {0, 0, 255}));\n  connect(capacitor6.n, capacitor5.n) annotation(\n    Line(points = {{64, -48}, {46, -48}}, color = {0, 0, 255}));\n  connect(capacitor5.p, resistor8.n) annotation(\n    Line(points = {{46, -28}, {46, -28}, {46, -18}, {46, -18}}, color = {0, 0, 255}));\n  connect(resistor5.n, resistor8.p) annotation(\n    Line(points = {{20, 52}, {46, 52}, {46, 2}}, color = {0, 0, 255}));\n  connect(resistor7.n, capacitor4.p) annotation(\n    Line(points = {{26, -18}, {26, -18}, {26, -28}, {26, -28}}, color = {0, 0, 255}));\n  connect(resistor6.n, pin6) annotation(\n    Line(points = {{20, 78}, {84, 78}, {84, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(resistor5.n, pin5) annotation(\n    Line(points = {{20, 52}, {84, 52}, {84, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(resistor4.n, pin4) annotation(\n    Line(points = {{20, 28}, {74, 28}, {74, 28}, {76, 28}, {76, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(resistor6.n, resistor9.p) annotation(\n    Line(points = {{20, 78}, {64, 78}, {64, 2}, {64, 2}}, color = {0, 0, 255}));\n  connect(resistor4.n, resistor7.p) annotation(\n    Line(points = {{20, 28}, {26, 28}, {26, 2}, {26, 2}}, color = {0, 0, 255}));\n  connect(pin1, inductor1.p) annotation(\n    Line(points = {{-100, -60}, {-80, -60}, {-80, 28}, {-24, 28}}, color = {0, 0, 255}));\n  connect(pin3, inductor3.p) annotation(\n    Line(points = {{-100, 60}, {-90, 60}, {-90, 78}, {-24, 78}}, color = {0, 0, 255}));\n  connect(capacitor3.n, ground1.p) annotation(\n    Line(points = {{-26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255}));\n  connect(capacitor4.n, ground1.p) annotation(\n    Line(points = {{26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255}));\n  connect(inductor2.p, pin2) annotation(\n    Line(points = {{-24, 52}, {-84, 52}, {-84, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255}));\n  connect(capacitor2.n, capacitor3.n) annotation(\n    Line(points = {{-48, -48}, {-26, -48}, {-26, -48}, {-26, -48}}, color = {0, 0, 255}));\n  connect(capacitor1.n, capacitor2.n) annotation(\n    Line(points = {{-70, -48}, {-48, -48}, {-48, -48}, {-48, -48}}, color = {0, 0, 255}));\n  connect(capacitor5.n, capacitor4.n) annotation(\n    Line(points = {{46, -48}, {26, -48}, {26, -48}, {26, -48}}, color = {0, 0, 255}));\nend PI;\n"
  },
  {
    "path": "omg_grid/Filter/LossesFilter/package.mo",
    "content": "within omg_grid.Filter;\n\npackage LossesFilter\nend LossesFilter;\n"
  },
  {
    "path": "omg_grid/Filter/LossesFilter/package.order",
    "content": "LC\nLCL\nPI\nLCLC\nL\n"
  },
  {
    "path": "omg_grid/Filter/package.mo",
    "content": "within omg_grid;\npackage Filter \nextends Modelica.Icons.Package;\n\nannotation (Icon(\n      coordinateSystem(preserveAspectRatio=true,\n        extent={{-100.0,-100.0},{100.0,100.0}}),\n        graphics={\n      Line(points={{-80.0,80.0},{-80.0,-88.0}},\n        color={192,192,192}),\n      Polygon(lineColor={192,192,192},\n        fillColor={192,192,192},\n        fillPattern=FillPattern.Solid,\n        points={{-80.0,92.0},{-88.0,70.0},{-72.0,70.0},{-80.0,92.0}}),\n      Line(points={{-90.0,-78.0},{82.0,-78.0}},\n        color={192,192,192}),\n      Polygon(lineColor={192,192,192},\n        fillColor={192,192,192},\n        fillPattern=FillPattern.Solid,\n        points={{90.0,-78.0},{68.0,-70.0},{68.0,-86.0},{90.0,-78.0}}),\n      Text(lineColor={192,192,192},\n        extent={{-66.0,52.0},{88.0,90.0}},\n        textString=\"%order\"),\n      Text(\n        extent={{-138.0,-140.0},{162.0,-110.0}},\n        textString=\"f_cut=%f_cut\"),\n      Rectangle(lineColor={160,160,164},\n        fillColor={255,255,255},\n        fillPattern=FillPattern.Backward,\n        extent={{-80.0,-78.0},{22.0,10.0}}),\n      Line(origin = {3.333,-6.667}, points = {{-83.333,34.667},{24.667,34.667},{42.667,-71.333}}, color = {0,0,127}, smooth = Smooth.Bezier)}));\nend Filter;\n"
  },
  {
    "path": "omg_grid/Filter/package.order",
    "content": "IdealFilter\nLossesFilter\n"
  },
  {
    "path": "omg_grid/Grids/Microgrid.mo",
    "content": "within omg_grid.Grids;\n\nmodel Microgrid\n  omg_grid.Inverters.Inverter inverter1 annotation(\n    Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LC lc1 annotation(\n    Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Inverters.Inverter inverter2 annotation(\n    Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LCL lcl1 annotation(\n    Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nomg_grid.Loads.RL rl1 annotation(\n    Placement(visible = true, transformation(origin = {92, 2}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nomg_grid.Filter.IdealFilter.L l12 annotation(\n    Placement(visible = true, transformation(origin = {2, 0}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nomg_grid.Filter.IdealFilter.L l13 annotation(\n    Placement(visible = true, transformation(origin = {46, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nomg_grid.Filter.IdealFilter.L l23 annotation(\n    Placement(visible = true, transformation(origin = {48, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(inverter1.pin3, lc1.pin3) annotation(\n    Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255}));\n  connect(inverter1.pin2, lc1.pin2) annotation(\n    Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255}));\n  connect(inverter1.pin1, lc1.pin1) annotation(\n    Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255}));\n  connect(i1p1, inverter1.u1) annotation(\n    Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n  connect(i1p2, inverter1.u2) annotation(\n    Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n  connect(i1p3, inverter1.u3) annotation(\n    Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n  connect(i2p3, inverter2.u3) annotation(\n    Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127}));\n  connect(i2p2, inverter2.u2) annotation(\n    Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127}));\n  connect(i2p1, inverter2.u1) annotation(\n    Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127}));\n  connect(inverter2.pin3, lcl1.pin3) annotation(\n    Line(points = {{-60, -24}, {-42, -24}, {-42, -24}, {-42, -24}}, color = {0, 0, 255}));\n  connect(inverter2.pin2, lcl1.pin2) annotation(\n    Line(points = {{-60, -30}, {-42, -30}, {-42, -30}, {-42, -30}}, color = {0, 0, 255}));\n  connect(inverter2.pin1, lcl1.pin1) annotation(\n    Line(points = {{-60, -36}, {-42, -36}, {-42, -36}, {-42, -36}}, color = {0, 0, 255}));\nconnect(lc1.pin6, l12.pin3) annotation(\n    Line(points = {{-20, 36}, {8, 36}, {8, 10}}, color = {0, 0, 255}));\nconnect(lc1.pin5, l12.pin2) annotation(\n    Line(points = {{-20, 30}, {2, 30}, {2, 10}}, color = {0, 0, 255}));\nconnect(lc1.pin4, l12.pin1) annotation(\n    Line(points = {{-20, 24}, {-4, 24}, {-4, 10}}, color = {0, 0, 255}));\nconnect(l12.pin6, lcl1.pin6) annotation(\n    Line(points = {{8, -10}, {8, -24}, {-22, -24}}, color = {0, 0, 255}));\nconnect(l12.pin5, lcl1.pin5) annotation(\n    Line(points = {{2, -10}, {2, -30}, {-22, -30}}, color = {0, 0, 255}));\nconnect(l12.pin4, lcl1.pin4) annotation(\n    Line(points = {{-4, -10}, {-4, -36}, {-22, -36}}, color = {0, 0, 255}));\nconnect(l13.pin3, lc1.pin6) annotation(\n    Line(points = {{36, 36}, {-20, 36}}, color = {0, 0, 255}));\nconnect(l13.pin2, lc1.pin5) annotation(\n    Line(points = {{36, 30}, {-20, 30}, {-20, 30}, {-20, 30}, {-20, 30}}, color = {0, 0, 255}));\nconnect(l13.pin1, lc1.pin4) annotation(\n    Line(points = {{36, 24}, {-20, 24}, {-20, 24}, {-20, 24}}, color = {0, 0, 255}));\nconnect(l23.pin3, lcl1.pin6) annotation(\n    Line(points = {{38, -24}, {-22, -24}, {-22, -24}, {-22, -24}}, color = {0, 0, 255}));\nconnect(l23.pin2, lcl1.pin5) annotation(\n    Line(points = {{38, -30}, {-22, -30}, {-22, -30}, {-22, -30}}, color = {0, 0, 255}));\nconnect(l23.pin1, lcl1.pin4) annotation(\n    Line(points = {{38, -36}, {-22, -36}, {-22, -36}, {-22, -36}}, color = {0, 0, 255}));\nconnect(l13.pin6, rl1.pin3) annotation(\n    Line(points = {{56, 36}, {72, 36}, {72, 8}, {82, 8}, {82, 8}}, color = {0, 0, 255}));\nconnect(l13.pin5, rl1.pin2) annotation(\n    Line(points = {{56, 30}, {66, 30}, {66, 2}, {82, 2}, {82, 2}}, color = {0, 0, 255}));\nconnect(l13.pin4, rl1.pin1) annotation(\n    Line(points = {{56, 24}, {62, 24}, {62, -4}, {82, -4}, {82, -4}}, color = {0, 0, 255}));\nconnect(l23.pin5, rl1.pin2) annotation(\n    Line(points = {{58, -30}, {66, -30}, {66, 2}, {82, 2}, {82, 2}}, color = {0, 0, 255}));\nconnect(l23.pin6, rl1.pin3) annotation(\n    Line(points = {{58, -24}, {72, -24}, {72, 8}, {82, 8}, {82, 8}}, color = {0, 0, 255}));\nconnect(l23.pin4, rl1.pin1) annotation(\n    Line(points = {{58, -36}, {62, -36}, {62, -4}, {82, -4}, {82, -4}}, color = {0, 0, 255}));\n  annotation(\n    Diagram);\nend Microgrid;\n"
  },
  {
    "path": "omg_grid/Grids/Network.mo",
    "content": "within omg_grid.Grids;\n\nmodel Network\n  omg_grid.Inverters.Inverter inverter1 annotation(\n    Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LC lc1 annotation(\n    Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Inverters.Inverter inverter2 annotation(\n    Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LC lc2 annotation(\n    Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LCL lcl1 annotation(\n    Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nomg_grid.Loads.RL rl1 annotation(\n    Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(lc1.pin6, lc2.pin3) annotation(\n    Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255}));\n  connect(lc1.pin5, lc2.pin2) annotation(\n    Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255}));\n  connect(lc1.pin4, lc2.pin1) annotation(\n    Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255}));\n  connect(inverter1.pin3, lc1.pin3) annotation(\n    Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255}));\n  connect(inverter1.pin2, lc1.pin2) annotation(\n    Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255}));\n  connect(inverter1.pin1, lc1.pin1) annotation(\n    Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255}));\n  connect(i1p1, inverter1.u1) annotation(\n    Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n  connect(i1p2, inverter1.u2) annotation(\n    Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n  connect(i1p3, inverter1.u3) annotation(\n    Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n  connect(i2p3, inverter2.u3) annotation(\n    Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127}));\n  connect(i2p2, inverter2.u2) annotation(\n    Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127}));\n  connect(i2p1, inverter2.u1) annotation(\n    Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127}));\n  connect(inverter2.pin3, lcl1.pin3) annotation(\n    Line(points = {{-60, -24}, {-42, -24}}, color = {0, 0, 255}));\n  connect(inverter2.pin2, lcl1.pin2) annotation(\n    Line(points = {{-60, -30}, {-42, -30}}, color = {0, 0, 255}));\n  connect(inverter2.pin1, lcl1.pin1) annotation(\n    Line(points = {{-60, -36}, {-42, -36}}, color = {0, 0, 255}));\n  connect(lcl1.pin6, lc2.pin3) annotation(\n    Line(points = {{-22, -24}, {-6, -24}, {-6, 36}, {20, 36}}, color = {0, 0, 255}));\n  connect(lcl1.pin5, lc2.pin2) annotation(\n    Line(points = {{-22, -30}, {0, -30}, {0, 30}, {20, 30}}, color = {0, 0, 255}));\n  connect(lcl1.pin4, lc2.pin1) annotation(\n    Line(points = {{-22, -36}, {6, -36}, {6, 24}, {20, 24}}, color = {0, 0, 255}));\nconnect(lc2.pin6, rl1.pin3) annotation(\n    Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255}));\nconnect(lc2.pin5, rl1.pin2) annotation(\n    Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255}));\nconnect(lc2.pin4, rl1.pin1) annotation(\n    Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255}));\n  annotation(\n    Diagram);\nend Network;\n"
  },
  {
    "path": "omg_grid/Grids/NetworkSineTest.bak-mo",
    "content": "within omg_grid.Grids;\n\nmodel NetworkSineTest\n\n  Modelica.Blocks.Sources.Sine sine(amplitude = 230, freqHz = 50) annotation(\n    Placement(visible = true, transformation(origin = {-76, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine1(amplitude = 230, freqHz = 50, phase = 2.0944) annotation(\n    Placement(visible = true, transformation(origin = {-76, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine2(amplitude = 230, freqHz = 50, phase = 4.18879) annotation(\n    Placement(visible = true, transformation(origin = {-76, -16}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine3(amplitude = 230, freqHz = 50) annotation(\n    Placement(visible = true, transformation(origin = {-76, 14}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine4(amplitude = 230, freqHz = 50, phase = 2.0944) annotation(\n    Placement(visible = true, transformation(origin = {-76, 48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Sine sine5(amplitude = 230, freqHz = 50, phase = 4.18879) annotation(\n    Placement(visible = true, transformation(origin = {-76, 80}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nomg_grid.Grids.Network network1 annotation(\n    Placement(visible = true, transformation(origin = {38, 2}, extent = {{-46, -46}, {46, 46}}, rotation = 0)));\nequation\n  connect(sine5.y, network1.i1p3) annotation(\n    Line(points = {{-64, 80}, {-34, 80}, {-34, 22}, {-10, 22}, {-10, 22}}, color = {0, 0, 127}));\nconnect(sine4.y, network1.i1p2) annotation(\n    Line(points = {{-64, 48}, {-40, 48}, {-40, 16}, {-10, 16}, {-10, 16}}, color = {0, 0, 127}));\nconnect(sine3.y, network1.i1p1) annotation(\n    Line(points = {{-64, 14}, {-34, 14}, {-34, 10}, {-10, 10}, {-10, 10}}, color = {0, 0, 127}));\nconnect(sine2.y, network1.i2p3) annotation(\n    Line(points = {{-64, -16}, {-64, -16}, {-64, -6}, {-10, -6}, {-10, -6}}, color = {0, 0, 127}));\nconnect(sine1.y, network1.i2p2) annotation(\n    Line(points = {{-64, -48}, {-50, -48}, {-50, -12}, {-10, -12}, {-10, -12}, {-10, -12}}, color = {0, 0, 127}));\nconnect(sine.y, network1.i2p1) annotation(\n    Line(points = {{-64, -82}, {-32, -82}, {-32, -18}, {-10, -18}, {-10, -18}}, color = {0, 0, 127}));\nend NetworkSineTest;\n"
  },
  {
    "path": "omg_grid/Grids/NetworkSingleInverter.mo",
    "content": "within omg_grid.Grids;\n\nmodel NetworkSingleInverter\n  omg_grid.Inverters.Inverter inverter1 annotation(\n    Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LC lc1(C1 = 0.00002, C2 = 0.00002, C3 = 0.00002, L1 = 0.002, L2 = 0.002, L3 = 0.002)  annotation(\n    Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\nomg_grid.Loads.RL rl1 annotation(\n    Placement(visible = true, transformation(origin = {24, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\nconnect(inverter1.pin3, lc1.pin3) annotation(\n    Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255}));\nconnect(inverter1.pin2, lc1.pin2) annotation(\n    Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255}));\nconnect(inverter1.pin1, lc1.pin1) annotation(\n    Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255}));\n  connect(i1p1, inverter1.u1) annotation(\n    Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n  connect(i1p2, inverter1.u2) annotation(\n    Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n  connect(i1p3, inverter1.u3) annotation(\n    Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\nconnect(lc1.pin6, rl1.pin3) annotation(\n    Line(points = {{-20, 36}, {14, 36}}, color = {0, 0, 255}));\nconnect(lc1.pin5, rl1.pin2) annotation(\n    Line(points = {{-20, 30}, {14, 30}}, color = {0, 0, 255}));\nconnect(lc1.pin4, rl1.pin1) annotation(\n    Line(points = {{-20, 24}, {14, 24}}, color = {0, 0, 255}));\n  annotation(\n    Diagram);\nend NetworkSingleInverter;\n"
  },
  {
    "path": "omg_grid/Grids/PLL.bak-mo",
    "content": "within omg_grid.Grids;\n\nmodel PLL\n  Modelica.Electrical.Analog.Interfaces.Pin a annotation(\n    Placement(visible = true, transformation(origin = {-100, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin b annotation(\n    Placement(visible = true, transformation(origin = {-100, 16}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 16}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin c annotation(\n    Placement(visible = true, transformation(origin = {-100, -14}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -14}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground annotation(\n    Placement(visible = true, transformation(origin = {-86, 62}, extent = {{-6, -6}, {6, 6}}, rotation = 180)));\n  Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_c annotation(\n    Placement(visible = true, transformation(origin = {-88, -8}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n  Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_a annotation(\n    Placement(visible = true, transformation(origin = {-86, 50}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n  Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_b annotation(\n    Placement(visible = true, transformation(origin = {-88, 22}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n  omg_grid.Transformations.ABC2AlphaBeta abc2AlphaBeta annotation(\n    Placement(visible = true, transformation(origin = {-62, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin annotation(\n    Placement(visible = true, transformation(origin = {-10, -6}, extent = {{-4, -4}, {4, 4}}, rotation = 180)));\n  Modelica.Blocks.Math.Cos cos annotation(\n    Placement(visible = true, transformation(origin = {-10, -18}, extent = {{-4, -4}, {4, 4}}, rotation = 180)));\n  Modelica.Blocks.Math.Gain Norm_U_ref_alpha(k = 1 / (230 * 1.414)) annotation(\n    Placement(visible = true, transformation(origin = {-33, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain Norm_U_ref_beta(k = 1 / (230 * 1.414)) annotation(\n    Placement(visible = true, transformation(origin = {-33, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Product alphaSin annotation(\n    Placement(visible = true, transformation(origin = {-7, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Product betaCos annotation(\n    Placement(visible = true, transformation(origin = {-9, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add(k1 = -1, k2 = +1) annotation(\n    Placement(visible = true, transformation(origin = {12, 24}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Continuous.PI pi(T = 0.2, k = 15) annotation(\n    Placement(visible = true, transformation(origin = {26, 24}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add_freq_nom_delta_f(k1 = +1, k2 = +1) annotation(\n    Placement(visible = true, transformation(origin = {48, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Sources.Constant f_nom(k = 50) annotation(\n    Placement(visible = true, transformation(origin = {28, 4}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Continuous.Integrator f2theta(y_start = 0) annotation(\n    Placement(visible = true, transformation(origin = {64, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain deg2rad(k = 2 * 3.1416) annotation(\n    Placement(visible = true, transformation(origin = {78, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\nequation\n  connect(a, voltageSensor_a.p) annotation(\n    Line(points = {{-100, 44}, {-86, 44}}, color = {0, 0, 255}));\n  connect(b, voltageSensor_b.p) annotation(\n    Line(points = {{-100, 16}, {-88, 16}}, color = {0, 0, 255}));\n  connect(c, voltageSensor_c.p) annotation(\n    Line(points = {{-100, -14}, {-88, -14}}, color = {0, 0, 255}));\n  connect(voltageSensor_a.n, ground.p) annotation(\n    Line(points = {{-86, 56}, {-86, 56}}, color = {0, 0, 255}));\n  connect(voltageSensor_b.n, ground.p) annotation(\n    Line(points = {{-88, 28}, {-88, 42}, {-86, 42}, {-86, 56}}, color = {0, 0, 255}));\n  connect(voltageSensor_c.n, ground.p) annotation(\n    Line(points = {{-88, -2}, {-88, 27}, {-86, 27}, {-86, 56}}, color = {0, 0, 255}));\n  connect(abc2AlphaBeta.b, voltageSensor_b.v) annotation(\n    Line(points = {{-72, 21}, {-74, 21}, {-74, 22}, {-82, 22}}, color = {0, 0, 127}));\n  connect(abc2AlphaBeta.a, voltageSensor_a.v) annotation(\n    Line(points = {{-72, 24}, {-76, 24}, {-76, 50}, {-80, 50}}, color = {0, 0, 127}));\n  connect(abc2AlphaBeta.c, voltageSensor_c.v) annotation(\n    Line(points = {{-72, 18}, {-76, 18}, {-76, -8}, {-82, -8}}, color = {0, 0, 127}));\n  connect(Norm_U_ref_alpha.u, abc2AlphaBeta.alpha) annotation(\n    Line(points = {{-37, 29}, {-40, 29}, {-40, 26}, {-52, 26}}, color = {0, 0, 127}));\n  connect(Norm_U_ref_beta.u, abc2AlphaBeta.beta) annotation(\n    Line(points = {{-37, 15}, {-42, 15}, {-42, 17}, {-52, 17}}, color = {0, 0, 127}));\n  connect(Norm_U_ref_alpha.y, alphaSin.u1) annotation(\n    Line(points = {{-30, 30}, {-11, 30}, {-11, 31}}, color = {0, 0, 127}));\n  connect(Norm_U_ref_beta.y, betaCos.u1) annotation(\n    Line(points = {{-30, 16}, {-13, 16}, {-13, 17}}, color = {0, 0, 127}));\n  connect(sin.y, alphaSin.u2) annotation(\n    Line(points = {{-14, -6}, {-22, -6}, {-22, 27}, {-11, 27}}, color = {0, 0, 127}));\n  connect(cos.y, betaCos.u2) annotation(\n    Line(points = {{-14, -18}, {-18, -18}, {-18, 13}, {-13, 13}}, color = {0, 0, 127}));\n  connect(add.u1, alphaSin.y) annotation(\n    Line(points = {{7, 26}, {3.5, 26}, {3.5, 30}, {-4, 30}}, color = {0, 0, 127}));\n  connect(betaCos.y, add.u2) annotation(\n    Line(points = {{-6, 16}, {4, 16}, {4, 22}, {7, 22}}, color = {0, 0, 127}));\n  connect(pi.u, add.y) annotation(\n    Line(points = {{19, 24}, {16, 24}}, color = {0, 0, 127}));\n  connect(add_freq_nom_delta_f.u1, pi.y) annotation(\n    Line(points = {{43, 24}, {33, 24}}, color = {0, 0, 127}));\n  connect(f_nom.y, add_freq_nom_delta_f.u2) annotation(\n    Line(points = {{32, 4}, {36, 4}, {36, 20}, {43, 20}}, color = {0, 0, 127}));\n  connect(f2theta.u, add_freq_nom_delta_f.y) annotation(\n    Line(points = {{59, 22}, {52, 22}}, color = {0, 0, 127}));\n  connect(deg2rad.u, f2theta.y) annotation(\n    Line(points = {{74, 22}, {68, 22}, {68, 22}, {68, 22}}, color = {0, 0, 127}));\n  connect(deg2rad.y, sin.u) annotation(\n    Line(points = {{82, 22}, {92, 22}, {92, -6}, {-6, -6}, {-6, -6}}, color = {0, 0, 127}));\n  connect(cos.u, deg2rad.y) annotation(\n    Line(points = {{-6, -18}, {4, -18}, {4, -6}, {92, -6}, {92, 22}, {82, 22}, {82, 22}, {82, 22}}, color = {0, 0, 127}));\nend PLL;\n"
  },
  {
    "path": "omg_grid/Grids/PLL_Network.mo",
    "content": "within omg_grid.Grids;\n\nmodel PLL_Network\n  omg_grid.Inverters.Inverter inverter1 annotation(\n    Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LC lc1 annotation(\n    Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Loads.RC rc1 annotation(\n    Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Inverters.Inverter inverter2 annotation(\n    Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LC lc2 annotation(\n    Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LCL lcl1 annotation(\n    Placement(visible = true, transformation(origin = {-30, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.PLLs.PLL pll annotation(\n    Placement(visible = true, transformation(origin = {20, -62}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(lc2.pin4, rc1.pin1) annotation(\n    Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255}));\n  connect(lc2.pin5, rc1.pin2) annotation(\n    Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255}));\n  connect(lc2.pin6, rc1.pin3) annotation(\n    Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255}));\n  connect(lc1.pin6, lc2.pin3) annotation(\n    Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255}));\n  connect(lc1.pin5, lc2.pin2) annotation(\n    Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255}));\n  connect(lc1.pin4, lc2.pin1) annotation(\n    Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255}));\n  connect(inverter1.pin3, lc1.pin3) annotation(\n    Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255}));\n  connect(inverter1.pin2, lc1.pin2) annotation(\n    Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255}));\n  connect(inverter1.pin1, lc1.pin1) annotation(\n    Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255}));\n  connect(i1p1, inverter1.u1) annotation(\n    Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n  connect(i1p2, inverter1.u2) annotation(\n    Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n  connect(i1p3, inverter1.u3) annotation(\n    Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n  connect(i2p3, inverter2.u3) annotation(\n    Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127}));\n  connect(i2p2, inverter2.u2) annotation(\n    Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127}));\n  connect(i2p1, inverter2.u1) annotation(\n    Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127}));\n  connect(inverter2.pin3, lcl1.pin3) annotation(\n    Line(points = {{-60, -24}, {-40, -24}, {-40, -24}, {-40, -24}}, color = {0, 0, 255}));\n  connect(inverter2.pin2, lcl1.pin2) annotation(\n    Line(points = {{-60, -30}, {-40, -30}, {-40, -30}, {-40, -30}}, color = {0, 0, 255}));\n  connect(inverter2.pin1, lcl1.pin1) annotation(\n    Line(points = {{-60, -36}, {-40, -36}, {-40, -36}, {-40, -36}}, color = {0, 0, 255}));\n  connect(lcl1.pin6, lc2.pin3) annotation(\n    Line(points = {{-20, -24}, {-4, -24}, {-4, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255}));\n  connect(lcl1.pin5, lc2.pin2) annotation(\n    Line(points = {{-20, -30}, {0, -30}, {0, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255}));\n  connect(lcl1.pin4, lc2.pin1) annotation(\n    Line(points = {{-20, -36}, {6, -36}, {6, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255}));\n  connect(pll.a, lcl1.pin6) annotation(\n    Line(points = {{10, -58}, {-14, -58}, {-14, -24}, {-20, -24}}, color = {0, 0, 255}));\n  connect(pll.b, lcl1.pin5) annotation(\n    Line(points = {{10, -60}, {-16, -60}, {-16, -30}, {-20, -30}}, color = {0, 0, 255}));\n  connect(pll.c, lcl1.pin4) annotation(\n    Line(points = {{10, -63}, {-18, -63}, {-18, -36}, {-20, -36}}, color = {0, 0, 255}));\n  annotation(\n    Diagram);\nend PLL_Network;\n"
  },
  {
    "path": "omg_grid/Grids/RLC_Network.mo",
    "content": "within omg_grid.Grids;\n\nmodel RLC_Network\n  omg_grid.Inverters.Inverter inverter1 annotation(\n    Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LC lc1 annotation(\n    Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Inverters.Inverter inverter2 annotation(\n    Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LC lc2 annotation(\n    Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  omg_grid.Filter.IdealFilter.LCL lcl1 annotation(\n    Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Loads.RLC rlc annotation(\n    Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(lc1.pin6, lc2.pin3) annotation(\n    Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255}));\n  connect(lc1.pin5, lc2.pin2) annotation(\n    Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255}));\n  connect(lc1.pin4, lc2.pin1) annotation(\n    Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255}));\n  connect(inverter1.pin3, lc1.pin3) annotation(\n    Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255}));\n  connect(inverter1.pin2, lc1.pin2) annotation(\n    Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255}));\n  connect(inverter1.pin1, lc1.pin1) annotation(\n    Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255}));\n  connect(i1p1, inverter1.u1) annotation(\n    Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n  connect(i1p2, inverter1.u2) annotation(\n    Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n  connect(i1p3, inverter1.u3) annotation(\n    Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n  connect(i2p3, inverter2.u3) annotation(\n    Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127}));\n  connect(i2p2, inverter2.u2) annotation(\n    Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127}));\n  connect(i2p1, inverter2.u1) annotation(\n    Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127}));\n  connect(inverter2.pin3, lcl1.pin3) annotation(\n    Line(points = {{-60, -24}, {-42, -24}}, color = {0, 0, 255}));\n  connect(inverter2.pin2, lcl1.pin2) annotation(\n    Line(points = {{-60, -30}, {-42, -30}}, color = {0, 0, 255}));\n  connect(inverter2.pin1, lcl1.pin1) annotation(\n    Line(points = {{-60, -36}, {-42, -36}}, color = {0, 0, 255}));\n  connect(lcl1.pin6, lc2.pin3) annotation(\n    Line(points = {{-22, -24}, {-6, -24}, {-6, 36}, {20, 36}}, color = {0, 0, 255}));\n  connect(lcl1.pin5, lc2.pin2) annotation(\n    Line(points = {{-22, -30}, {0, -30}, {0, 30}, {20, 30}}, color = {0, 0, 255}));\n  connect(lcl1.pin4, lc2.pin1) annotation(\n    Line(points = {{-22, -36}, {6, -36}, {6, 24}, {20, 24}}, color = {0, 0, 255}));\n  connect(lc2.pin6, rlc.pin3) annotation(\n    Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255}));\n  connect(lc2.pin5, rlc.pin2) annotation(\n    Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255}));\n  connect(lc2.pin4, rlc.pin1) annotation(\n    Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255}));\n  annotation(\n    Diagram);\nend RLC_Network;\n"
  },
  {
    "path": "omg_grid/Grids/SingleModel.mo",
    "content": "within omg_grid.Grids;\n\nmodel SingleModel\n  omg_grid.Inverters.Inverter inverter1 annotation(\n    Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  omg_grid.Inverters.Inverter inverter2 annotation(\n    Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i2p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\nomg_grid.Loads.R r(R1 = 100, R2 = 100, R3 = 100)  annotation(\n    Placement(visible = true, transformation(origin = {8, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nomg_grid.Filter.IdealFilter.L l annotation(\n    Placement(visible = true, transformation(origin = {-40, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(i1p1, inverter1.u1) annotation(\n    Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n  connect(i1p2, inverter1.u2) annotation(\n    Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n  connect(i1p3, inverter1.u3) annotation(\n    Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n  connect(i2p3, inverter2.u3) annotation(\n    Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127}));\n  connect(i2p2, inverter2.u2) annotation(\n    Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127}));\n  connect(i2p1, inverter2.u1) annotation(\n    Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127}));\n  connect(inverter1.pin3, r.pin3) annotation(\n    Line(points = {{-60, 36}, {-2, 36}, {-2, 36}, {-2, 36}}, color = {0, 0, 255}));\n  connect(inverter1.pin2, r.pin2) annotation(\n    Line(points = {{-60, 30}, {-2, 30}, {-2, 30}, {-2, 30}}, color = {0, 0, 255}));\n  connect(inverter1.pin1, r.pin1) annotation(\n    Line(points = {{-60, 24}, {-2, 24}, {-2, 24}, {-2, 24}}, color = {0, 0, 255}));\nconnect(inverter2.pin3, l.pin3) annotation(\n    Line(points = {{-60, -24}, {-50, -24}}, color = {0, 0, 255}));\nconnect(inverter2.pin2, l.pin2) annotation(\n    Line(points = {{-60, -30}, {-50, -30}}, color = {0, 0, 255}));\nconnect(inverter2.pin1, l.pin1) annotation(\n    Line(points = {{-60, -36}, {-50, -36}}, color = {0, 0, 255}));\nconnect(l.pin6, r.pin3) annotation(\n    Line(points = {{-30, -24}, {-20, -24}, {-20, 36}, {-2, 36}, {-2, 36}, {-2, 36}}, color = {0, 0, 255}));\nconnect(l.pin5, r.pin2) annotation(\n    Line(points = {{-30, -30}, {-14, -30}, {-14, 30}, {-2, 30}, {-2, 30}}, color = {0, 0, 255}));\nconnect(l.pin4, r.pin1) annotation(\n    Line(points = {{-30, -36}, {-8, -36}, {-8, 24}, {-2, 24}, {-2, 24}, {-2, 24}}, color = {0, 0, 255}));\n  annotation(\n    Diagram);\nend SingleModel;\n"
  },
  {
    "path": "omg_grid/Grids/Testbench_SC2.mo",
    "content": "within omg_grid.Grids;\n\nmodel Testbench_SC2\n  omg_grid.Inverters.Inverter inverter1(v_DC = 60)  annotation(\n    Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n    Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n    Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n    Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\nomg_grid.Filter.LossesFilter.L rl(L1 = 0.0023, L2 = 0.0023, L3 = 0.0023, R1 = 0.170, R2 = 0.170, R3 = 0.170)  annotation(\n    Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(i1p1, inverter1.u1) annotation(\n    Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n  connect(i1p2, inverter1.u2) annotation(\n    Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n  connect(i1p3, inverter1.u3) annotation(\n    Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\nconnect(inverter1.pin3, rl.pin3) annotation(\n    Line(points = {{-60, 36}, {-40, 36}, {-40, 36}, {-40, 36}}, color = {0, 0, 255}));\nconnect(inverter1.pin2, rl.pin2) annotation(\n    Line(points = {{-60, 30}, {-40, 30}, {-40, 30}, {-40, 30}}, color = {0, 0, 255}));\nconnect(inverter1.pin1, rl.pin1) annotation(\n    Line(points = {{-60, 24}, {-40, 24}, {-40, 24}, {-40, 24}}, color = {0, 0, 255}));\nconnect(rl.pin6, rl.pin4) annotation(\n    Line(points = {{-20, 36}, {-14, 36}, {-14, 24}, {-20, 24}, {-20, 24}}, color = {0, 0, 255}));\nconnect(rl.pin5, rl.pin6) annotation(\n    Line(points = {{-20, 30}, {-14, 30}, {-14, 36}, {-20, 36}, {-20, 36}}, color = {0, 0, 255}));\nconnect(rl.pin4, rl.pin5) annotation(\n    Line(points = {{-20, 24}, {-14, 24}, {-14, 30}, {-20, 30}, {-20, 30}}, color = {0, 0, 255}));\n  annotation(\n    Diagram);\nend Testbench_SC2;\n"
  },
  {
    "path": "omg_grid/Grids/package.mo",
    "content": "within omg_grid;\n\npackage Grids \nextends Modelica.Icons.Package;\nannotation (Icon(coordinateSystem(preserveAspectRatio=true, extent={{-100.0,-100.0},{100.0,100.0}}), graphics={\n    Rectangle(\n      origin={20.3125,82.8571},\n      extent={{-45.3125,-57.8571},{4.6875,-27.8571}}),\n    Line(\n      origin={8.0,48.0},\n      points={{32.0,-58.0},{72.0,-58.0}}),\n    Line(\n      origin={9.0,54.0},\n      points={{31.0,-49.0},{71.0,-49.0}}),\n    Line(\n      origin={-2.0,55.0},\n      points={{-83.0,-50.0},{-33.0,-50.0}}),\n    Line(\n      origin={-3.0,45.0},\n      points={{-72.0,-55.0},{-42.0,-55.0}}),\n    Line(\n      origin={1.0,50.0},\n      points={{-61.0,-45.0},{-61.0,-10.0},{-26.0,-10.0}}),\n    Line(\n      origin={7.0,50.0},\n      points={{18.0,-10.0},{53.0,-10.0},{53.0,-45.0}}),\n    Line(\n      origin={6.2593,48.0},\n      points={{53.7407,-58.0},{53.7407,-93.0},{-66.2593,-93.0},{-66.2593,-58.0}})}));\n\nend Grids;\n"
  },
  {
    "path": "omg_grid/Grids/package.order",
    "content": "Network\nNetworkSingleInverter\nPLL_Network\nRLC_Network\nSingleModel\nMicrogrid\nTestbench_SC2\nPaper_SC\nPaper_Loadstep\n"
  },
  {
    "path": "omg_grid/Inverters/Inverter.mo",
    "content": "within omg_grid.Inverters;\n\nmodel Inverter\n  parameter Real v_DC = 1000;\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {-74, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage1 annotation(\n    Placement(visible = true, transformation(origin = {-74, -42}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage2 annotation(\n    Placement(visible = true, transformation(origin = {-74, 18}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage3 annotation(\n    Placement(visible = true, transformation(origin = {-74, 78}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Blocks.Interfaces.RealInput u1 annotation(\n    Placement(visible = true, transformation(origin = {-104, -60}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -60}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput u3 annotation(\n    Placement(visible = true, transformation(origin = {-104, 60}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 60}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput u2 annotation(\n    Placement(visible = true, transformation(origin = {-104, 4.44089e-16}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 4.44089e-16}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain3(k = v_DC) annotation(\n    Placement(visible = true, transformation(origin = {-26, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain1(k = v_DC) annotation(\n    Placement(visible = true, transformation(origin = {-26, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain2(k = v_DC) annotation(\n    Placement(visible = true, transformation(origin = {-26, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(signalVoltage2.p, pin2) annotation(\n    Line(points = {{-74, 28}, {80, 28}, {80, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(signalVoltage3.p, pin3) annotation(\n    Line(points = {{-74, 88}, {80, 88}, {80, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(signalVoltage1.p, pin1) annotation(\n    Line(points = {{-74, -32}, {80, -32}, {80, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(signalVoltage3.n, ground1.p) annotation(\n    Line(points = {{-74, 68}, {-74, 48}, {-82, 48}, {-82, -72}, {-74, -72}}, color = {0, 0, 255}));\n  connect(signalVoltage2.n, ground1.p) annotation(\n    Line(points = {{-74, 8}, {-82, 8}, {-82, -72}, {-74, -72}}, color = {0, 0, 255}));\n  connect(signalVoltage1.n, ground1.p) annotation(\n    Line(points = {{-74, -52}, {-74, -72}}, color = {0, 0, 255}));\n/*  connect(signalVoltage1.v, regler1) annotation(\n    Line);\n  connect(signalVoltage2.v, regler2) annotation(\n    Line);\n  connect(signalVoltage3.v, regler3) annotation(\n    Line);\n*/\n  connect(u1, gain1.u) annotation(\n    Line(points = {{-104, -60}, {-38, -60}, {-38, -60}, {-38, -60}}, color = {0, 0, 127}));\n  connect(gain1.y, signalVoltage1.v) annotation(\n    Line(points = {{-14, -60}, {-6, -60}, {-6, -42}, {-60, -42}, {-60, -42}, {-62, -42}}, color = {0, 0, 127}));\n  connect(u2, gain2.u) annotation(\n    Line(points = {{-104, 0}, {-38, 0}, {-38, 0}, {-38, 0}}, color = {0, 0, 127}));\n  connect(gain2.y, signalVoltage2.v) annotation(\n    Line(points = {{-14, 0}, {-6, 0}, {-6, 18}, {-62, 18}, {-62, 18}}, color = {0, 0, 127}));\n  connect(u3, gain3.u) annotation(\n    Line(points = {{-104, 60}, {-38, 60}, {-38, 60}, {-38, 60}}, color = {0, 0, 127}));\n  connect(gain3.y, signalVoltage3.v) annotation(\n    Line(points = {{-14, 60}, {-6, 60}, {-6, 78}, {-62, 78}, {-62, 78}}, color = {0, 0, 127}));\n  annotation(\n    uses(Modelica(version = \"3.2.3\")));\nend Inverter;\n"
  },
  {
    "path": "omg_grid/Inverters/package.mo",
    "content": "within omg_grid;\npackage Inverters \nextends Modelica.Icons.Package;\n\nannotation (Icon(graphics={Line(\n          points={{-80,-2},{-68.7,32.2},{-61.5,51.1},{-55.1,64.4},{-49.4,72.6},\n              {-43.8,77.1},{-38.2,77.8},{-32.6,74.6},{-26.9,67.7},{-21.3,57.4},\n              {-14.9,42.1},{-6.83,19.2},{10.1,-32.8},{17.3,-52.2},{23.7,-66.2},\n              {29.3,-75.1},{35,-80.4},{40.6,-82},{46.2,-79.6},{51.9,-73.5},{\n              57.5,-63.9},{63.9,-49.2},{72,-26.8},{80,-2}},\n          color={95,95,95},\n          smooth=Smooth.Bezier)}));\nend Inverters;\n"
  },
  {
    "path": "omg_grid/Inverters/package.order",
    "content": "Inverter\n"
  },
  {
    "path": "omg_grid/Loads/C.mo",
    "content": "within omg_grid.Loads;\n\nmodel C\n  parameter SI.Capacitance C1 = 0.00001;\n  parameter SI.Capacitance C2 = 0.00001;\n  parameter SI.Capacitance C3 = 0.00001;\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-102, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-102, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {-34, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {4, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {40, 38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {4, -84}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(capacitor3.p, pin3) annotation(\n    Line(points = {{40, 48}, {40, 60}, {-100, 60}}, color = {0, 0, 255}));\n  connect(pin2, capacitor2.p) annotation(\n    Line(points = {{-102, 0}, {4, 0}}, color = {0, 0, 255}));\n  connect(capacitor2.n, ground1.p) annotation(\n    Line(points = {{4, -20}, {4, -74}}, color = {0, 0, 255}));\n  connect(pin1, capacitor1.p) annotation(\n    Line(points = {{-100, -60}, {-67, -60}, {-67, -34}, {-34, -34}}, color = {0, 0, 255}));\n  connect(capacitor3.n, ground1.p) annotation(\n    Line(points = {{40, 28}, {40, -54}, {4, -54}, {4, -74}}, color = {0, 0, 255}));\n  connect(capacitor1.n, ground1.p) annotation(\n    Line(points = {{-34, -54}, {4, -54}, {4, -74}, {4, -74}}, color = {0, 0, 255}));\nend C;\n"
  },
  {
    "path": "omg_grid/Loads/L.mo",
    "content": "within omg_grid.Loads;\n\nmodel L\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-48, -50}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {0, -16}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {50, 50}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nequation\n  connect(inductor3.n, ground1.p) annotation(\n    Line(points = {{50, 40}, {50, 40}, {50, -60}, {0, -60}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n  connect(pin3, inductor3.p) annotation(\n    Line(points = {{-100, 60}, {50, 60}, {50, 60}, {50, 60}}, color = {0, 0, 255}));\n  connect(inductor2.n, ground1.p) annotation(\n    Line(points = {{0, -26}, {0, -26}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n  connect(pin2, inductor2.p) annotation(\n    Line(points = {{-100, 0}, {0, 0}, {0, -6}}, color = {0, 0, 255}));\n  connect(inductor1.n, ground1.p) annotation(\n    Line(points = {{-48, -60}, {0, -60}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n  connect(pin1, inductor1.p) annotation(\n    Line(points = {{-100, -60}, {-78, -60}, {-78, -40}, {-48, -40}, {-48, -40}, {-48, -40}}, color = {0, 0, 255}));\nend L;\n"
  },
  {
    "path": "omg_grid/Loads/LC.mo",
    "content": "within omg_grid.Loads;\n\nmodel LC\n  parameter SI.Capacitance C1(start = 0.00001);\n  parameter SI.Capacitance C2(start = 0.00001);\n  parameter SI.Capacitance C3(start = 0.00001);\n  parameter SI.Inductance L1(start = 0.001);\n  parameter SI.Inductance L2(start = 0.001);\n  parameter SI.Inductance L3(start = 0.001);\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {-56, -18}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {0, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {56, 42}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-56, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {0, -24}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {56, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nequation\n  connect(capacitor3.n, inductor3.p) annotation(\n    Line(points = {{56, 32}, {56, 32}, {56, 18}, {56, 18}}, color = {0, 0, 255}));\n  connect(inductor3.n, ground1.p) annotation(\n    Line(points = {{56, -2}, {56, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n  connect(capacitor1.n, inductor1.p) annotation(\n    Line(points = {{-56, -28}, {-56, -28}, {-56, -34}, {-56, -34}}, color = {0, 0, 255}));\n  connect(inductor1.n, ground1.p) annotation(\n    Line(points = {{-56, -54}, {-56, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n  connect(capacitor1.p, pin1) annotation(\n    Line(points = {{-56, -8}, {-56, 0}, {-88, 0}, {-88, -60}, {-100, -60}}, color = {0, 0, 255}));\n  connect(capacitor2.p, pin2) annotation(\n    Line(points = {{0, 18}, {0, 24}, {-94, 24}, {-94, 0}, {-100, 0}}, color = {0, 0, 255}));\n  connect(capacitor2.n, inductor2.p) annotation(\n    Line(points = {{0, -2}, {0, -2}, {0, -14}, {0, -14}}, color = {0, 0, 255}));\n  connect(inductor2.n, ground1.p) annotation(\n    Line(points = {{0, -34}, {0, -76}}, color = {0, 0, 255}));\n  connect(pin3, capacitor3.p) annotation(\n    Line(points = {{-100, 60}, {56, 60}, {56, 52}}, color = {0, 0, 255}));\n  connect(capacitor3.p, pin3) annotation(\n    Line(points = {{56, 52}, {56, 60}, {-100, 60}}, color = {0, 0, 255}));\nend LC;\n"
  },
  {
    "path": "omg_grid/Loads/R.mo",
    "content": "within omg_grid.Loads;\n\nmodel R\n  parameter SI.Resistance R1 = 20;\n  parameter SI.Resistance R2 = 20;\n  parameter SI.Resistance R3 = 20;\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation(\n    Placement(visible = true, transformation(origin = {-66, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R1) annotation(\n    Placement(visible = true, transformation(origin = {-66, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R1) annotation(\n    Placement(visible = true, transformation(origin = {-66, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(resistor3.n, ground1.p) annotation(\n    Line(points = {{-56, 60}, {0, 60}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n  connect(resistor2.n, ground1.p) annotation(\n    Line(points = {{-56, 0}, {-56, 0}, {-56, 0}, {0, 0}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n  connect(resistor1.n, ground1.p) annotation(\n    Line(points = {{-56, -60}, {0, -60}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n  connect(pin1, resistor1.p) annotation(\n    Line(points = {{-100, -60}, {-76, -60}, {-76, -60}, {-76, -60}}, color = {0, 0, 255}));\n  connect(pin2, resistor2.p) annotation(\n    Line(points = {{-100, 0}, {-100, 0}, {-100, 0}, {-76, 0}}, color = {0, 0, 255}));\n  connect(pin3, resistor3.p) annotation(\n    Line(points = {{-100, 60}, {-76, 60}, {-76, 60}, {-76, 60}}, color = {0, 0, 255}));\nend R;\n"
  },
  {
    "path": "omg_grid/Loads/RC.mo",
    "content": "within omg_grid.Loads;\n\nmodel RC\n  parameter SI.Resistance R1 = 20;\n  parameter SI.Resistance R2 = 20;\n  parameter SI.Resistance R3 = 20;\n  parameter SI.Capacitance C1 = 0.00001;\n  parameter SI.Capacitance C2 = 0.00001;\n  parameter SI.Capacitance C3 = 0.00001;\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {-66, -48}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {-32, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {48, 0}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation(\n    Placement(visible = true, transformation(origin = {-50, -48}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation(\n    Placement(visible = true, transformation(origin = {0, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation(\n    Placement(visible = true, transformation(origin = {72, -2}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nequation\n  connect(resistor2.n, ground1.p) annotation(\n    Line(points = {{0, -20}, {0, -20}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n  connect(pin2, resistor2.p) annotation(\n    Line(points = {{-100, 0}, {0, 0}, {0, 0}, {0, 0}}, color = {0, 0, 255}));\n  connect(resistor1.p, pin1) annotation(\n    Line(points = {{-50, -38}, {-50, -38}, {-50, -22}, {-90, -22}, {-90, -60}, {-100, -60}, {-100, -60}}, color = {0, 0, 255}));\n  connect(resistor1.n, ground1.p) annotation(\n    Line(points = {{-50, -58}, {-50, -58}, {-50, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n  connect(resistor3.n, ground1.p) annotation(\n    Line(points = {{72, -12}, {72, -12}, {72, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n  connect(pin3, resistor3.p) annotation(\n    Line(points = {{-100, 60}, {68, 60}, {68, 60}, {72, 60}, {72, 8}, {72, 8}}, color = {0, 0, 255}));\n  connect(capacitor1.p, pin1) annotation(\n    Line(points = {{-66, -38}, {-66, -22}, {-90, -22}, {-90, -60}, {-100, -60}}, color = {0, 0, 255}));\n  connect(capacitor3.p, pin3) annotation(\n    Line(points = {{48, 10}, {48, 60}, {-100, 60}}, color = {0, 0, 255}));\n  connect(pin3, capacitor3.p) annotation(\n    Line(points = {{-100, 60}, {48, 60}, {48, 10}}, color = {0, 0, 255}));\n  connect(capacitor3.n, ground1.p) annotation(\n    Line(points = {{48, -10}, {48, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n  connect(capacitor2.p, pin2) annotation(\n    Line(points = {{-32, 0}, {-100, 0}}, color = {0, 0, 255}));\n  connect(capacitor2.n, ground1.p) annotation(\n    Line(points = {{-32, -20}, {-32, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n  connect(capacitor1.n, ground1.p) annotation(\n    Line(points = {{-66, -58}, {-66, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\nend RC;\n"
  },
  {
    "path": "omg_grid/Loads/RL.mo",
    "content": "within omg_grid.Loads;\n\nmodel RL\n  parameter SI.Resistance R1 = 20;\n  parameter SI.Resistance R2 = 20;\n  parameter SI.Resistance R3 = 20;\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-40, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {0, -18}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {60, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor1(R=R1) annotation(\n    Placement(visible = true, transformation(origin = {-40, -20}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor2(R=R2) annotation(\n    Placement(visible = true, transformation(origin = {0, 12}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor3(R=R3) annotation(\n    Placement(visible = true, transformation(origin = {60, 34}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nequation\n  connect(resistor1.n, inductor1.p) annotation(\n    Line(points = {{-40, -30}, {-40, -30}, {-40, -36}, {-40, -36}}, color = {0, 0, 255}));\n  connect(pin3, resistor3.p) annotation(\n    Line(points = {{-100, 60}, {60, 60}, {60, 44}, {60, 44}}, color = {0, 0, 255}));\n  connect(resistor3.n, inductor3.p) annotation(\n    Line(points = {{60, 24}, {60, 24}, {60, 24}, {60, 18}}, color = {0, 0, 255}));\n  connect(resistor2.n, inductor2.p) annotation(\n    Line(points = {{0, 2}, {0, 2}, {0, -8}, {0, -8}}, color = {0, 0, 255}));\n  connect(pin2, resistor2.p) annotation(\n    Line(points = {{-100, 0}, {-66, 0}, {-66, 22}, {0, 22}}, color = {0, 0, 255}));\n  connect(pin1, resistor1.p) annotation(\n    Line(points = {{-100, -60}, {-74, -60}, {-74, -10}, {-40, -10}, {-40, -10}}, color = {0, 0, 255}));\n  connect(inductor3.n, ground1.p) annotation(\n    Line(points = {{60, -2}, {60, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n  connect(inductor1.n, ground1.p) annotation(\n    Line(points = {{-40, -56}, {-40, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n  connect(inductor2.n, ground1.p) annotation(\n    Line(points = {{0, -28}, {0, -76}}, color = {0, 0, 255}));\nend RL;\n"
  },
  {
    "path": "omg_grid/Loads/RLC.mo",
    "content": "within omg_grid.Loads;\n\nmodel RLC\n  parameter SI.Resistance R1 = 20;\n  parameter SI.Resistance R2 = 20;\n  parameter SI.Resistance R3 = 20;\n  parameter SI.Capacitance C1 = 0.00001;\n  parameter SI.Capacitance C2 = 0.00001;\n  parameter SI.Capacitance C3 = 0.00001;\n  parameter SI.Inductance L1 = 0.001;\n  parameter SI.Inductance L2 = 0.001;\n  parameter SI.Inductance L3 = 0.001;\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n    Placement(visible = true, transformation(origin = {-74, -68}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n    Placement(visible = true, transformation(origin = {0, -30}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n    Placement(visible = true, transformation(origin = {74, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n    Placement(visible = true, transformation(origin = {-74, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n    Placement(visible = true, transformation(origin = {0, 2}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n    Placement(visible = true, transformation(origin = {74, -4}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation(\n    Placement(visible = true, transformation(origin = {-74, -20}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation(\n    Placement(visible = true, transformation(origin = {0, 36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation(\n    Placement(visible = true, transformation(origin = {74, 42}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\nequation\n  connect(resistor1.p, pin1) annotation(\n    Line(points = {{-74, -10}, {-100, -10}, {-100, -60}}, color = {0, 0, 255}));\n  connect(pin2, resistor2.p) annotation(\n    Line(points = {{-100, 0}, {-50, 0}, {-50, 46}, {0, 46}}, color = {0, 0, 255}));\n  connect(resistor3.p, pin3) annotation(\n    Line(points = {{74, 52}, {74, 52}, {74, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255}));\n  connect(capacitor2.n, ground1.p) annotation(\n    Line(points = {{0, -40}, {0, -76}}, color = {0, 0, 255}));\n  connect(capacitor3.n, ground1.p) annotation(\n    Line(points = {{74, -56}, {74, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n  connect(capacitor1.p, inductor1.n) annotation(\n    Line(points = {{-74, -58}, {-74, -58}, {-74, -54}, {-74, -54}}, color = {0, 0, 255}));\n  connect(resistor1.n, inductor1.p) annotation(\n    Line(points = {{-74, -30}, {-74, -30}, {-74, -30}, {-74, -34}}, color = {0, 0, 255}));\n  connect(resistor2.n, inductor2.p) annotation(\n    Line(points = {{0, 26}, {0, 26}, {0, 12}, {0, 12}}, color = {0, 0, 255}));\n  connect(inductor2.n, capacitor2.p) annotation(\n    Line(points = {{0, -8}, {0, -8}, {0, -8}, {0, -20}}, color = {0, 0, 255}));\n  connect(resistor3.n, inductor3.p) annotation(\n    Line(points = {{74, 32}, {74, 32}, {74, 6}, {74, 6}}, color = {0, 0, 255}));\n  connect(capacitor3.p, inductor3.n) annotation(\n    Line(points = {{74, -36}, {74, -36}, {74, -14}, {74, -14}}, color = {0, 0, 255}));\n  connect(capacitor1.n, ground1.p) annotation(\n    Line(points = {{-74, -78}, {-46, -78}, {-46, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\nend RLC;\n"
  },
  {
    "path": "omg_grid/Loads/package.mo",
    "content": "within omg_grid;\npackage Loads \nextends Modelica.Icons.Package;\n\nannotation (Icon(coordinateSystem(preserveAspectRatio=true, extent={{-100.0,-100.0},{100.0,100.0}}), graphics={\n      Rectangle(\n        origin={0.0,35.1488},\n        fillColor={255,255,255},\n        extent={{-30.0,-20.1488},{30.0,20.1488}}),\n      Rectangle(\n        origin={0.0,-34.8512},\n        fillColor={255,255,255},\n        extent={{-30.0,-20.1488},{30.0,20.1488}}),\n      Line(\n        origin={-51.25,0.0},\n        points={{21.25,-35.0},{-13.75,-35.0},{-13.75,35.0},{6.25,35.0}}),\n      Polygon(\n        origin={-40.0,35.0},\n        pattern=LinePattern.None,\n        fillPattern=FillPattern.Solid,\n        points={{10.0,0.0},{-5.0,5.0},{-5.0,-5.0}}),\n      Line(\n        origin={51.25,0.0},\n        points={{-21.25,35.0},{13.75,35.0},{13.75,-35.0},{-6.25,-35.0}}),\n      Polygon(\n        origin={40.0,-35.0},\n        pattern=LinePattern.None,\n        fillPattern=FillPattern.Solid,\n        points={{-10.0,0.0},{5.0,5.0},{5.0,-5.0}})}));\nend Loads;\n\n\n"
  },
  {
    "path": "omg_grid/Loads/package.order",
    "content": "RL\nRC\nRLC\nR\nLC\nC\nL\n"
  },
  {
    "path": "omg_grid/PLLs/Inverter.bak-mo",
    "content": "within omg_grid.PLLs;\n\nmodel Inverter\n  parameter Real v_DC = 1000;\n  Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n    Placement(visible = true, transformation(origin = {-74, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage1 annotation(\n    Placement(visible = true, transformation(origin = {-74, -42}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage2 annotation(\n    Placement(visible = true, transformation(origin = {-74, 18}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage3 annotation(\n    Placement(visible = true, transformation(origin = {-74, 78}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Blocks.Interfaces.RealInput u1 annotation(\n    Placement(visible = true, transformation(origin = {-104, -60}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -60}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput u3 annotation(\n    Placement(visible = true, transformation(origin = {-104, 60}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 60}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput u2 annotation(\n    Placement(visible = true, transformation(origin = {-104, 4.44089e-16}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 4.44089e-16}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain3(k = v_DC) annotation(\n    Placement(visible = true, transformation(origin = {-26, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain1(k = v_DC) annotation(\n    Placement(visible = true, transformation(origin = {-26, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain2(k = v_DC) annotation(\n    Placement(visible = true, transformation(origin = {-26, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(signalVoltage2.p, pin2) annotation(\n    Line(points = {{-74, 28}, {80, 28}, {80, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(signalVoltage3.p, pin3) annotation(\n    Line(points = {{-74, 88}, {80, 88}, {80, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(signalVoltage1.p, pin1) annotation(\n    Line(points = {{-74, -32}, {80, -32}, {80, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(signalVoltage3.n, ground1.p) annotation(\n    Line(points = {{-74, 68}, {-74, 48}, {-82, 48}, {-82, -72}, {-74, -72}}, color = {0, 0, 255}));\n  connect(signalVoltage2.n, ground1.p) annotation(\n    Line(points = {{-74, 8}, {-82, 8}, {-82, -72}, {-74, -72}}, color = {0, 0, 255}));\n  connect(signalVoltage1.n, ground1.p) annotation(\n    Line(points = {{-74, -52}, {-74, -72}}, color = {0, 0, 255}));\n/*  connect(signalVoltage1.v, regler1) annotation(\n    Line);\n  connect(signalVoltage2.v, regler2) annotation(\n    Line);\n  connect(signalVoltage3.v, regler3) annotation(\n    Line);\n*/\n  connect(u1, gain1.u) annotation(\n    Line(points = {{-104, -60}, {-38, -60}, {-38, -60}, {-38, -60}}, color = {0, 0, 127}));\n  connect(gain1.y, signalVoltage1.v) annotation(\n    Line(points = {{-14, -60}, {-6, -60}, {-6, -42}, {-60, -42}, {-60, -42}, {-62, -42}}, color = {0, 0, 127}));\n  connect(u2, gain2.u) annotation(\n    Line(points = {{-104, 0}, {-38, 0}, {-38, 0}, {-38, 0}}, color = {0, 0, 127}));\n  connect(gain2.y, signalVoltage2.v) annotation(\n    Line(points = {{-14, 0}, {-6, 0}, {-6, 18}, {-62, 18}, {-62, 18}}, color = {0, 0, 127}));\n  connect(u3, gain3.u) annotation(\n    Line(points = {{-104, 60}, {-38, 60}, {-38, 60}, {-38, 60}}, color = {0, 0, 127}));\n  connect(gain3.y, signalVoltage3.v) annotation(\n    Line(points = {{-14, 60}, {-6, 60}, {-6, 78}, {-62, 78}, {-62, 78}}, color = {0, 0, 127}));\n  annotation(\n    uses(Modelica(version = \"3.2.3\")));\nend Inverter;\n"
  },
  {
    "path": "omg_grid/PLLs/PLL.mo",
    "content": "within omg_grid.PLLs;\n\nmodel PLL\n  Modelica.Electrical.Analog.Interfaces.Pin a annotation(\n    Placement(visible = true, transformation(origin = {-100, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin b annotation(\n    Placement(visible = true, transformation(origin = {-100, 16}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 16}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin c annotation(\n    Placement(visible = true, transformation(origin = {-100, -14}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -14}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground annotation(\n    Placement(visible = true, transformation(origin = {-86, 62}, extent = {{-6, -6}, {6, 6}}, rotation = 180)));\n  Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_c annotation(\n    Placement(visible = true, transformation(origin = {-88, -8}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n  Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_a annotation(\n    Placement(visible = true, transformation(origin = {-86, 50}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n  Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_b annotation(\n    Placement(visible = true, transformation(origin = {-88, 22}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n  omg_grid.Transformations.ABC2AlphaBeta abc2AlphaBeta annotation(\n    Placement(visible = true, transformation(origin = {-62, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin annotation(\n    Placement(visible = true, transformation(origin = {-10, -6}, extent = {{-4, -4}, {4, 4}}, rotation = 180)));\n  Modelica.Blocks.Math.Cos cos annotation(\n    Placement(visible = true, transformation(origin = {-10, -18}, extent = {{-4, -4}, {4, 4}}, rotation = 180)));\n  Modelica.Blocks.Math.Gain Norm_U_ref_alpha(k = 1 / (230 * 1.414)) annotation(\n    Placement(visible = true, transformation(origin = {-33, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain Norm_U_ref_beta(k = 1 / (230 * 1.414)) annotation(\n    Placement(visible = true, transformation(origin = {-33, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Product alphaSin annotation(\n    Placement(visible = true, transformation(origin = {-7, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Product betaCos annotation(\n    Placement(visible = true, transformation(origin = {-9, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add(k1 = -1, k2 = +1) annotation(\n    Placement(visible = true, transformation(origin = {12, 24}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Continuous.PI pi(T = 0.2, k = 15) annotation(\n    Placement(visible = true, transformation(origin = {26, 24}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add_freq_nom_delta_f(k1 = +1, k2 = +1) annotation(\n    Placement(visible = true, transformation(origin = {48, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Sources.Constant f_nom(k = 50) annotation(\n    Placement(visible = true, transformation(origin = {28, 4}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Continuous.Integrator f2theta(y_start = 0) annotation(\n    Placement(visible = true, transformation(origin = {64, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain deg2rad(k = 2 * 3.1416) annotation(\n    Placement(visible = true, transformation(origin = {78, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\nequation\n  connect(a, voltageSensor_a.p) annotation(\n    Line(points = {{-100, 44}, {-86, 44}}, color = {0, 0, 255}));\n  connect(b, voltageSensor_b.p) annotation(\n    Line(points = {{-100, 16}, {-88, 16}}, color = {0, 0, 255}));\n  connect(c, voltageSensor_c.p) annotation(\n    Line(points = {{-100, -14}, {-88, -14}}, color = {0, 0, 255}));\n  connect(voltageSensor_a.n, ground.p) annotation(\n    Line(points = {{-86, 56}, {-86, 56}}, color = {0, 0, 255}));\n  connect(voltageSensor_b.n, ground.p) annotation(\n    Line(points = {{-88, 28}, {-88, 42}, {-86, 42}, {-86, 56}}, color = {0, 0, 255}));\n  connect(voltageSensor_c.n, ground.p) annotation(\n    Line(points = {{-88, -2}, {-88, 27}, {-86, 27}, {-86, 56}}, color = {0, 0, 255}));\n  connect(abc2AlphaBeta.b, voltageSensor_b.v) annotation(\n    Line(points = {{-72, 21}, {-74, 21}, {-74, 22}, {-82, 22}}, color = {0, 0, 127}));\n  connect(abc2AlphaBeta.a, voltageSensor_a.v) annotation(\n    Line(points = {{-72, 24}, {-76, 24}, {-76, 50}, {-80, 50}}, color = {0, 0, 127}));\n  connect(abc2AlphaBeta.c, voltageSensor_c.v) annotation(\n    Line(points = {{-72, 18}, {-76, 18}, {-76, -8}, {-82, -8}}, color = {0, 0, 127}));\n  connect(Norm_U_ref_alpha.u, abc2AlphaBeta.alpha) annotation(\n    Line(points = {{-37, 29}, {-40, 29}, {-40, 26}, {-52, 26}}, color = {0, 0, 127}));\n  connect(Norm_U_ref_beta.u, abc2AlphaBeta.beta) annotation(\n    Line(points = {{-37, 15}, {-42, 15}, {-42, 17}, {-52, 17}}, color = {0, 0, 127}));\n  connect(Norm_U_ref_alpha.y, alphaSin.u1) annotation(\n    Line(points = {{-30, 30}, {-11, 30}, {-11, 31}}, color = {0, 0, 127}));\n  connect(Norm_U_ref_beta.y, betaCos.u1) annotation(\n    Line(points = {{-30, 16}, {-13, 16}, {-13, 17}}, color = {0, 0, 127}));\n  connect(sin.y, alphaSin.u2) annotation(\n    Line(points = {{-14, -6}, {-22, -6}, {-22, 27}, {-11, 27}}, color = {0, 0, 127}));\n  connect(cos.y, betaCos.u2) annotation(\n    Line(points = {{-14, -18}, {-18, -18}, {-18, 13}, {-13, 13}}, color = {0, 0, 127}));\n  connect(add.u1, alphaSin.y) annotation(\n    Line(points = {{7, 26}, {3.5, 26}, {3.5, 30}, {-4, 30}}, color = {0, 0, 127}));\n  connect(betaCos.y, add.u2) annotation(\n    Line(points = {{-6, 16}, {4, 16}, {4, 22}, {7, 22}}, color = {0, 0, 127}));\n  connect(pi.u, add.y) annotation(\n    Line(points = {{19, 24}, {16, 24}}, color = {0, 0, 127}));\n  connect(add_freq_nom_delta_f.u1, pi.y) annotation(\n    Line(points = {{43, 24}, {33, 24}}, color = {0, 0, 127}));\n  connect(f_nom.y, add_freq_nom_delta_f.u2) annotation(\n    Line(points = {{32, 4}, {36, 4}, {36, 20}, {43, 20}}, color = {0, 0, 127}));\n  connect(f2theta.u, add_freq_nom_delta_f.y) annotation(\n    Line(points = {{59, 22}, {52, 22}}, color = {0, 0, 127}));\n  connect(deg2rad.u, f2theta.y) annotation(\n    Line(points = {{74, 22}, {68, 22}, {68, 22}, {68, 22}}, color = {0, 0, 127}));\n  connect(deg2rad.y, sin.u) annotation(\n    Line(points = {{82, 22}, {92, 22}, {92, -6}, {-6, -6}, {-6, -6}}, color = {0, 0, 127}));\n  connect(cos.u, deg2rad.y) annotation(\n    Line(points = {{-6, -18}, {4, -18}, {4, -6}, {92, -6}, {92, 22}, {82, 22}, {82, 22}, {82, 22}}, color = {0, 0, 127}));\nend PLL;\n"
  },
  {
    "path": "omg_grid/PLLs/PLL_DQ.mo",
    "content": "within omg_grid.PLLs;\n\nmodel PLL_DQ\n  Real Pi = 3.14159265;\n  Modelica.Electrical.Analog.Interfaces.Pin a annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin b annotation(\n    Placement(visible = true, transformation(origin = {-102, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-102, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin c annotation(\n    Placement(visible = true, transformation(origin = {-102, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-102, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground annotation(\n    Placement(visible = true, transformation(origin = {-86, 62}, extent = {{-6, -6}, {6, 6}}, rotation = 180)));\n  Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_c annotation(\n    Placement(visible = true, transformation(origin = {-88, -8}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n  Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_a annotation(\n    Placement(visible = true, transformation(origin = {-86, 50}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n  Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_b annotation(\n    Placement(visible = true, transformation(origin = {-88, 22}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n  omg_grid.Transformations.ABC2AlphaBeta abc2AlphaBeta annotation(\n    Placement(visible = true, transformation(origin = {-52, 78}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin annotation(\n    Placement(visible = true, transformation(origin = {-8, 56}, extent = {{-4, -4}, {4, 4}}, rotation = 180)));\n  Modelica.Blocks.Math.Cos cos annotation(\n    Placement(visible = true, transformation(origin = {-8, 46}, extent = {{-4, -4}, {4, 4}}, rotation = 180)));\n  Modelica.Blocks.Math.Gain Norm_U_ref_alpha(k = 1 / (230 * 1.414)) annotation(\n    Placement(visible = true, transformation(origin = {-31, 83}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain Norm_U_ref_beta(k = 1 / (230 * 1.414)) annotation(\n    Placement(visible = true, transformation(origin = {-31, 69}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Product alphaSin annotation(\n    Placement(visible = true, transformation(origin = {-5, 83}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Product betaCos annotation(\n    Placement(visible = true, transformation(origin = {-7, 69}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add(k1 = -1, k2 = +1) annotation(\n    Placement(visible = true, transformation(origin = {14, 78}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Continuous.PI pi(T = 0.05, k = 20) annotation(\n    Placement(visible = true, transformation(origin = {28, 78}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add_freq_nom_delta_f(k1 = +1, k2 = +1) annotation(\n    Placement(visible = true, transformation(origin = {50, 76}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Sources.Constant f_nom(k = 50) annotation(\n    Placement(visible = true, transformation(origin = {30, 58}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Continuous.Integrator f2theta(y_start = 0) annotation(\n    Placement(visible = true, transformation(origin = {66, 76}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain deg2rad(k = 2 * 3.1416) annotation(\n    Placement(visible = true, transformation(origin = {80, 76}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add1(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {-6, 8}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain(k = 2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-64, 40}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain1(k = 2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-63, 17}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product2 annotation(\n    Placement(visible = true, transformation(origin = {38, 34}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Cos cos2 annotation(\n    Placement(visible = true, transformation(origin = {14, 30}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.MultiSum multiSum(nu = 3) annotation(\n    Placement(visible = true, transformation(origin = {64, 28}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product1 annotation(\n    Placement(visible = true, transformation(origin = {32, 10}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product annotation(\n    Placement(visible = true, transformation(origin = {44, -14}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add2(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {2, -18}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Cos cos1 annotation(\n    Placement(visible = true, transformation(origin = {22, -18}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression1(y = 4 * Pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-19, -22}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain2(k = 2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-63, -9}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealOutput d annotation(\n    Placement(visible = true, transformation(origin = {110, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {110, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression(y = 2 * Pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-29, 4}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Math.Cos cos3 annotation(\n    Placement(visible = true, transformation(origin = {14, 6}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealOutput theta annotation(\n    Placement(visible = true, transformation(origin = {110, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {110, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product3 annotation(\n    Placement(visible = true, transformation(origin = {44, -84}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product5 annotation(\n    Placement(visible = true, transformation(origin = {38, -36}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain3(k = -2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-63, -53}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add3(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {2, -88}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression2(y = 2 * Pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-29, -66}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin1 annotation(\n    Placement(visible = true, transformation(origin = {15, -65}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain4(k = -2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-64, -30}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain5(k = -2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-63, -79}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.MultiSum multiSum1(nu = 3) annotation(\n    Placement(visible = true, transformation(origin = {64, -42}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealOutput q annotation(\n    Placement(visible = true, transformation(origin = {110, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {110, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin2 annotation(\n    Placement(visible = true, transformation(origin = {9, -43}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression3(y = 4 * Pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-19, -92}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin3 annotation(\n    Placement(visible = true, transformation(origin = {23, -89}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add4(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {-8, -64}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product4 annotation(\n    Placement(visible = true, transformation(origin = {32, -60}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\nequation\n  connect(a, voltageSensor_a.p) annotation(\n    Line(points = {{-100, 60}, {-93, 60}, {-93, 44}, {-86, 44}}, color = {0, 0, 255}));\n  connect(b, voltageSensor_b.p) annotation(\n    Line(points = {{-102, 0}, {-94, 0}, {-94, 16}, {-88, 16}}, color = {0, 0, 255}));\n  connect(c, voltageSensor_c.p) annotation(\n    Line(points = {{-102, -60}, {-94, -60}, {-94, -14}, {-88, -14}}, color = {0, 0, 255}));\n  connect(voltageSensor_a.n, ground.p) annotation(\n    Line(points = {{-86, 56}, {-86, 56}}, color = {0, 0, 255}));\n  connect(voltageSensor_b.n, ground.p) annotation(\n    Line(points = {{-88, 28}, {-88, 42}, {-86, 42}, {-86, 56}}, color = {0, 0, 255}));\n  connect(voltageSensor_c.n, ground.p) annotation(\n    Line(points = {{-88, -2}, {-88, 27}, {-86, 27}, {-86, 56}}, color = {0, 0, 255}));\n  connect(Norm_U_ref_alpha.u, abc2AlphaBeta.alpha) annotation(\n    Line(points = {{-35, 83}, {-42, 83}, {-42, 84}}, color = {0, 0, 127}));\n  connect(Norm_U_ref_alpha.y, alphaSin.u1) annotation(\n    Line(points = {{-28, 83}, {-18.5, 83}, {-18.5, 85}, {-9, 85}}, color = {0, 0, 127}));\n  connect(Norm_U_ref_beta.y, betaCos.u1) annotation(\n    Line(points = {{-28, 69}, {-19.5, 69}, {-19.5, 71}, {-11, 71}}, color = {0, 0, 127}));\n  connect(sin.y, alphaSin.u2) annotation(\n    Line(points = {{-12, 56}, {-20, 56}, {-20, 81}, {-9, 81}}, color = {0, 0, 127}));\n  connect(cos.y, betaCos.u2) annotation(\n    Line(points = {{-12, 46}, {-16, 46}, {-16, 67}, {-11, 67}}, color = {0, 0, 127}));\n  connect(add.u1, alphaSin.y) annotation(\n    Line(points = {{9, 80}, {-0.25, 80}, {-0.25, 83}, {-2, 83}}, color = {0, 0, 127}));\n  connect(betaCos.y, add.u2) annotation(\n    Line(points = {{-4, 69}, {-1, 69}, {-1, 76}, {9, 76}}, color = {0, 0, 127}));\n  connect(pi.u, add.y) annotation(\n    Line(points = {{21, 78}, {18, 78}}, color = {0, 0, 127}));\n  connect(deg2rad.u, f2theta.y) annotation(\n    Line(points = {{75, 76}, {70, 76}}, color = {0, 0, 127}));\n  connect(cos.u, deg2rad.y) annotation(\n    Line(points = {{-3, 46}, {92, 46}, {92, 76}, {84, 76}}, color = {0, 0, 127}));\n  connect(gain.y, product2.u1) annotation(\n    Line(points = {{-57, 40}, {-13, 40}, {-13, 38}, {31, 38}}, color = {0, 0, 127}));\n  connect(cos3.y, product1.u2) annotation(\n    Line(points = {{21, 6}, {25, 6}}, color = {0, 0, 127}));\n  connect(cos1.y, product.u2) annotation(\n    Line(points = {{29, -18}, {37, -18}}, color = {0, 0, 127}));\n  connect(gain1.y, product1.u1) annotation(\n    Line(points = {{-55, 17}, {25, 17}, {25, 14}}, color = {0, 0, 127}));\n  connect(realExpression.y, add1.u2) annotation(\n    Line(points = {{-21, 4}, {-13, 4}}, color = {0, 0, 127}));\n  connect(product2.y, multiSum.u[3]) annotation(\n    Line(points = {{45, 34}, {55.5, 34}, {55.5, 28}, {54, 28}}, color = {0, 0, 127}));\n  connect(product.y, multiSum.u[1]) annotation(\n    Line(points = {{51, -14}, {54, -14}, {54, 28}}, color = {0, 0, 127}));\n  connect(add1.y, cos3.u) annotation(\n    Line(points = {{1, 8}, {4, 8}, {4, 6}, {7, 6}}, color = {0, 0, 127}));\n  connect(add2.y, cos1.u) annotation(\n    Line(points = {{9, -18}, {15, -18}}, color = {0, 0, 127}));\n  connect(multiSum.y, d) annotation(\n    Line(points = {{76, 28}, {95, 28}, {95, 0}, {110, 0}}, color = {0, 0, 127}));\n  connect(product1.y, multiSum.u[2]) annotation(\n    Line(points = {{39, 10}, {54, 10}, {54, 28}}, color = {0, 0, 127}));\n  connect(cos2.y, product2.u2) annotation(\n    Line(points = {{21, 30}, {31, 30}}, color = {0, 0, 127}));\n  connect(realExpression1.y, add2.u2) annotation(\n    Line(points = {{-11, -22}, {-5, -22}}, color = {0, 0, 127}));\n  connect(gain2.y, product.u1) annotation(\n    Line(points = {{-55, -9}, {37, -9}, {37, -10}}, color = {0, 0, 127}));\n  connect(voltageSensor_a.v, gain.u) annotation(\n    Line(points = {{-80, 50}, {-74, 50}, {-74, 40}, {-71, 40}}, color = {0, 0, 127}));\n  connect(deg2rad.y, cos2.u) annotation(\n    Line(points = {{84, 76}, {92, 76}, {92, 46}, {7, 46}, {7, 30}}, color = {0, 0, 127}));\n  connect(deg2rad.y, add1.u1) annotation(\n    Line(points = {{84, 76}, {92, 76}, {92, 46}, {6, 46}, {6, 24}, {-17, 24}, {-17, 12}, {-13, 12}}, color = {0, 0, 127}));\n  connect(deg2rad.y, add2.u1) annotation(\n    Line(points = {{84, 76}, {92, 76}, {92, 46}, {6, 46}, {6, 24}, {-16, 24}, {-16, -14}, {-5, -14}}, color = {0, 0, 127}));\n  connect(sin.u, deg2rad.y) annotation(\n    Line(points = {{-4, 56}, {0, 56}, {0, 46}, {92, 46}, {92, 76}, {84, 76}}, color = {0, 0, 127}));\n  connect(voltageSensor_c.v, gain2.u) annotation(\n    Line(points = {{-82, -8}, {-71, -8}, {-71, -9}}, color = {0, 0, 127}));\n  connect(realExpression2.y, add4.u2) annotation(\n    Line(points = {{-21, -66}, {-18, -66}, {-18, -68}, {-15, -68}}, color = {0, 0, 127}));\n  connect(gain4.y, product5.u1) annotation(\n    Line(points = {{-57, -30}, {-13, -30}, {-13, -32}, {31, -32}}, color = {0, 0, 127}));\n  connect(gain3.y, product4.u1) annotation(\n    Line(points = {{-55, -53}, {25, -53}, {25, -56}}, color = {0, 0, 127}));\n  connect(sin1.y, product4.u2) annotation(\n    Line(points = {{23, -65}, {23, -60.5}, {25, -60.5}, {25, -64}}, color = {0, 0, 127}));\n  connect(realExpression3.y, add3.u2) annotation(\n    Line(points = {{-11, -92}, {-5, -92}}, color = {0, 0, 127}));\n  connect(sin2.y, product5.u2) annotation(\n    Line(points = {{17, -43}, {28, -43}, {28, -40}, {31, -40}}, color = {0, 0, 127}));\n  connect(product3.y, multiSum1.u[1]) annotation(\n    Line(points = {{51, -84}, {54, -84}, {54, -42}}, color = {0, 0, 127}));\n  connect(gain5.y, product3.u1) annotation(\n    Line(points = {{-55, -79}, {37, -79}, {37, -80}}, color = {0, 0, 127}));\n  connect(product5.y, multiSum1.u[3]) annotation(\n    Line(points = {{45, -36}, {75.5, -36}, {75.5, -42}, {54, -42}}, color = {0, 0, 127}));\n  connect(add3.y, sin3.u) annotation(\n    Line(points = {{9, -88}, {26, -88}, {26, -89}, {15, -89}}, color = {0, 0, 127}));\n  connect(sin1.u, add4.y) annotation(\n    Line(points = {{7, -65}, {16, -65}, {16, -64}, {-1, -64}}, color = {0, 0, 127}));\n  connect(product4.y, multiSum1.u[2]) annotation(\n    Line(points = {{39, -60}, {54, -60}, {54, -42}}, color = {0, 0, 127}));\n  connect(sin3.y, product3.u2) annotation(\n    Line(points = {{31, -89}, {31, -88.5}, {37, -88.5}, {37, -88}}, color = {0, 0, 127}));\n  connect(multiSum1.y, q) annotation(\n    Line(points = {{76, -42}, {93, -42}, {93, -60}, {110, -60}}, color = {0, 0, 127}));\n  connect(abc2AlphaBeta.beta, Norm_U_ref_beta.u) annotation(\n    Line(points = {{-42, 74}, {-38, 74}, {-38, 68}, {-34, 68}, {-34, 70}}, color = {0, 0, 127}));\n  connect(deg2rad.y, sin2.u) annotation(\n    Line(points = {{84, 76}, {92, 76}, {92, 46}, {6, 46}, {6, 24}, {-44, 24}, {-44, -44}, {0, -44}, {0, -42}}, color = {0, 0, 127}));\n  connect(voltageSensor_a.v, gain4.u) annotation(\n    Line(points = {{-80, 50}, {-74, 50}, {-74, -30}, {-72, -30}, {-72, -30}}, color = {0, 0, 127}));\n  connect(voltageSensor_b.v, gain3.u) annotation(\n    Line(points = {{-81, 22}, {-76, 22}, {-76, -54}, {-72, -54}, {-72, -52}}, color = {0, 0, 127}));\n  connect(add4.u1, deg2rad.y) annotation(\n    Line(points = {{-16, -60}, {-44, -60}, {-44, 24}, {6, 24}, {6, 46}, {92, 46}, {92, 76}, {84, 76}, {84, 76}}, color = {0, 0, 127}));\n  connect(add3.u1, deg2rad.y) annotation(\n    Line(points = {{-6, -84}, {-44, -84}, {-44, 24}, {6, 24}, {6, 46}, {92, 46}, {92, 76}, {84, 76}, {84, 76}}, color = {0, 0, 127}));\n  connect(gain5.u, voltageSensor_c.v) annotation(\n    Line(points = {{-72, -78}, {-80, -78}, {-80, -8}, {-82, -8}, {-82, -8}}, color = {0, 0, 127}));\n  connect(voltageSensor_b.v, gain1.u) annotation(\n    Line(points = {{-82, 22}, {-76, 22}, {-76, 16}, {-72, 16}, {-72, 18}}, color = {0, 0, 127}));\n  connect(voltageSensor_c.v, abc2AlphaBeta.c) annotation(\n    Line(points = {{-82, -8}, {-78, -8}, {-78, 76}, {-62, 76}}, color = {0, 0, 127}));\n  connect(voltageSensor_b.v, abc2AlphaBeta.b) annotation(\n    Line(points = {{-82, 22}, {-76, 22}, {-76, 78}, {-62, 78}, {-62, 80}}, color = {0, 0, 127}));\n  connect(abc2AlphaBeta.a, voltageSensor_a.v) annotation(\n    Line(points = {{-62, 82}, {-80, 82}, {-80, 50}, {-80, 50}}, color = {0, 0, 127}));\n  connect(deg2rad.y, theta) annotation(\n    Line(points = {{84, 76}, {92, 76}, {92, 60}, {110, 60}, {110, 60}}, color = {0, 0, 127}));\n  connect(f_nom.y, add_freq_nom_delta_f.u2) annotation(\n    Line(points = {{34, 58}, {37.5, 58}, {37.5, 74}, {45, 74}}, color = {0, 0, 127}));\n  connect(pi.y, add_freq_nom_delta_f.u1) annotation(\n    Line(points = {{34, 78}, {44, 78}, {44, 78}, {46, 78}}, color = {0, 0, 127}));\n  connect(f2theta.u, add_freq_nom_delta_f.y) annotation(\n    Line(points = {{62, 76}, {54, 76}, {54, 76}, {54, 76}}, color = {0, 0, 127}));\nend PLL_DQ;\n"
  },
  {
    "path": "omg_grid/PLLs/package.mo",
    "content": "within omg_grid;\n\npackage PLLs \nextends Modelica.Icons.SensorsPackage;\n\nend PLLs;\n"
  },
  {
    "path": "omg_grid/PLLs/package.order",
    "content": "PLL\nPLL_DQ\n"
  },
  {
    "path": "omg_grid/Transformations/ABC2AlphaBeta.mo",
    "content": "within omg_grid.Transformations;\n\nmodel ABC2AlphaBeta\n  Modelica.Blocks.Interfaces.RealInput a annotation(\n    Placement(visible = true, transformation(origin = {-104, 40}, extent = {{-12, -12}, {12, 12}}, rotation = 0), iconTransformation(origin = {-104, 40}, extent = {{-12, -12}, {12, 12}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput b annotation(\n    Placement(visible = true, transformation(origin = {-104, 12}, extent = {{-12, -12}, {12, 12}}, rotation = 0), iconTransformation(origin = {-104, 12}, extent = {{-12, -12}, {12, 12}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput c annotation(\n    Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-12, -12}, {12, 12}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-12, -12}, {12, 12}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain(k = 2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-40, 78}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain1(k = -1 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-39, 55}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain2(k = -1 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-39, 29}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.MultiSum multiSum(nu = 3) annotation(\n    Placement(visible = true, transformation(origin = {58, 66}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain3(k = -1 / sqrt(3)) annotation(\n    Placement(visible = true, transformation(origin = {-32, -18}, extent = {{-14, -14}, {14, 14}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain4(k = 1 / sqrt(3)) annotation(\n    Placement(visible = true, transformation(origin = {-32, -64}, extent = {{-14, -14}, {14, 14}}, rotation = 0)));\n  Modelica.Blocks.Math.MultiSum multiSum1(nu = 2) annotation(\n    Placement(visible = true, transformation(origin = {48, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealOutput alpha annotation(\n    Placement(visible = true, transformation(origin = {102, 64}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {102, 64}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealOutput beta annotation(\n    Placement(visible = true, transformation(origin = {102, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {102, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\nequation\n  connect(a, gain.u) annotation(\n    Line(points = {{-104, 40}, {-77, 40}, {-77, 78}, {-48, 78}}, color = {0, 0, 127}));\n  connect(gain1.u, b) annotation(\n    Line(points = {{-48, 56}, {-71, 56}, {-71, 12}, {-104, 12}}, color = {0, 0, 127}));\n  connect(c, gain2.u) annotation(\n    Line(points = {{-104, -18}, {-62, -18}, {-62, 30}, {-48, 30}}, color = {0, 0, 127}));\n  connect(gain.y, multiSum.u[1]) annotation(\n    Line(points = {{-34, 78}, {48, 78}, {48, 66}}, color = {0, 0, 127}));\n  connect(gain1.y, multiSum.u[2]) annotation(\n    Line(points = {{-32, 56}, {48, 56}, {48, 66}, {48, 66}}, color = {0, 0, 127}));\n  connect(gain2.y, multiSum.u[3]) annotation(\n    Line(points = {{-32, 30}, {48, 30}, {48, 66}, {48, 66}}, color = {0, 0, 127}));\n  connect(gain4.u, b) annotation(\n    Line(points = {{-48, -64}, {-73, -64}, {-73, -62}, {-72, -62}, {-72, 12}, {-104, 12}}, color = {0, 0, 127}));\n  connect(gain3.u, c) annotation(\n    Line(points = {{-48, -18}, {-96, -18}, {-96, -18}, {-104, -18}}, color = {0, 0, 127}));\n  connect(gain3.y, multiSum1.u[1]) annotation(\n    Line(points = {{-16, -18}, {38, -18}, {38, -34}, {38, -34}}, color = {0, 0, 127}));\n  connect(gain4.y, multiSum1.u[2]) annotation(\n    Line(points = {{-16, -64}, {38, -64}, {38, -34}, {38, -34}}, color = {0, 0, 127}));\n  connect(multiSum.y, alpha) annotation(\n    Line(points = {{70, 66}, {96, 66}, {96, 64}, {102, 64}}, color = {0, 0, 127}));\n  connect(multiSum1.y, beta) annotation(\n    Line(points = {{60, -34}, {102, -34}}, color = {0, 0, 127}));\nend ABC2AlphaBeta;\n"
  },
  {
    "path": "omg_grid/Transformations/ABC2DQ_Currents.mo",
    "content": "within omg_grid.Transformations;\n\nmodel ABC2DQ_Currents\n  Real Pi = 3.14159265;\n  Modelica.Electrical.Analog.Interfaces.Pin a annotation(\n    Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin b annotation(\n    Placement(visible = true, transformation(origin = {-102, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-102, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin c annotation(\n    Placement(visible = true, transformation(origin = {-102, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-102, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealOutput d annotation(\n    Placement(visible = true, transformation(origin = {-40, -110}, extent = {{-10, -10}, {10, 10}}, rotation = -90), iconTransformation(origin = {-40, -110}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Blocks.Interfaces.RealOutput q annotation(\n    Placement(visible = true, transformation(origin = {40, -110}, extent = {{-10, -10}, {10, 10}}, rotation = -90), iconTransformation(origin = {40, -110}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n    Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n    Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n    Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain(k = 2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-62, 60}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain1(k = 2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-61, 37}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain2(k = 2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-61, 11}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression1(y = 4 * Pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-17, -2}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression(y = 2 * Pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-27, 24}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add1(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {-4, 28}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add2(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {4, 2}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Cos cos2 annotation(\n    Placement(visible = true, transformation(origin = {16, 50}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product annotation(\n    Placement(visible = true, transformation(origin = {46, 6}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Cos cos1 annotation(\n    Placement(visible = true, transformation(origin = {24, 2}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Cos cos3 annotation(\n    Placement(visible = true, transformation(origin = {16, 26}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product2 annotation(\n    Placement(visible = true, transformation(origin = {40, 54}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product1 annotation(\n    Placement(visible = true, transformation(origin = {34, 30}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.MultiSum multiSum(nu = 3) annotation(\n    Placement(visible = true, transformation(origin = {74, 54}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.MultiSum multiSum1(nu = 3) annotation(\n    Placement(visible = true, transformation(origin = {66, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product5 annotation(\n    Placement(visible = true, transformation(origin = {40, -24}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add4(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {-6, -52}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add3(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {4, -76}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression2(y = 2 * Pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-27, -54}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression3(y = 4 * Pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-17, -80}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product3 annotation(\n    Placement(visible = true, transformation(origin = {46, -72}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin3 annotation(\n    Placement(visible = true, transformation(origin = {25, -77}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain5(k = -2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-61, -67}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product4 annotation(\n    Placement(visible = true, transformation(origin = {34, -48}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain3(k = -2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-61, -41}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin1 annotation(\n    Placement(visible = true, transformation(origin = {17, -53}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Gain gain4(k = -2 / 3) annotation(\n    Placement(visible = true, transformation(origin = {-62, -18}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin2 annotation(\n    Placement(visible = true, transformation(origin = {11, -31}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput theta annotation(\n    Placement(visible = true, transformation(origin = {0, 112}, extent = {{-20, -20}, {20, 20}}, rotation = -90), iconTransformation(origin = {0, 112}, extent = {{-20, -20}, {20, 20}}, rotation = -90)));\n  Modelica.Electrical.Analog.Sensors.CurrentSensor currentSensor3 annotation(\n    Placement(visible = true, transformation(origin = {-90, -50}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n  Modelica.Electrical.Analog.Sensors.CurrentSensor currentSensor1 annotation(\n    Placement(visible = true, transformation(origin = {-88, 70}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n  Modelica.Electrical.Analog.Sensors.CurrentSensor currentSensor2 annotation(\n    Placement(visible = true, transformation(origin = {-90, 10}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\nequation\n  connect(realExpression.y, add1.u2) annotation(\n    Line(points = {{-19, 24}, {-11, 24}}, color = {0, 0, 127}));\n  connect(realExpression1.y, add2.u2) annotation(\n    Line(points = {{-9, -2}, {-3, -2}}, color = {0, 0, 127}));\n  connect(gain2.y, product.u1) annotation(\n    Line(points = {{-53, 11}, {39, 11}, {39, 10}}, color = {0, 0, 127}));\n  connect(add2.y, cos1.u) annotation(\n    Line(points = {{11, 2}, {17, 2}}, color = {0, 0, 127}));\n  connect(cos1.y, product.u2) annotation(\n    Line(points = {{31, 2}, {39, 2}}, color = {0, 0, 127}));\n  connect(add1.y, cos3.u) annotation(\n    Line(points = {{3, 28}, {4, 28}, {4, 26}, {9, 26}}, color = {0, 0, 127}));\n  connect(cos2.y, product2.u2) annotation(\n    Line(points = {{23, 50}, {33, 50}}, color = {0, 0, 127}));\n  connect(gain.y, product2.u1) annotation(\n    Line(points = {{-55, 60}, {-11, 60}, {-11, 58}, {33, 58}}, color = {0, 0, 127}));\n  connect(gain1.y, product1.u1) annotation(\n    Line(points = {{-53, 37}, {27, 37}, {27, 34}}, color = {0, 0, 127}));\n  connect(cos3.y, product1.u2) annotation(\n    Line(points = {{23, 26}, {27, 26}}, color = {0, 0, 127}));\n  connect(multiSum.y, d) annotation(\n    Line(points = {{86, 54}, {86, -94}, {-40, -94}, {-40, -110}}, color = {0, 0, 127}));\n  connect(product1.y, multiSum.u[1]) annotation(\n    Line(points = {{41, 30}, {55.5, 30}, {55.5, 54}, {64, 54}}, color = {0, 0, 127}));\n  connect(product.y, multiSum.u[2]) annotation(\n    Line(points = {{53, 6}, {64, 6}, {64, 54}}, color = {0, 0, 127}));\n  connect(product2.y, multiSum.u[3]) annotation(\n    Line(points = {{47, 54}, {64, 54}}, color = {0, 0, 127}));\n  connect(sin2.y, product5.u2) annotation(\n    Line(points = {{19, -31}, {30, -31}, {30, -28}, {33, -28}}, color = {0, 0, 127}));\n  connect(product4.y, multiSum1.u[2]) annotation(\n    Line(points = {{41, -48}, {56, -48}, {56, -30}}, color = {0, 0, 127}));\n  connect(gain3.y, product4.u1) annotation(\n    Line(points = {{-53, -41}, {27, -41}, {27, -44}}, color = {0, 0, 127}));\n  connect(realExpression3.y, add3.u2) annotation(\n    Line(points = {{-9, -80}, {-3, -80}}, color = {0, 0, 127}));\n  connect(product5.y, multiSum1.u[3]) annotation(\n    Line(points = {{47, -24}, {62.25, -24}, {62.25, -28}, {59.125, -28}, {59.125, -30}, {56, -30}}, color = {0, 0, 127}));\n  connect(add3.y, sin3.u) annotation(\n    Line(points = {{11, -76}, {28, -76}, {28, -77}, {17, -77}}, color = {0, 0, 127}));\n  connect(sin1.u, add4.y) annotation(\n    Line(points = {{9, -53}, {18, -53}, {18, -52}, {1, -52}}, color = {0, 0, 127}));\n  connect(sin3.y, product3.u2) annotation(\n    Line(points = {{33, -77}, {33, -76.5}, {39, -76.5}, {39, -76}}, color = {0, 0, 127}));\n  connect(sin1.y, product4.u2) annotation(\n    Line(points = {{25, -53}, {25, -50.5}, {27, -50.5}, {27, -52}}, color = {0, 0, 127}));\n  connect(gain5.y, product3.u1) annotation(\n    Line(points = {{-53, -67}, {39, -67}, {39, -68}}, color = {0, 0, 127}));\n  connect(realExpression2.y, add4.u2) annotation(\n    Line(points = {{-19, -54}, {-16, -54}, {-16, -56}, {-13, -56}}, color = {0, 0, 127}));\n  connect(product3.y, multiSum1.u[1]) annotation(\n    Line(points = {{53, -72}, {56, -72}, {56, -30}}, color = {0, 0, 127}));\n  connect(gain4.y, product5.u1) annotation(\n    Line(points = {{-55, -18}, {-35, -18}, {-35, -20}, {33, -20}}, color = {0, 0, 127}));\n  connect(multiSum1.y, q) annotation(\n    Line(points = {{78, -30}, {84, -30}, {84, -92}, {40, -92}, {40, -110}, {40, -110}}, color = {0, 0, 127}));\n  connect(theta, cos2.u) annotation(\n    Line(points = {{0, 112}, {0, 112}, {0, 50}, {8, 50}, {8, 50}}, color = {0, 0, 127}));\n  connect(theta, add1.u1) annotation(\n    Line(points = {{0, 112}, {0, 112}, {0, 86}, {-44, 86}, {-44, 32}, {-12, 32}, {-12, 32}}, color = {0, 0, 127}));\n  connect(theta, add2.u1) annotation(\n    Line(points = {{0, 112}, {0, 112}, {0, 86}, {-44, 86}, {-44, 6}, {-4, 6}, {-4, 6}}, color = {0, 0, 127}));\n  connect(theta, sin2.u) annotation(\n    Line(points = {{0, 112}, {0, 112}, {0, 86}, {-44, 86}, {-44, -32}, {2, -32}, {2, -30}}, color = {0, 0, 127}));\n  connect(theta, add4.u1) annotation(\n    Line(points = {{0, 112}, {0, 112}, {0, 86}, {-44, 86}, {-44, -48}, {-14, -48}, {-14, -48}}, color = {0, 0, 127}));\n  connect(theta, add3.u1) annotation(\n    Line(points = {{0, 112}, {0, 112}, {0, 86}, {-44, 86}, {-44, -72}, {-4, -72}, {-4, -72}}, color = {0, 0, 127}));\n  connect(c, currentSensor3.p) annotation(\n    Line(points = {{-102, -60}, {-90, -60}, {-90, -56}, {-90, -56}}, color = {0, 0, 255}));\n  connect(b, currentSensor2.p) annotation(\n    Line(points = {{-102, 0}, {-90, 0}, {-90, 4}, {-90, 4}, {-90, 4}}, color = {0, 0, 255}));\n  connect(a, currentSensor1.p) annotation(\n    Line(points = {{-100, 60}, {-88, 60}, {-88, 64}, {-88, 64}, {-88, 64}}, color = {0, 0, 255}));\n  connect(currentSensor1.n, pin3) annotation(\n    Line(points = {{-88, 76}, {-88, 76}, {-88, 84}, {94, 84}, {94, 60}, {100, 60}}, color = {0, 0, 255}));\n  connect(currentSensor2.n, pin2) annotation(\n    Line(points = {{-90, 16}, {-86, 16}, {-86, 82}, {92, 82}, {92, 0}, {100, 0}}, color = {0, 0, 255}));\n  connect(currentSensor3.n, pin1) annotation(\n    Line(points = {{-90, -44}, {-84, -44}, {-84, 80}, {90, 80}, {90, -60}, {100, -60}}, color = {0, 0, 255}));\n  connect(currentSensor1.i, gain.u) annotation(\n    Line(points = {{-82, 70}, {-72, 70}, {-72, 60}, {-70, 60}, {-70, 60}}, color = {0, 0, 127}));\n  connect(gain4.u, currentSensor1.i) annotation(\n    Line(points = {{-70, -18}, {-72, -18}, {-72, 70}, {-82, 70}, {-82, 70}}, color = {0, 0, 127}));\n  connect(currentSensor2.i, gain1.u) annotation(\n    Line(points = {{-84, 10}, {-80, 10}, {-80, 36}, {-70, 36}, {-70, 38}}, color = {0, 0, 127}));\n  connect(currentSensor2.i, gain3.u) annotation(\n    Line(points = {{-84, 10}, {-80, 10}, {-80, -42}, {-70, -42}, {-70, -40}}, color = {0, 0, 127}));\n  connect(currentSensor3.i, gain2.u) annotation(\n    Line(points = {{-84, -50}, {-76, -50}, {-76, 10}, {-70, 10}, {-70, 12}}, color = {0, 0, 127}));\n  connect(currentSensor3.i, gain5.u) annotation(\n    Line(points = {{-84, -50}, {-76, -50}, {-76, -68}, {-70, -68}, {-70, -66}}, color = {0, 0, 127}));\nend ABC2DQ_Currents;\n"
  },
  {
    "path": "omg_grid/Transformations/DQ2ABC.mo",
    "content": "within omg_grid.Transformations;\n\nmodel DQ2ABC\n  Real pi = 2 * Modelica.Math.asin(1.0);\n  Modelica.Blocks.Interfaces.RealInput d annotation(\n    Placement(visible = true, transformation(origin = {-104, 40}, extent = {{-12, -12}, {12, 12}}, rotation = 0), iconTransformation(origin = {-104, 40}, extent = {{-12, -12}, {12, 12}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput q annotation(\n    Placement(visible = true, transformation(origin = {-104, -40}, extent = {{-12, -12}, {12, 12}}, rotation = 0), iconTransformation(origin = {-104, -40}, extent = {{-12, -12}, {12, 12}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealOutput b annotation(\n    Placement(visible = true, transformation(origin = {106, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {106, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression1(y = 2 * pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-7, 6}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add1(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {14, 10}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Cos cos1 annotation(\n    Placement(visible = true, transformation(origin = {34, 10}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Cos cos2 annotation(\n    Placement(visible = true, transformation(origin = {26, 68}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product annotation(\n    Placement(visible = true, transformation(origin = {56, 14}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product1 annotation(\n    Placement(visible = true, transformation(origin = {44, 48}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product2 annotation(\n    Placement(visible = true, transformation(origin = {50, 72}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product3 annotation(\n    Placement(visible = true, transformation(origin = {58, -58}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product4 annotation(\n    Placement(visible = true, transformation(origin = {46, -34}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add2(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {16, -62}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add3(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {6, -38}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression2(y = 4 * pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-17, -40}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealOutput c annotation(\n    Placement(visible = true, transformation(origin = {106, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {106, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression3(y = 4 * pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-5, -66}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin annotation(\n    Placement(visible = true, transformation(origin = {37, -63}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin5 annotation(\n    Placement(visible = true, transformation(origin = {17, 43}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add annotation(\n    Placement(visible = true, transformation(origin = {74, 58}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealOutput a annotation(\n    Placement(visible = true, transformation(origin = {106, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {106, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Product product6 annotation(\n    Placement(visible = true, transformation(origin = {54, -8}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add4(k2 = -1) annotation(\n    Placement(visible = true, transformation(origin = {14, -12}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Sources.RealExpression realExpression(y = 2 * pi / 3) annotation(\n    Placement(visible = true, transformation(origin = {-11, -16}, extent = {{-7, -8}, {7, 8}}, rotation = 0)));\n  Modelica.Blocks.Math.Sin sin8 annotation(\n    Placement(visible = true, transformation(origin = {35, -13}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add5 annotation(\n    Placement(visible = true, transformation(origin = {80, 2}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Math.Cos cos annotation(\n    Placement(visible = true, transformation(origin = {24, -38}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n  Modelica.Blocks.Math.Add add6 annotation(\n    Placement(visible = true, transformation(origin = {82, -52}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Interfaces.RealInput theta annotation(\n    Placement(visible = true, transformation(origin = {-34, 116}, extent = {{-20, -20}, {20, 20}}, rotation = -90), iconTransformation(origin = {-34, 116}, extent = {{-20, -20}, {20, 20}}, rotation = -90)));\nequation\n  connect(realExpression1.y, add1.u2) annotation(\n    Line(points = {{1, 6}, {7, 6}}, color = {0, 0, 127}));\n  connect(add1.y, cos1.u) annotation(\n    Line(points = {{21, 10}, {27, 10}}, color = {0, 0, 127}));\n  connect(cos2.y, product2.u2) annotation(\n    Line(points = {{32, 68}, {42, 68}, {42, 68}, {42, 68}}, color = {0, 0, 127}));\n  connect(cos1.y, product.u2) annotation(\n    Line(points = {{41, 10}, {49, 10}}, color = {0, 0, 127}));\n  connect(realExpression2.y, add3.u2) annotation(\n    Line(points = {{-9, -40}, {-4, -40}, {-4, -42}, {-1, -42}}, color = {0, 0, 127}));\n  connect(realExpression3.y, add2.u2) annotation(\n    Line(points = {{3, -66}, {9, -66}}, color = {0, 0, 127}));\n  connect(add2.y, sin.u) annotation(\n    Line(points = {{22, -62}, {26, -62}, {26, -63}, {29, -63}}, color = {0, 0, 127}));\n  connect(sin.y, product3.u2) annotation(\n    Line(points = {{45, -63}, {45, -62}, {50, -62}}, color = {0, 0, 127}));\n  connect(d, product2.u1) annotation(\n    Line(points = {{-104, 40}, {-60, 40}, {-60, 76}, {42, 76}, {42, 76}}, color = {0, 0, 127}));\n  connect(sin5.y, product1.u2) annotation(\n    Line(points = {{24, 44}, {36, 44}, {36, 44}, {36, 44}}, color = {0, 0, 127}));\n  connect(q, product1.u1) annotation(\n    Line(points = {{-104, -40}, {-50, -40}, {-50, 52}, {36, 52}, {36, 52}}, color = {0, 0, 127}));\n  connect(product1.y, add.u2) annotation(\n    Line(points = {{50, 48}, {56, 48}, {56, 52}, {62, 52}, {62, 52}}, color = {0, 0, 127}));\n  connect(add.y, a) annotation(\n    Line(points = {{86, 58}, {92, 58}, {92, 60}, {106, 60}}, color = {0, 0, 127}));\n  connect(product2.y, add.u1) annotation(\n    Line(points = {{56, 72}, {58, 72}, {58, 64}, {62, 64}, {62, 64}, {62, 64}}, color = {0, 0, 127}));\n  connect(sin8.y, product6.u2) annotation(\n    Line(points = {{43, -13}, {43, -12.5}, {47, -12.5}, {47, -12}}, color = {0, 0, 127}));\n  connect(realExpression.y, add4.u2) annotation(\n    Line(points = {{-3, -16}, {7, -16}}, color = {0, 0, 127}));\n  connect(sin8.u, add4.y) annotation(\n    Line(points = {{27, -13}, {26, -13}, {26, -12}, {21, -12}}, color = {0, 0, 127}));\n  connect(d, product.u1) annotation(\n    Line(points = {{-104, 40}, {-60, 40}, {-60, 18}, {49, 18}}, color = {0, 0, 127}));\n  connect(q, product6.u1) annotation(\n    Line(points = {{-104, -40}, {-50, -40}, {-50, -2}, {47, -2}, {47, -4}}, color = {0, 0, 127}));\n  connect(product6.y, add5.u2) annotation(\n    Line(points = {{61, -8}, {64, -8}, {64, -4}, {68, -4}}, color = {0, 0, 127}));\n  connect(product.y, add5.u1) annotation(\n    Line(points = {{63, 14}, {64, 14}, {64, 8}, {68, 8}}, color = {0, 0, 127}));\n  connect(add5.y, b) annotation(\n    Line(points = {{92, 2}, {94, 2}, {94, 0}, {106, 0}, {106, 0}}, color = {0, 0, 127}));\n  connect(cos.y, product4.u2) annotation(\n    Line(points = {{30, -38}, {38, -38}, {38, -38}, {38, -38}}, color = {0, 0, 127}));\n  connect(d, product4.u1) annotation(\n    Line(points = {{-104, 40}, {-60, 40}, {-60, -30}, {38, -30}, {38, -30}}, color = {0, 0, 127}));\n  connect(add3.y, cos.u) annotation(\n    Line(points = {{12, -38}, {16, -38}, {16, -38}, {16, -38}}, color = {0, 0, 127}));\n  connect(q, product3.u1) annotation(\n    Line(points = {{-104, -40}, {-50, -40}, {-50, -48}, {48, -48}, {48, -54}, {50, -54}, {50, -54}}, color = {0, 0, 127}));\n  connect(product3.y, add6.u2) annotation(\n    Line(points = {{64, -58}, {68, -58}, {68, -58}, {70, -58}}, color = {0, 0, 127}));\n  connect(product4.y, add6.u1) annotation(\n    Line(points = {{52, -34}, {58, -34}, {58, -46}, {68, -46}, {68, -46}, {70, -46}}, color = {0, 0, 127}));\n  connect(add6.y, c) annotation(\n    Line(points = {{94, -52}, {94, -52}, {94, -60}, {106, -60}, {106, -60}}, color = {0, 0, 127}));\n  connect(theta, add4.u1) annotation(\n    Line(points = {{-34, 116}, {-34, -8}, {7, -8}}, color = {0, 0, 127}));\n  connect(theta, sin5.u) annotation(\n    Line(points = {{-34, 116}, {-34, 116}, {-34, 42}, {8, 42}, {8, 44}}, color = {0, 0, 127}));\n  connect(theta, add3.u1) annotation(\n    Line(points = {{-34, 116}, {-34, -32}, {-2.25, -32}, {-2.25, -34}, {-1, -34}}, color = {0, 0, 127}));\n  connect(theta, add2.u1) annotation(\n    Line(points = {{-34, 116}, {-34, 116}, {-34, -58}, {8, -58}, {8, -58}}, color = {0, 0, 127}));\n  connect(theta, cos2.u) annotation(\n    Line(points = {{-34, 116}, {-34, 68}, {18, 68}}, color = {0, 0, 127}));\n  connect(theta, add1.u1) annotation(\n    Line(points = {{-34, 116}, {-34, 14}, {7, 14}}, color = {0, 0, 127}));\nend DQ2ABC;\n"
  },
  {
    "path": "omg_grid/Transformations/package.mo",
    "content": "within omg_grid;\npackage Transformations \nextends Modelica.Icons.Package;\n\nannotation (Icon(graphics={\n        Rectangle(\n          extent={{-60,66},{-30,18}},\n          lineColor={95,95,95},\n          fillColor={175,175,175},\n          fillPattern=FillPattern.Solid),\n        Rectangle(\n          extent={{28,66},{58,18}},\n          lineColor={95,95,95},\n          fillColor={175,175,175},\n          fillPattern=FillPattern.Solid),\n        Rectangle(\n          extent={{-60,-18},{-30,-66}},\n          lineColor={95,95,95},\n          fillColor={175,175,175},\n          fillPattern=FillPattern.Solid),\n        Rectangle(\n          extent={{28,-18},{58,-66}},\n          lineColor={95,95,95},\n          fillColor={175,175,175},\n          fillPattern=FillPattern.Solid)}));\nend Transformations;\n"
  },
  {
    "path": "omg_grid/Transformations/package.order",
    "content": "ABC2AlphaBeta\nDQ2ABC\nABC2DQ_Currents\n"
  },
  {
    "path": "omg_grid/UsersGuide/Contact.mo",
    "content": "within omg_grid.UsersGuide;\nmodel Contact \"Contact\"\n    extends Modelica.Icons.Contact;\n\n  annotation (\n  Documentation(info=\"<html>   \n  <dl><dt>The development of this Modelica package is organized by</dt>\n  <dd>Henrik Bode</dd>\n  <dd>Chair of Power Electronics and Electrical Drives, Paderborn University</dd>\n  <dd>Warburger Straße 100</dd>\n  <dd>33098 Paderborn</dd>\n  <dd>Germany</dd>\n  <dd>email: <a href=\\\"mailto:bode@lea.uni-paderborn.de\\\">bode@lea.uni-paderborn.de</a></dd>   \n  <dt>&nbsp;</dt>  \n  <dt>List of contributors:</dt>\n  <dd>Daniel Weber</dd>\n  <dd>Stefan Heid</dd>\n  <dd>Jarren Lange</dd>\n  <dd>Oliver Wallscheid</dd></dl>\n  <p></p>\n\n</html>\"));\n\nend Contact;\n"
  },
  {
    "path": "omg_grid/UsersGuide/ModelicaLicense2.mo",
    "content": "within omg_grid.UsersGuide;\nclass ModelicaLicense2 \"Modelica License 2\"\n  extends Modelica.Icons.Information;\n\n  annotation (Documentation(info=\"<html>\n<head>\n    <title>The Modelica License 2</title>\n    <style type=\\\"text/css\\\">\n    *       { font-size: 10pt; font-family: Arial,sans-serif; }\n    code    { font-size:  9pt; font-family: Courier,monospace;}\n    h6      { font-size: 10pt; font-weight: bold; color: green; }\n    h5      { font-size: 11pt; font-weight: bold; color: green; }\n    h4      { font-size: 13pt; font-weight: bold; color: green; }\n    address {                  font-weight: normal}\n    td      { solid #000; vertical-align:top; }\n    th      { solid #000; vertical-align:top; font-weight: bold; }\n    table   { solid #000; border-collapse: collapse;}\n    </style>\n</head>\n<body lang=\\\"en-US\\\">\n    <p>All files in this directory and in all subdirectories are released under\n    the &quot;Modelica License&nbsp;2&quot; (if not explicitly noted\n    otherwise).</p>\n    <p><a href=\\\"#The_Modelica_License_2-outline\\\">The Modelica\n    License&nbsp;2</a><br>\n    <a href=\\\"#How_to_Apply_the_Modelica_License_2-outline\\\">How to Apply the\n    Modelica License&nbsp;2</a><br>\n    <a href=\\\"#Frequently_Asked_Questions-outline\\\">Frequently Asked\n    Questions</a><br></p>\n    <hr>\n    <h4><a name=\\\"The_Modelica_License_2-outline\\\" id=\n    \\\"The_Modelica_License_2-outline\\\"></a>The Modelica License&nbsp;2</h4>\n    <p><strong>Preamble.</strong> The goal of this license is that Modelica\n    related model libraries, software, images, documents, data files etc. can\n    be used freely in the original or a modified form, in open source and in\n    commercial environments (as long as the license conditions below are\n    fulfilled, in particular sections&nbsp;2c) and 2d). The Original Work is\n    provided free of charge and the use is completely at your own risk.\n    Developers of free Modelica packages are encouraged to utilize this license\n    for their work.</p>\n    <p>The Modelica License applies to any Original Work that contains the\n    following licensing notice adjacent to the copyright notice(s) for this\n    Original Work:</p>\n    <p><strong>Licensed by &lt;name of Licensor&gt; under the Modelica\n    License&nbsp;2</strong></p>\n    <p><strong>1. Definitions.</strong></p>\n    <ol type=\\\"a\\\">\n        <li>&quot;License&quot; is this Modelica License.</li>\n        <li>&quot;Original Work&quot; is any work of authorship, including\n        software, images, documents, data files, that contains the above\n        licensing notice or that is packed together with a licensing notice\n        referencing it.</li>\n        <li>&quot;Licensor&quot; is the provider of the Original Work who has\n        placed this licensing notice adjacent to the copyright notice(s) for\n        the Original Work. The Original Work is either directly provided by the\n        owner of the Original Work, or by a licensee of the owner.</li>\n        <li>&quot;Derivative Work&quot; is any modification of the Original\n        Work which represents, as a whole, an original work of authorship. For\n        the matter of clarity and as examples:\n            <ol type=\\\"a\\\">\n                <li>Derivative Work shall not include work that remains\n                separable from the Original Work, as well as merely extracting\n                a part of the Original Work without modifying it.</li>\n                <li>Derivative Work shall not include (a) fixing of errors\n                and/or (b) adding vendor specific Modelica annotations and/or\n                (c) using a subset of the classes of a Modelica package, and/or\n                (d) using a different representation, e.g., a binary\n                representation.</li>\n                <li>Derivative Work shall include classes that are copied from\n                the Original Work where declarations, equations or the\n                documentation are modified.</li>\n                <li>Derivative Work shall include executables to simulate the\n                models that are generated by a Modelica translator based on the\n                Original Work (of a Modelica package).</li>\n            </ol>\n        </li>\n        <li>&quot;Modified Work&quot; is any modification of the Original\n        Work with the following exceptions: (a) fixing of errors and/or (b)\n        adding vendor specific Modelica annotations and/or (c) using a subset\n        of the classes of a Modelica package, and/or (d) using a different\n        representation, e.g., a binary representation.</li>\n        <li>&quot;Source Code&quot; means the preferred form of the Original\n        Work for making modifications to it and all available documentation\n        describing how to modify the Original Work.</li>\n        <li>&quot;You&quot; means an individual or a legal entity exercising\n        rights under, and complying with all of the terms of, this\n        License.</li>\n        <li>&quot;Modelica package&quot; means any Modelica library that is\n        defined with the\n        &quot;<code><strong>package</strong>&nbsp;&lt;Name&gt;&nbsp;...&nbsp;<strong>end</strong>&nbsp;&lt;Name&gt;;</code>&quot;\n        Modelica language element.</li>\n    </ol>\n    <p><strong>2. Grant of Copyright License.</strong> Licensor grants You a\n    worldwide, royalty-free, non-exclusive, sublicensable license, for the\n    duration of the copyright, to do the following:</p>\n    <ol type=\\\"a\\\">\n        <li>\n            <p>To reproduce the Original Work in copies, either alone or as\n            part of a collection.</p>\n        </li>\n        <li>\n            <p>To create Derivative Works according to Section&nbsp;1d) of this\n            License.</p>\n        </li>\n        <li>\n            <p>To distribute or communicate to the public copies of the\n            <u>Original Work</u> or a <u>Derivative Work</u> under <u>this\n            License</u>. No fee, neither as a copyright-license fee, nor as a\n            selling fee for the copy as such may be charged under this License.\n            Furthermore, a verbatim copy of this License must be included in\n            any copy of the Original Work or a Derivative Work under this\n            License.<br>\n            For the matter of clarity, it is permitted A) to distribute or\n            communicate such copies as part of a (possible commercial)\n            collection where other parts are provided under different licenses\n            and a license fee is charged for the other parts only and B) to\n            charge for mere printing and shipping costs.</p>\n        </li>\n        <li>\n            <p>To distribute or communicate to the public copies of a\n            <u>Derivative Work</u>, alternatively to Section&nbsp;2c), under\n            <u>any other license</u> of your choice, especially also under a\n            license for commercial/proprietary software, as long as You comply\n            with Sections&nbsp;3, 4 and 8 below.<br>\n            For the matter of clarity, no restrictions regarding fees, either\n            as to a copyright-license fee or as to a selling fee for the copy\n            as such apply.</p>\n        </li>\n        <li>\n            <p>To perform the Original Work publicly.</p>\n        </li>\n        <li>\n            <p>To display the Original Work publicly.</p>\n        </li>\n    </ol>\n    <p><strong>3. Acceptance.</strong> Any use of the Original Work or a\n    Derivative Work, or any action according to either Section&nbsp;2a) to 2f)\n    above constitutes Your acceptance of this License.</p>\n    <p><strong>4. Designation of Derivative Works and of Modified\n    Works.</strong> The identifying designation of Derivative Work and of\n    Modified Work must be different to the corresponding identifying\n    designation of the Original Work. This means especially that the\n    (root-level) name of a Modelica package under this license must be changed\n    if the package is modified (besides fixing of errors, adding vendor\n    specific Modelica annotations, using a subset of the classes of a Modelica\n    package, or using another representation, e.g. a binary\n    representation).</p>\n    <p><strong>5. Grant of Patent License.</strong> Licensor grants You a\n    worldwide, royalty-free, non-exclusive, sublicensable license, under patent\n    claims owned by the Licensor or licensed to the Licensor by the owners of\n    the Original Work that are embodied in the Original Work as furnished by\n    the Licensor, for the duration of the patents, to make, use, sell, offer\n    for sale, have made, and import the Original Work and Derivative Works\n    under the conditions as given in Section&nbsp;2. For the matter of clarity,\n    the license regarding Derivative Works covers patent claims to the extent\n    as they are embodied in the Original Work only.</p>\n    <p><strong>6. Provision of Source Code.</strong> Licensor agrees to provide\n    You with a copy of the Source Code of the Original Work but reserves the\n    right to decide freely on the manner of how the Original Work is\n    provided.<br>\n    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;For the matter of clarity, Licensor\n    might provide only a binary representation of the Original Work. In that\n    case, You may (a) either reproduce the Source Code from the binary\n    representation if this is possible (e.g., by performing a copy of an\n    encrypted Modelica package, if encryption allows the copy operation) or (b)\n    request the Source Code from the Licensor who will provide it to You.</p>\n    <p><strong>7. Exclusions from License Grant.</strong> Neither the names of\n    Licensor, nor the names of any contributors to the Original Work, nor any\n    of their trademarks or service marks, may be used to endorse or promote\n    products derived from this Original Work without express prior permission\n    of the Licensor. Except as otherwise expressly stated in this License and\n    in particular in Sections&nbsp;2 and 5, nothing in this License grants any\n    license to Licensor&apos;s trademarks, copyrights, patents, trade secrets or any\n    other intellectual property, and no patent license is granted to make, use,\n    sell, offer for sale, have made, or import embodiments of any patent\n    claims.<br>\n    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;No license is granted to the trademarks\n    of Licensor even if such trademarks are included in the Original Work,\n    except as expressly stated in this License. Nothing in this License shall\n    be interpreted to prohibit Licensor from licensing under terms different\n    from this License any Original Work that Licensor otherwise would have a\n    right to license.</p>\n    <p><strong>8. Attribution Rights.</strong> You must retain in the Source\n    Code of the Original Work and of any Derivative Works that You create, all\n    author, copyright, patent, or trademark notices, as well as any descriptive\n    text identified therein as an &quot;Attribution Notice&quot;. The same\n    applies to the licensing notice of this License in the Original Work. For\n    the matter of clarity, &quot;author notice&quot; means the notice that\n    identifies the original author(s).<br>\n    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;You must cause the Source Code for any\n    Derivative Works that You create to carry a prominent Attribution Notice\n    reasonably calculated to inform recipients that You have modified the\n    Original Work.<br>\n    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;In case the Original Work or Derivative\n    Work is not provided in Source Code, the Attribution Notices shall be\n    appropriately displayed, e.g., in the documentation of the Derivative\n    Work.</p>\n    <p><strong>9. Disclaimer of Warranty.<br></strong> <u><strong>The Original\n    Work is provided under this License on an &quot;as is&quot; basis and\n    without warranty, either express or implied, including, without limitation,\n    the warranties of non-infringement, merchantability or fitness for a\n    particular purpose. The entire risk as to the quality of the Original Work\n    is with You.</strong></u> This disclaimer of warranty constitutes an\n    essential part of this License. No license to the Original Work is granted\n    by this License except under this disclaimer.</p>\n    <p><strong>10. Limitation of Liability.</strong> Under no circumstances and\n    under no legal theory, whether in tort (including negligence), contract, or\n    otherwise, shall the Licensor, the owner or a licensee of the Original Work\n    be liable to anyone for any direct, indirect, general, special, incidental,\n    or consequential damages of any character arising as a result of this\n    License or the use of the Original Work including, without limitation,\n    damages for loss of goodwill, work stoppage, computer failure or\n    malfunction, or any and all other commercial damages or losses. This\n    limitation of liability shall not apply to the extent applicable law\n    prohibits such limitation.</p>\n    <p><strong>11. Termination.</strong> This License conditions your rights to\n    undertake the activities listed in Section&nbsp;2 and 5, including your\n    right to create Derivative Works based upon the Original Work, and doing so\n    without observing these terms and conditions is prohibited by copyright law\n    and international treaty. Nothing in this License is intended to affect\n    copyright exceptions and limitations. This License shall terminate\n    immediately and You may no longer exercise any of the rights granted to You\n    by this License upon your failure to observe the conditions of this\n    license.</p>\n    <p><strong>12. Termination for Patent Action.</strong> This License shall\n    terminate automatically and You may no longer exercise any of the rights\n    granted to You by this License as of the date You commence an action,\n    including a cross-claim or counterclaim, against Licensor, any owners of\n    the Original Work or any licensee alleging that the Original Work infringes\n    a patent. This termination provision shall not apply for an action alleging\n    patent infringement through combinations of the Original Work under\n    combination with other software or hardware.</p>\n    <p><strong>13. Jurisdiction.</strong> Any action or suit relating to this\n    License may be brought only in the courts of a jurisdiction wherein the\n    Licensor resides and under the laws of that jurisdiction excluding its\n    conflict-of-law provisions. The application of the United Nations\n    Convention on Contracts for the International Sale of Goods is expressly\n    excluded. Any use of the Original Work outside the scope of this License or\n    after its termination shall be subject to the requirements and penalties of\n    copyright or patent law in the appropriate jurisdiction. This section shall\n    survive the termination of this License.</p>\n    <p><strong>14. Attorneys&apos; Fees.</strong> In any action to enforce the terms\n    of this License or seeking damages relating thereto, the prevailing party\n    shall be entitled to recover its costs and expenses, including, without\n    limitation, reasonable attorneys&apos; fees and costs incurred in connection\n    with such action, including any appeal of such action. This section shall\n    survive the termination of this License.</p>\n    <p><strong>15. Miscellaneous.</strong></p>\n    <ol type=\\\"a\\\">\n        <li>If any provision of this License is held to be unenforceable, such\n        provision shall be reformed only to the extent necessary to make it\n        enforceable.</li>\n        <li>No verbal ancillary agreements have been made. Changes and\n        additions to this License must appear in writing to be valid. This also\n        applies to changing the clause pertaining to written form.</li>\n        <li>You may use the Original Work in all ways not otherwise restricted\n        or conditioned by this License or by law, and Licensor promises not to\n        interfere with or be responsible for such uses by You.</li>\n    </ol>\n    <hr>\n    <h4><a name=\\\"How_to_Apply_the_Modelica_License_2-outline\\\" id=\n    \\\"How_to_Apply_the_Modelica_License_2-outline\\\"></a> How to Apply the\n    Modelica License&nbsp;2</h4>\n    <p>At the top level of your Modelica package and at every important\n    subpackage, add the following notices in the info layer of the package:</p>\n    <p>Licensed by &lt;Licensor&gt; under the Modelica License&nbsp;2<br>\n    Copyright &copy; &lt;year1&gt;-&lt;year2&gt;, &lt;name of copyright\n    holder(s)&gt;.</p>\n    <p><em>This Modelica package is <u>free</u> software and the use is\n    completely at <u>your own risk</u>; it can be redistributed and/or modified\n    under the terms of the Modelica License&nbsp;2. For license conditions\n    (including the disclaimer of warranty) see <a href=\n    \\\"modelica://Modelica.UsersGuide.ModelicaLicense2\\\">Modelica.UsersGuide.ModelicaLicense2</a>\n    or visit <a href=\n    \\\"http://www.modelica.org/licenses/ModelicaLicense2\\\">http://www.modelica.org/licenses/ModelicaLicense2</a>.</em></p>\n    <p>Include a copy of the Modelica License&nbsp;2 under\n    <strong>&lt;library&gt;.UsersGuide.ModelicaLicense2</strong> (use <a href=\n    \\\"http://www.modelica.org/licenses/ModelicaLicense2.mo\\\">http://www.modelica.org/licenses/ModelicaLicense2.mo</a>).\n    Furthermore, add the list of authors and contributors under\n    <strong>&lt;library&gt;.UsersGuide.Contributors</strong> or\n    <strong>&lt;library&gt;.UsersGuide.Contact</strong>.</p>\n    <p>For example, sublibrary Modelica.Blocks of the Modelica Standard Library\n    may have the following notices:</p>\n    <p>Licensed by Modelica Association under the Modelica License&nbsp;2<br>\n    Copyright &copy; 1998-2008, Modelica Association.</p>\n    <p><em>This Modelica package is <u>free</u> software and the use is\n    completely at <u>your own risk</u>; it can be redistributed and/or modified\n    under the terms of the Modelica License&nbsp;2. For license conditions\n    (including the disclaimer of warranty) see <a href=\n    \\\"modelica://Modelica.UsersGuide.ModelicaLicense2\\\">Modelica.UsersGuide.ModelicaLicense2</a>\n    or visit <a href=\n    \\\"http://www.modelica.org/licenses/ModelicaLicense2\\\">http://www.modelica.org/licenses/ModelicaLicense2</a>.</em></p>\n    <p>For C-source code and documents, add similar notices in the\n    corresponding file.</p>\n    <p>For images, add a &quot;readme.txt&quot; file to the directories where\n    the images are stored and include a similar notice in this file.</p>\n    <p>In these cases, save a copy of the Modelica License&nbsp;2 in one\n    directory of the distribution, e.g., <a href=\n    \\\"http://www.modelica.org/licenses/ModelicaLicense2.html\\\">http://www.modelica.org/licenses/ModelicaLicense2.html</a>\n    in directory\n    <strong>&lt;library&gt;/Resources/Documentation/ModelicaLicense2.html</strong>.</p>\n    <hr>\n    <h5><a name=\\\"Frequently_Asked_Questions-outline\\\" id=\n    \\\"Frequently_Asked_Questions-outline\\\"></a> Frequently Asked Questions</h5>\n    <p>This section contains questions/answer to users and/or distributors of\n    Modelica packages and/or documents under Modelica License&nbsp;2. Note, the\n    answers to the questions below are not a legal interpretation of the\n    Modelica License&nbsp;2. In case of a conflict, the language of the license\n    shall prevail.</p>\n    <h6>Using or Distributing a Modelica <u>Package</u> under the Modelica\n    License&nbsp;2</h6>\n    <p><strong>What are the main differences to the previous version of the\n    Modelica License?</strong></p>\n    <ol>\n        <li>\n            <p>Modelica License&nbsp;1 is unclear whether the licensed Modelica\n            package can be distributed under a different license.\n            Version&nbsp;2 explicitly allows that &quot;Derivative Work&quot;\n            can be distributed under any license of Your choice, see examples\n            in Section&nbsp;1d) as to what qualifies as Derivative Work (so,\n            version&nbsp;2 is clearer).</p>\n        </li>\n        <li>\n            <p>If You modify a Modelica package under Modelica License&nbsp;2\n            (besides fixing of errors, adding vendor specific Modelica\n            annotations, using a subset of the classes of a Modelica package,\n            or using another representation, e.g., a binary representation),\n            you must rename the root-level name of the package for your\n            distribution. In version&nbsp;1 you could keep the name (so,\n            version&nbsp;2 is more restrictive). The reason of this restriction\n            is to reduce the risk that Modelica packages are available that\n            have identical names, but different functionality.</p>\n        </li>\n        <li>\n            <p>Modelica License&nbsp;1 states that &quot;It is not allowed to\n            charge a fee for the original version or a modified version of the\n            software, besides a reasonable fee for distribution and\n            support&quot;. Version&nbsp;2 has a similar intention for all\n            Original Work under <u>Modelica License&nbsp;2</u> (to remain free\n            of charge and open source) but states this more clearly as\n            &quot;No fee, neither as a copyright-license fee, nor as a selling\n            fee for the copy as such may be charged&quot;. Contrary to\n            version&nbsp;1, Modelica License&nbsp;2 has no restrictions on fees\n            for Derivative Work that is provided under a different license (so,\n            version&nbsp;2 is clearer and has fewer restrictions).</p>\n        </li>\n        <li>\n            <p>Modelica License&nbsp;2 introduces several useful provisions for\n            the licensee (articles&nbsp;5, 6, 12), and for the licensor\n            (articles&nbsp;7, 12, 13, 14) that have no counter part in\n            version&nbsp;1.</p>\n        </li>\n        <li>\n            <p>Modelica License&nbsp;2 can be applied to all type of work,\n            including documents, images and data files, contrary to\n            version&nbsp;1 that was dedicated for software only (so,\n            version&nbsp;2 is more general).</p>\n        </li>\n    </ol>\n    <p><strong>Can I distribute a Modelica package (under Modelica\n    License&nbsp;2) as part of my commercial Modelica modeling and simulation\n    environment?</strong></p>\n    <p>Yes, according to Section&nbsp;2c). However, you are not allowed to\n    charge a fee for this part of your environment. Of course, you can charge\n    for your part of the environment.</p>\n    <p><strong>Can I distribute a Modelica package (under Modelica\n    License&nbsp;2) under a different license?</strong></p>\n    <p>No. The license of an unmodified Modelica package cannot be changed\n    according to Sections&nbsp;2c) and 2d). This means that you cannot\n    <u>sell</u> copies of it, any distribution has to be free of charge.</p>\n    <p><strong>Can I distribute a Modelica package (under Modelica\n    License&nbsp;2) under a different license when I first encrypt the\n    package?</strong></p>\n    <p>No. Merely encrypting a package does not qualify for Derivative Work and\n    therefore the encrypted package has to stay under Modelica\n    License&nbsp;2.</p>\n    <p><strong>Can I distribute a Modelica package (under Modelica\n    License&nbsp;2) under a different license when I first add classes to the\n    package?</strong></p>\n    <p>No. The package itself remains unmodified, i.e., it is Original Work,\n    and therefore the license for this part must remain under Modelica\n    License&nbsp;2. The newly added classes can be, however, under a different\n    license.</p>\n    <p><strong>Can I copy a class out of a Modelica package (under Modelica\n    License&nbsp;2) and include it</strong> <u><strong>unmodified</strong></u>\n    <strong>in a Modelica package under a</strong>\n    <u><strong>commercial/proprietary</strong></u>\n    <strong>license?</strong></p>\n    <p>No, according to article&nbsp;2c). However, you can include model,\n    block, function, package, record and connector classes in your Modelica\n    package under <u>Modelica License&nbsp;2</u>. This means that your Modelica\n    package could be under a commercial/proprietary license, but one or more\n    classes of it are under Modelica License&nbsp;2.<br>\n    Note, a &quot;type&quot; class (e.g., type Angle =\n    Real(unit=&quot;rad&quot;)) can be copied and included unmodified under a\n    commercial/proprietary license (for details, see the next question).</p>\n    <p><strong>Can I copy a type class or</strong> <u><strong>part</strong></u>\n    <strong>of a model, block, function, record, connector class, out of a\n    Modelica package (under Modelica License&nbsp;2) and include it modified or\n    unmodified in a Modelica package under a</strong>\n    <u><strong>commercial/proprietary</strong></u>\n    <strong>license?</strong></p>\n    <p>Yes, according to article&nbsp;2d), since this will in the end usually\n    qualify as Derivative Work. The reasoning is the following: A type class or\n    part of another class (e.g., an equation, a declaration, part of a class\n    description) cannot be utilized &quot;by its own&quot;. In order to make\n    this &quot;usable&quot;, you have to add additional code in order that\n    the class can be utilized. This is therefore usually Derivative Work and\n    Derivative Work can be provided under a different license. Note, this only\n    holds, if the additional code introduced is sufficient to qualify for\n    Derivative Work. Merely, just copying a class and changing, say, one\n    character in the documentation of this class would be no Derivative Work\n    and therefore the copied code would have to stay under Modelica\n    License&nbsp;2.</p>\n    <p><strong>Can I copy a class out of a Modelica package (under Modelica\n    License&nbsp;2) and include it in</strong> <u><strong>modified</strong></u>\n    <strong>form in a</strong> <u><strong>commercial/proprietary</strong></u>\n    <strong>Modelica package?</strong></p>\n    <p>Yes. If the modification can be seen as a &quot;Derivative Work&quot;,\n    you can place it under your commercial/proprietary license. If the\n    modification does not qualify as &quot;Derivative Work&quot; (e.g., bug\n    fixes, vendor specific annotations), it must remain under Modelica\n    License&nbsp;2. This means that your Modelica package could be under a\n    commercial/proprietary license, but one or more parts of it are under\n    Modelica License&nbsp;2.</p>\n    <p><strong>Can I distribute a &quot;save total model&quot; under my\n    commercial/proprietary license, even if classes under Modelica\n    License&nbsp;2 are included?</strong></p>\n    <p>Your classes of the &quot;save total model&quot; can be distributed\n    under your commercial/proprietary license, but the classes under Modelica\n    License&nbsp;2 must remain under Modelica License&nbsp;2. This means you\n    can distribute a &quot;save total model&quot;, but some parts might be\n    under Modelica License&nbsp;2.</p>\n    <p><strong>Can I distribute a Modelica package (under Modelica\n    License&nbsp;2) in encrypted form?</strong></p>\n    <p>Yes. Note, if the encryption does not allow &quot;copying&quot; of\n    classes (in to unencrypted Modelica source code), you have to send the\n    Modelica source code of this package to your customer, if he/she wishes it,\n    according to article&nbsp;6.</p>\n    <p><strong>Can I distribute an executable under my commercial/proprietary\n    license, if the model from which the executable is generated uses models\n    from a Modelica package under Modelica License&nbsp;2?</strong></p>\n    <p>Yes, according to article&nbsp;2d), since this is seen as Derivative\n    Work. The reasoning is the following: An executable allows the simulation\n    of a concrete model, whereas models from a Modelica package (without\n    pre-processing, translation, tool run-time library) are not able to be\n    simulated without tool support. By the processing of the tool and by its\n    run-time libraries, significant new functionality is added (a model can be\n    simulated whereas previously it could not be simulated) and functionality\n    available in the package is removed (e.g., to build up a new model by\n    dragging components of the package is no longer possible with the\n    executable).</p>\n    <p><strong>Is my modification to a Modelica package (under Modelica\n    License&nbsp;2) a Derivative Work?</strong></p>\n    <p>It is not possible to give a general answer to it. To be regarded as\n    &quot;an original work of authorship&quot;, a derivative work must be\n    different enough from the original or must contain a substantial amount of\n    new material. Making minor changes or additions of little substance to a\n    preexisting work will not qualify the work as a new version for such\n    purposes.</p>\n    <h6>Using or Distributing a Modelica <u>Document</u> under the Modelica\n    License&nbsp;2</h6>\n    <p>This section is devoted especially for the following applications:</p>\n    <ol type=\\\"a\\\">\n        <li>\n            <p>A Modelica tool extracts information out of a Modelica package\n            and presents the result in form of a &quot;manual&quot; for this\n            package in, e.g., html, doc, or pdf format.</p>\n        </li>\n        <li>\n            <p>The Modelica language specification is a document defining the\n            Modelica language. It will be licensed under Modelica\n            License&nbsp;2.</p>\n        </li>\n        <li>\n            <p>Someone writes a book about the Modelica language and/or\n            Modelica packages and uses information which is available in the\n            Modelica language specification and/or the corresponding Modelica\n            package.</p>\n        </li>\n    </ol>\n    <p><strong>Can I sell a manual that was basically derived by extracting\n    information automatically from a Modelica package under Modelica\n    License&nbsp;2 (e.g., a &quot;reference guide&quot; of the Modelica\n    Standard Library)?</strong></p>\n    <p>Yes. Extracting information from a Modelica package, and providing it in\n    a human readable, suitable format, like html, doc or pdf format, where the\n    content is significantly modified (e.g. tables with interface information\n    are constructed from the declarations of the public variables) qualifies as\n    Derivative Work and there are no restrictions to charge a fee for\n    Derivative Work under alternative&nbsp;2d).</p>\n    <p><strong>Can I copy a text passage out of a Modelica document (under\n    Modelica License&nbsp;2) and use it</strong>\n    <u><strong>unmodified</strong></u> <strong>in my document (e.g. the\n    Modelica syntax description in the Modelica Specification)?</strong></p>\n    <p>Yes. In case you distribute your document, the copied parts are still\n    under Modelica License&nbsp;2 and you are not allowed to charge a license\n    fee for this part. You can, of course, charge a fee for the rest of your\n    document.</p>\n    <p><strong>Can I copy a text passage out of a Modelica document (under\n    Modelica License&nbsp;2) and use it in</strong>\n    <u><strong>modified</strong></u> <strong>form in my document?</strong></p>\n    <p>Yes, the creation of Derivative Works is allowed. In case the content is\n    significantly modified this qualifies as Derivative Work and there are no\n    restrictions to charge a fee for Derivative Work under\n    alternative&nbsp;2d).</p>\n    <p><strong>Can I sell a printed version of a Modelica document (under\n    Modelica License&nbsp;2), e.g., the Modelica Language\n    Specification?</strong></p>\n    <p>No, if you are not the copyright-holder, since article&nbsp;2c) does not\n    allow a selling fee for a (in this case physical) copy. However, mere\n    printing and shipping costs may be recovered.</p>\n</body>\n</html>\"));\nend ModelicaLicense2;\n"
  },
  {
    "path": "omg_grid/UsersGuide/package.mo",
    "content": "within omg_grid;\npackage UsersGuide \"User's guide\"\n  extends Modelica.Icons.Information;\n\n\n\n  annotation (DocumentationClass=true, Documentation(info=\"<html>\n\n  <p>This library aims at providing models the simulation of Microgrids.</p>\n\n   <p>   \n   </p>\n  \n</html>\"));\nend UsersGuide;\n"
  },
  {
    "path": "omg_grid/UsersGuide/package.order",
    "content": "Contact\nModelicaLicense2\n"
  },
  {
    "path": "omg_grid/create_fmu.mos",
    "content": "OpenModelica.Scripting.loadFile(\"grid.mo\"); getErrorString();\nsetCommandLineOptions(\"-d=newInst\"); getErrorString();\nsetCommandLineOptions(\"-d=initialization\"); getErrorString();\nsetCommandLineOptions(\"--simCodeTarget=Cpp\"); getErrorString();\nsetCommandLineOptions(\"-d=-disableDirectionalDerivatives\"); getErrorString();\nOpenModelica.Scripting.translateModelFMU(grid.network, version=\"2.0\", fmuType = \"me\"); getErrorString();\n"
  },
  {
    "path": "omg_grid/grid.mo",
    "content": "package grid\n  import SI = Modelica.SIunits;\n\n  package filter\n    model pi\n      parameter SI.Capacitance C1 = 0.00001;\n      parameter SI.Capacitance C2 = 0.00001;\n      parameter SI.Capacitance C3 = 0.00001;\n      parameter SI.Capacitance C4 = 0.00001;\n      parameter SI.Capacitance C5 = 0.00001;\n      parameter SI.Capacitance C6 = 0.00001;\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      parameter SI.Resistance R1 = 0.01;\n      parameter SI.Resistance R2 = 0.01;\n      parameter SI.Resistance R3 = 0.01;\n      parameter SI.Resistance R4 = 0.01;\n      parameter SI.Resistance R5 = 0.01;\n      parameter SI.Resistance R6 = 0.01;\n      parameter SI.Resistance R7 = 0.01;\n      parameter SI.Resistance R8 = 0.01;\n      parameter SI.Resistance R9 = 0.01;\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-14, 28}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {-14, 52}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {-14, 78}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {-70, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {-48, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {-26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {0, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n        Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n        Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n        Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation(\n        Placement(visible = true, transformation(origin = {26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation(\n        Placement(visible = true, transformation(origin = {46, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation(\n        Placement(visible = true, transformation(origin = {64, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor1(R = R1) annotation(\n        Placement(visible = true, transformation(origin = {-68, -6}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor2(R = R2) annotation(\n        Placement(visible = true, transformation(origin = {-48, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor3(R = R3) annotation(\n        Placement(visible = true, transformation(origin = {-26, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor4(R = R4) annotation(\n        Placement(visible = true, transformation(origin = {10, 28}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor5(R = R5) annotation(\n        Placement(visible = true, transformation(origin = {10, 52}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor6(R = R6) annotation(\n        Placement(visible = true, transformation(origin = {10, 78}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor7(R = R7) annotation(\n        Placement(visible = true, transformation(origin = {26, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor8(R = R8) annotation(\n        Placement(visible = true, transformation(origin = {46, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor9(R = R9) annotation(\n        Placement(visible = true, transformation(origin = {64, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n    equation\n      connect(inductor1.n, resistor4.p) annotation(\n        Line(points = {{-4, 28}, {0, 28}, {0, 28}, {0, 28}}, color = {0, 0, 255}));\n      connect(inductor2.n, resistor5.p) annotation(\n        Line(points = {{-4, 52}, {-4, 52}, {-4, 52}, {0, 52}}, color = {0, 0, 255}));\n      connect(inductor3.n, resistor6.p) annotation(\n        Line(points = {{-4, 78}, {0, 78}, {0, 78}, {0, 78}}, color = {0, 0, 255}));\n      connect(resistor3.p, pin3) annotation(\n        Line(points = {{-26, 2}, {-26, 2}, {-26, 18}, {-36, 18}, {-36, 78}, {-90, 78}, {-90, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255}));\n      connect(resistor2.p, pin2) annotation(\n        Line(points = {{-48, 2}, {-48, 2}, {-48, 52}, {-84, 52}, {-84, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255}));\n  connect(resistor1.p, pin1) annotation(\n        Line(points = {{-68, 4}, {-68, -39}, {-100, -39}, {-100, -60}}, color = {0, 0, 255}));\n  connect(resistor1.n, capacitor1.p) annotation(\n        Line(points = {{-68, -16}, {-75, -16}, {-75, -28}, {-70, -28}}, color = {0, 0, 255}));\n      connect(resistor2.n, capacitor2.p) annotation(\n        Line(points = {{-48, -18}, {-48, -18}, {-48, -28}, {-48, -28}}, color = {0, 0, 255}));\n      connect(resistor3.n, capacitor3.p) annotation(\n        Line(points = {{-26, -18}, {-26, -18}, {-26, -28}, {-26, -28}}, color = {0, 0, 255}));\n      connect(resistor9.n, capacitor6.p) annotation(\n        Line(points = {{64, -18}, {64, -18}, {64, -28}, {64, -28}}, color = {0, 0, 255}));\n      connect(capacitor6.n, capacitor5.n) annotation(\n        Line(points = {{64, -48}, {46, -48}}, color = {0, 0, 255}));\n      connect(capacitor5.p, resistor8.n) annotation(\n        Line(points = {{46, -28}, {46, -28}, {46, -18}, {46, -18}}, color = {0, 0, 255}));\n      connect(resistor5.n, resistor8.p) annotation(\n        Line(points = {{20, 52}, {46, 52}, {46, 2}}, color = {0, 0, 255}));\n      connect(resistor7.n, capacitor4.p) annotation(\n        Line(points = {{26, -18}, {26, -18}, {26, -28}, {26, -28}}, color = {0, 0, 255}));\n      connect(resistor6.n, pin6) annotation(\n        Line(points = {{20, 78}, {84, 78}, {84, 60}, {100, 60}}, color = {0, 0, 255}));\n      connect(resistor5.n, pin5) annotation(\n        Line(points = {{20, 52}, {84, 52}, {84, 0}, {100, 0}}, color = {0, 0, 255}));\n      connect(resistor4.n, pin4) annotation(\n        Line(points = {{20, 28}, {74, 28}, {74, 28}, {76, 28}, {76, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255}));\n      connect(resistor6.n, resistor9.p) annotation(\n        Line(points = {{20, 78}, {64, 78}, {64, 2}, {64, 2}}, color = {0, 0, 255}));\n      connect(resistor4.n, resistor7.p) annotation(\n        Line(points = {{20, 28}, {26, 28}, {26, 2}, {26, 2}}, color = {0, 0, 255}));\n      connect(pin1, inductor1.p) annotation(\n        Line(points = {{-100, -60}, {-80, -60}, {-80, 28}, {-24, 28}}, color = {0, 0, 255}));\n      connect(pin3, inductor3.p) annotation(\n        Line(points = {{-100, 60}, {-90, 60}, {-90, 78}, {-24, 78}}, color = {0, 0, 255}));\n      connect(capacitor3.n, ground1.p) annotation(\n        Line(points = {{-26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255}));\n      connect(capacitor4.n, ground1.p) annotation(\n        Line(points = {{26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255}));\n      connect(inductor2.p, pin2) annotation(\n        Line(points = {{-24, 52}, {-84, 52}, {-84, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255}));\n      connect(capacitor2.n, capacitor3.n) annotation(\n        Line(points = {{-48, -48}, {-26, -48}, {-26, -48}, {-26, -48}}, color = {0, 0, 255}));\n      connect(capacitor1.n, capacitor2.n) annotation(\n        Line(points = {{-70, -48}, {-48, -48}, {-48, -48}, {-48, -48}}, color = {0, 0, 255}));\n      connect(capacitor5.n, capacitor4.n) annotation(\n        Line(points = {{46, -48}, {26, -48}, {26, -48}, {26, -48}}, color = {0, 0, 255}));\n    end pi;\n\n    model lcl\n      parameter SI.Capacitance C1 = 0.00001;\n      parameter SI.Capacitance C2 = 0.00001;\n      parameter SI.Capacitance C3 = 0.00001;\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      parameter SI.Inductance L4 = 0.001;\n      parameter SI.Inductance L5 = 0.001;\n      parameter SI.Inductance L6 = 0.001;\n      parameter SI.Resistance R1 = 0.01;\n      parameter SI.Resistance R2 = 0.01;\n      parameter SI.Resistance R3 = 0.01;\n      parameter SI.Resistance R4 = 0.01;\n      parameter SI.Resistance R5 = 0.01;\n      parameter SI.Resistance R6 = 0.01;\n      parameter SI.Resistance R7 = 0.01;\n      parameter SI.Resistance R8 = 0.01;\n      parameter SI.Resistance R9 = 0.01;\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {-64, 58}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {-72, 86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {38, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {-28, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n        Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n        Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n        Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation(\n        Placement(visible = true, transformation(origin = {68, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation(\n        Placement(visible = true, transformation(origin = {70, 40}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation(\n        Placement(visible = true, transformation(origin = {74, 62}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor1 annotation(\n        Placement(visible = true, transformation(origin = {-36, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor2 annotation(\n        Placement(visible = true, transformation(origin = {-32, 48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor3 annotation(\n        Placement(visible = true, transformation(origin = {-30, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor4 annotation(\n        Placement(visible = true, transformation(origin = {42, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor5 annotation(\n        Placement(visible = true, transformation(origin = {8, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor6 annotation(\n        Placement(visible = true, transformation(origin = {-22, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor7 annotation(\n        Placement(visible = true, transformation(origin = {32, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor8 annotation(\n        Placement(visible = true, transformation(origin = {40, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor9 annotation(\n        Placement(visible = true, transformation(origin = {34, 68}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    equation\n      connect(resistor2.n, resistor5.p) annotation(\n        Line(points = {{-22, 48}, {8, 48}, {8, 0}}, color = {0, 0, 255}));\n      connect(resistor8.p, resistor2.n) annotation(\n        Line(points = {{30, 44}, {2, 44}, {2, 48}, {-22, 48}}, color = {0, 0, 255}));\n      connect(resistor2.p, inductor2.n) annotation(\n        Line(points = {{-42, 48}, {-50, 48}, {-50, 58}, {-54, 58}}, color = {0, 0, 255}));\n      connect(pin2, inductor2.p) annotation(\n        Line(points = {{-100, 0}, {-91, 0}, {-91, 58}, {-74, 58}}, color = {0, 0, 255}));\n      connect(pin3, inductor3.p) annotation(\n        Line(points = {{-100, 60}, {-93, 60}, {-93, 86}, {-82, 86}}, color = {0, 0, 255}));\n      connect(resistor3.p, inductor3.n) annotation(\n        Line(points = {{-40, 82}, {-47, 82}, {-47, 86}, {-62, 86}}, color = {0, 0, 255}));\n      connect(resistor3.n, resistor9.p) annotation(\n        Line(points = {{-20, 82}, {3, 82}, {3, 68}, {24, 68}}, color = {0, 0, 255}));\n      connect(resistor6.p, resistor3.n) annotation(\n        Line(points = {{-22, 2}, {-22, 41}, {-20, 41}, {-20, 82}}, color = {0, 0, 255}));\n      connect(inductor6.n, pin6) annotation(\n        Line(points = {{84, 62}, {84, 60}, {100, 60}}, color = {0, 0, 255}));\n      connect(resistor9.n, inductor6.p) annotation(\n        Line(points = {{44, 68}, {55, 68}, {55, 62}, {64, 62}}, color = {0, 0, 255}));\n      connect(inductor5.n, pin5) annotation(\n        Line(points = {{80, 40}, {88, 40}, {88, 0}, {100, 0}}, color = {0, 0, 255}));\n      connect(resistor8.n, inductor5.p) annotation(\n        Line(points = {{50, 44}, {55, 44}, {55, 40}, {60, 40}}, color = {0, 0, 255}));\n      connect(resistor7.n, inductor4.p) annotation(\n        Line(points = {{42, 30}, {54, 30}, {54, 6}, {58, 6}}, color = {0, 0, 255}));\n      connect(resistor4.p, resistor7.p) annotation(\n        Line(points = {{42, 0}, {42, 14.5}, {22, 14.5}, {22, 30}}, color = {0, 0, 255}));\n      connect(resistor1.n, resistor7.p) annotation(\n        Line(points = {{-26, 20}, {2, 20}, {2, 30}, {22, 30}}, color = {0, 0, 255}));\n      connect(inductor4.n, pin4) annotation(\n        Line(points = {{78, 6}, {80, 6}, {80, -60}, {100, -60}}, color = {0, 0, 255}));\n      connect(capacitor2.n, capacitor1.n) annotation(\n        Line(points = {{12, -46}, {23, -46}, {23, -56}, {38, -56}}, color = {0, 0, 255}));\n      connect(resistor4.n, capacitor1.p) annotation(\n        Line(points = {{42, -20}, {42, -24}, {38, -24}, {38, -36}}, color = {0, 0, 255}));\n      connect(resistor5.n, capacitor2.p) annotation(\n        Line(points = {{8, -20}, {8, -24}, {12, -24}, {12, -26}}, color = {0, 0, 255}));\n      connect(capacitor3.n, capacitor2.n) annotation(\n        Line(points = {{-28, -46}, {12, -46}}, color = {0, 0, 255}));\n      connect(resistor6.n, capacitor3.p) annotation(\n        Line(points = {{-22, -18}, {-22, -24}, {-28, -24}, {-28, -26}}, color = {0, 0, 255}));\n      connect(resistor1.p, inductor1.n) annotation(\n        Line(points = {{-46, 20}, {-50, 20}, {-50, 20}, {-50, 20}}, color = {0, 0, 255}));\n      connect(pin1, inductor1.p) annotation(\n        Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n      connect(capacitor2.n, ground1.p) annotation(\n        Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255}));\n    end lcl;\n\n    model lc\n      parameter SI.Capacitance C1 = 0.00001;\n      parameter SI.Capacitance C2 = 0.00001;\n      parameter SI.Capacitance C3 = 0.00001;\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      parameter SI.Resistance R1 = 0.01;\n      parameter SI.Resistance R2 = 0.01;\n      parameter SI.Resistance R3 = 0.01;\n      parameter SI.Resistance R4 = 0.01;\n      parameter SI.Resistance R5 = 0.01;\n      parameter SI.Resistance R6 = 0.01;\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n        Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n        Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n        Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor1(R = R1) annotation(\n        Placement(visible = true, transformation(origin = {-34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor2(R = R2) annotation(\n        Placement(visible = true, transformation(origin = {-34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor3(R = R3) annotation(\n        Placement(visible = true, transformation(origin = {-26, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor4(R = R4) annotation(\n        Placement(visible = true, transformation(origin = {32, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor5(R = R5) annotation(\n        Placement(visible = true, transformation(origin = {12, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor6(R = R6) annotation(\n        Placement(visible = true, transformation(origin = {-8, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n    equation\n      connect(resistor3.n, resistor6.p) annotation(\n        Line(points = {{-16, 70}, {-8, 70}, {-8, 2}}, color = {0, 0, 255}));\n      connect(resistor3.n, pin6) annotation(\n        Line(points = {{-16, 70}, {80, 70}, {80, 60}, {100, 60}}, color = {0, 0, 255}));\n      connect(inductor3.n, resistor3.p) annotation(\n        Line(points = {{-50, 70}, {-36, 70}}, color = {0, 0, 255}));\n      connect(pin4, resistor1.n) annotation(\n        Line(points = {{100, -60}, {62, -60}, {62, 20}, {-24, 20}, {-24, 20}}, color = {0, 0, 255}));\n      connect(resistor4.n, capacitor1.p) annotation(\n        Line(points = {{32, -18}, {32, -18}, {32, -26}, {32, -26}}, color = {0, 0, 255}));\n      connect(resistor5.n, capacitor2.p) annotation(\n        Line(points = {{12, -18}, {12, -18}, {12, -26}, {12, -26}}, color = {0, 0, 255}));\n      connect(resistor6.n, capacitor3.p) annotation(\n        Line(points = {{-8, -18}, {-8, -18}, {-8, -26}, {-8, -26}}, color = {0, 0, 255}));\n      connect(pin5, resistor2.n) annotation(\n        Line(points = {{100, 0}, {78, 0}, {78, 44}, {-24, 44}, {-24, 44}}, color = {0, 0, 255}));\n      connect(resistor2.n, resistor5.p) annotation(\n        Line(points = {{-24, 44}, {12, 44}, {12, 2}, {12, 2}}, color = {0, 0, 255}));\n      connect(resistor1.n, resistor4.p) annotation(\n        Line(points = {{-24, 20}, {32, 20}, {32, 2}, {32, 2}}, color = {0, 0, 255}));\n      connect(inductor1.n, resistor1.p) annotation(\n        Line(points = {{-50, 20}, {-44, 20}, {-44, 20}, {-44, 20}}, color = {0, 0, 255}));\n      connect(inductor2.n, resistor2.p) annotation(\n        Line(points = {{-50, 44}, {-44, 44}, {-44, 44}, {-44, 44}}, color = {0, 0, 255}));\n      connect(capacitor3.n, capacitor2.n) annotation(\n        Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255}));\n      connect(capacitor2.n, ground1.p) annotation(\n        Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255}));\n      connect(capacitor2.n, capacitor1.n) annotation(\n        Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255}));\n      connect(pin1, inductor1.p) annotation(\n        Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n      connect(pin3, inductor3.p) annotation(\n        Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255}));\n      connect(pin2, inductor2.p) annotation(\n        Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255}));\n    end lc;\n\n    model lclc\n      parameter SI.Capacitance C1 = 0.00001;\n      parameter SI.Capacitance C2 = 0.00001;\n      parameter SI.Capacitance C3 = 0.00001;\n      parameter SI.Capacitance C4 = 0.00001;\n      parameter SI.Capacitance C5 = 0.00001;\n      parameter SI.Capacitance C6 = 0.00001;\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      parameter SI.Inductance L4 = 0.001;\n      parameter SI.Inductance L5 = 0.001;\n      parameter SI.Inductance L6 = 0.001;\n      parameter SI.Resistance R1 = 0.01;\n      parameter SI.Resistance R2 = 0.01;\n      parameter SI.Resistance R3 = 0.01;\n      parameter SI.Resistance R4 = 0.01;\n      parameter SI.Resistance R5 = 0.01;\n      parameter SI.Resistance R6 = 0.01;\n      parameter SI.Resistance R7 = 0.01;\n      parameter SI.Resistance R8 = 0.01;\n      parameter SI.Resistance R9 = 0.01;\n      parameter SI.Resistance R10 = 0.01;\n      parameter SI.Resistance R11 = 0.01;\n      parameter SI.Resistance R12 = 0.01;\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-82, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {-82, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {-84, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {-2, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {-22, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {-42, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {16, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n        Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n        Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n        Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation(\n        Placement(visible = true, transformation(origin = {34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation(\n        Placement(visible = true, transformation(origin = {34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation(\n        Placement(visible = true, transformation(origin = {36, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation(\n        Placement(visible = true, transformation(origin = {72, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation(\n        Placement(visible = true, transformation(origin = {52, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation(\n        Placement(visible = true, transformation(origin = {32, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor1(R = R1) annotation(\n        Placement(visible = true, transformation(origin = {-56, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor2(R = R2) annotation(\n        Placement(visible = true, transformation(origin = {-56, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor3(R = R3) annotation(\n        Placement(visible = true, transformation(origin = {-56, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor4(R = R4) annotation(\n        Placement(visible = true, transformation(origin = {-2, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor5(R = R5) annotation(\n        Placement(visible = true, transformation(origin = {-22, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor6(R = R6) annotation(\n        Placement(visible = true, transformation(origin = {-42, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor7(R = R7) annotation(\n        Placement(visible = true, transformation(origin = {10, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor8(R = R8) annotation(\n        Placement(visible = true, transformation(origin = {10, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor9(R = R9) annotation(\n        Placement(visible = true, transformation(origin = {10, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor10(R = R10) annotation(\n        Placement(visible = true, transformation(origin = {72, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor11(R = R11) annotation(\n        Placement(visible = true, transformation(origin = {52, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor12(R = R12) annotation(\n        Placement(visible = true, transformation(origin = {32, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n    equation\n      connect(resistor10.p, inductor4.n) annotation(\n        Line(points = {{72, -4}, {72, -4}, {72, 20}, {44, 20}, {44, 20}}, color = {0, 0, 255}));\n      connect(inductor5.n, resistor11.p) annotation(\n        Line(points = {{44, 44}, {54, 44}, {54, 44}, {52, 44}, {52, -4}, {52, -4}}, color = {0, 0, 255}));\n      connect(resistor12.p, inductor6.n) annotation(\n        Line(points = {{32, -4}, {32, -4}, {32, 10}, {58, 10}, {58, 70}, {46, 70}, {46, 70}}, color = {0, 0, 255}));\n      connect(resistor11.n, capacitor5.p) annotation(\n        Line(points = {{52, -24}, {52, -24}, {52, -28}, {52, -28}}, color = {0, 0, 255}));\n      connect(resistor12.n, capacitor6.p) annotation(\n        Line(points = {{32, -24}, {32, -24}, {32, -28}, {32, -28}}, color = {0, 0, 255}));\n      connect(resistor1.n, resistor4.p) annotation(\n        Line(points = {{-46, 20}, {-2, 20}, {-2, -4}, {-2, -4}, {-2, -4}}, color = {0, 0, 255}));\n      connect(resistor2.n, resistor5.p) annotation(\n        Line(points = {{-46, 44}, {-22, 44}, {-22, -4}, {-22, -4}, {-22, -4}}, color = {0, 0, 255}));\n      connect(resistor6.p, resistor3.n) annotation(\n        Line(points = {{-42, -4}, {-42, -4}, {-42, 70}, {-46, 70}, {-46, 70}}, color = {0, 0, 255}));\n      connect(resistor1.n, resistor7.p) annotation(\n        Line(points = {{-46, 20}, {-46, 20}, {-46, 20}, {0, 20}}, color = {0, 0, 255}));\n      connect(resistor2.n, resistor8.p) annotation(\n        Line(points = {{-46, 44}, {-46, 44}, {-46, 44}, {0, 44}}, color = {0, 0, 255}));\n      connect(resistor3.n, resistor9.p) annotation(\n        Line(points = {{-46, 70}, {0, 70}, {0, 70}, {0, 70}}, color = {0, 0, 255}));\n      connect(resistor7.n, inductor4.p) annotation(\n        Line(points = {{20, 20}, {24, 20}, {24, 20}, {24, 20}}, color = {0, 0, 255}));\n      connect(resistor6.n, capacitor3.p) annotation(\n        Line(points = {{-42, -24}, {-42, -24}, {-42, -28}, {-42, -28}}, color = {0, 0, 255}));\n      connect(resistor5.n, capacitor2.p) annotation(\n        Line(points = {{-22, -24}, {-22, -24}, {-22, -24}, {-22, -28}}, color = {0, 0, 255}));\n      connect(resistor4.n, capacitor1.p) annotation(\n        Line(points = {{-2, -24}, {-2, -24}, {-2, -28}, {-2, -28}}, color = {0, 0, 255}));\n      connect(resistor10.n, capacitor4.p) annotation(\n        Line(points = {{72, -24}, {72, -24}, {72, -28}, {72, -28}}, color = {0, 0, 255}));\n      connect(resistor8.n, inductor5.p) annotation(\n        Line(points = {{20, 44}, {24, 44}}, color = {0, 0, 255}));\n      connect(resistor9.n, inductor6.p) annotation(\n        Line(points = {{20, 70}, {26, 70}, {26, 70}, {26, 70}}, color = {0, 0, 255}));\n      connect(inductor1.n, resistor1.p) annotation(\n        Line(points = {{-72, 20}, {-66, 20}, {-66, 20}, {-66, 20}}, color = {0, 0, 255}));\n      connect(inductor3.n, resistor3.p) annotation(\n        Line(points = {{-74, 70}, {-74, 70}, {-74, 70}, {-66, 70}}, color = {0, 0, 255}));\n      connect(inductor2.n, resistor2.p) annotation(\n        Line(points = {{-72, 44}, {-66, 44}, {-66, 44}, {-66, 44}}, color = {0, 0, 255}));\n      connect(inductor4.n, pin4) annotation(\n        Line(points = {{44, 20}, {92, 20}, {92, -60}, {100, -60}}, color = {0, 0, 255}));\n      connect(inductor6.n, pin6) annotation(\n        Line(points = {{46, 70}, {76, 70}, {76, 60}, {100, 60}}, color = {0, 0, 255}));\n      connect(capacitor6.n, ground1.p) annotation(\n        Line(points = {{32, -48}, {16, -48}, {16, -60}}, color = {0, 0, 255}));\n      connect(capacitor6.n, capacitor5.n) annotation(\n        Line(points = {{32, -48}, {52, -48}, {52, -48}, {52, -48}}, color = {0, 0, 255}));\n      connect(capacitor5.n, capacitor4.n) annotation(\n        Line(points = {{52, -48}, {72, -48}, {72, -48}, {72, -48}}, color = {0, 0, 255}));\n      connect(capacitor1.n, ground1.p) annotation(\n        Line(points = {{-2, -48}, {16, -48}, {16, -60}, {16, -60}}, color = {0, 0, 255}));\n      connect(inductor5.n, pin5) annotation(\n        Line(points = {{44, 44}, {94, 44}, {94, 0}, {100, 0}}, color = {0, 0, 255}));\n      connect(capacitor3.n, capacitor2.n) annotation(\n        Line(points = {{-42, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255}));\n      connect(capacitor1.n, capacitor2.n) annotation(\n        Line(points = {{-2, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255}));\n      connect(pin3, inductor3.p) annotation(\n        Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-94, 70}}, color = {0, 0, 255}));\n      connect(pin2, inductor2.p) annotation(\n        Line(points = {{-100, 0}, {-95, 0}, {-95, 44}, {-92, 44}}, color = {0, 0, 255}));\n      connect(pin1, inductor1.p) annotation(\n        Line(points = {{-100, -60}, {-93, -60}, {-93, 20}, {-92, 20}}, color = {0, 0, 255}));\n    end lclc;\n\n    model l\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      parameter SI.Resistance R1 = 0.01;\n      parameter SI.Resistance R2 = 0.01;\n      parameter SI.Resistance R3 = 0.01;\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n        Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n        Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n        Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor1(R = R1) annotation(\n        Placement(visible = true, transformation(origin = {-32, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor2(R = R2) annotation(\n        Placement(visible = true, transformation(origin = {-32, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor3(R = R3) annotation(\n        Placement(visible = true, transformation(origin = {-32, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    equation\n      connect(resistor1.n, pin4) annotation(\n        Line(points = {{-22, 20}, {14, 20}, {14, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255}));\n      connect(resistor2.n, pin5) annotation(\n        Line(points = {{-22, 44}, {70, 44}, {70, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255}));\n      connect(resistor3.n, pin6) annotation(\n        Line(points = {{-22, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255}));\n      connect(inductor1.n, resistor1.p) annotation(\n        Line(points = {{-50, 20}, {-50, 20}, {-50, 20}, {-42, 20}}, color = {0, 0, 255}));\n      connect(inductor2.n, resistor2.p) annotation(\n        Line(points = {{-50, 44}, {-42, 44}, {-42, 44}, {-42, 44}}, color = {0, 0, 255}));\n      connect(inductor3.n, resistor3.p) annotation(\n        Line(points = {{-50, 70}, {-42, 70}, {-42, 70}, {-42, 70}}, color = {0, 0, 255}));\n      connect(pin1, inductor1.p) annotation(\n        Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n      connect(pin3, inductor3.p) annotation(\n        Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255}));\n      connect(pin2, inductor2.p) annotation(\n        Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255}));\n    end l;\n  end filter;\n\n  package loads\n    model rc\n      parameter SI.Resistance R1 = 20;\n      parameter SI.Resistance R2 = 20;\n      parameter SI.Resistance R3 = 20;\n      parameter SI.Capacitance C1 = 0.00001;\n      parameter SI.Capacitance C2 = 0.00001;\n      parameter SI.Capacitance C3 = 0.00001;\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {-66, -48}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {-32, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {48, 0}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor1(R = R1) annotation(\n        Placement(visible = true, transformation(origin = {-50, -48}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor2(R = R2) annotation(\n        Placement(visible = true, transformation(origin = {0, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor3(R = R3) annotation(\n        Placement(visible = true, transformation(origin = {72, -2}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n    equation\n      connect(resistor2.n, ground1.p) annotation(\n        Line(points = {{0, -20}, {0, -20}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n      connect(pin2, resistor2.p) annotation(\n        Line(points = {{-100, 0}, {0, 0}, {0, 0}, {0, 0}}, color = {0, 0, 255}));\n      connect(resistor1.p, pin1) annotation(\n        Line(points = {{-50, -38}, {-50, -38}, {-50, -22}, {-90, -22}, {-90, -60}, {-100, -60}, {-100, -60}}, color = {0, 0, 255}));\n      connect(resistor1.n, ground1.p) annotation(\n        Line(points = {{-50, -58}, {-50, -58}, {-50, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n      connect(resistor3.n, ground1.p) annotation(\n        Line(points = {{72, -12}, {72, -12}, {72, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n      connect(pin3, resistor3.p) annotation(\n        Line(points = {{-100, 60}, {68, 60}, {68, 60}, {72, 60}, {72, 8}, {72, 8}}, color = {0, 0, 255}));\n      connect(capacitor1.p, pin1) annotation(\n        Line(points = {{-66, -38}, {-66, -22}, {-90, -22}, {-90, -60}, {-100, -60}}, color = {0, 0, 255}));\n      connect(capacitor3.p, pin3) annotation(\n        Line(points = {{48, 10}, {48, 60}, {-100, 60}}, color = {0, 0, 255}));\n      connect(pin3, capacitor3.p) annotation(\n        Line(points = {{-100, 60}, {48, 60}, {48, 10}}, color = {0, 0, 255}));\n      connect(capacitor3.n, ground1.p) annotation(\n        Line(points = {{48, -10}, {48, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n      connect(capacitor2.p, pin2) annotation(\n        Line(points = {{-32, 0}, {-100, 0}}, color = {0, 0, 255}));\n      connect(capacitor2.n, ground1.p) annotation(\n        Line(points = {{-32, -20}, {-32, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n      connect(capacitor1.n, ground1.p) annotation(\n        Line(points = {{-66, -58}, {-66, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n    end rc;\n\n    model c\n      parameter SI.Capacitance C1 = 0.00001;\n      parameter SI.Capacitance C2 = 0.00001;\n      parameter SI.Capacitance C3 = 0.00001;\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-102, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-102, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {-34, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {4, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {40, 38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {4, -84}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    equation\n      connect(capacitor3.p, pin3) annotation(\n        Line(points = {{40, 48}, {40, 60}, {-100, 60}}, color = {0, 0, 255}));\n      connect(pin2, capacitor2.p) annotation(\n        Line(points = {{-102, 0}, {4, 0}}, color = {0, 0, 255}));\n      connect(capacitor2.n, ground1.p) annotation(\n        Line(points = {{4, -20}, {4, -74}}, color = {0, 0, 255}));\n      connect(pin1, capacitor1.p) annotation(\n        Line(points = {{-100, -60}, {-67, -60}, {-67, -34}, {-34, -34}}, color = {0, 0, 255}));\n      connect(capacitor3.n, ground1.p) annotation(\n        Line(points = {{40, 28}, {40, -54}, {4, -54}, {4, -74}}, color = {0, 0, 255}));\n      connect(capacitor1.n, ground1.p) annotation(\n        Line(points = {{-34, -54}, {4, -54}, {4, -74}, {4, -74}}, color = {0, 0, 255}));\n    end c;\n\n    model r\n      parameter SI.Resistance R1 = 20;\n      parameter SI.Resistance R2 = 20;\n      parameter SI.Resistance R3 = 20;\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor1(R = R1) annotation(\n        Placement(visible = true, transformation(origin = {-66, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor2(R = R1) annotation(\n        Placement(visible = true, transformation(origin = {-66, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      grid.components.resistor resistor3(R = R1) annotation(\n        Placement(visible = true, transformation(origin = {-66, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    equation\n      connect(resistor3.n, ground1.p) annotation(\n        Line(points = {{-56, 60}, {0, 60}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n      connect(resistor2.n, ground1.p) annotation(\n        Line(points = {{-56, 0}, {-56, 0}, {-56, 0}, {0, 0}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n      connect(resistor1.n, ground1.p) annotation(\n        Line(points = {{-56, -60}, {0, -60}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n      connect(pin1, resistor1.p) annotation(\n        Line(points = {{-100, -60}, {-76, -60}, {-76, -60}, {-76, -60}}, color = {0, 0, 255}));\n      connect(pin2, resistor2.p) annotation(\n        Line(points = {{-100, 0}, {-100, 0}, {-100, 0}, {-76, 0}}, color = {0, 0, 255}));\n      connect(pin3, resistor3.p) annotation(\n        Line(points = {{-100, 60}, {-76, 60}, {-76, 60}, {-76, 60}}, color = {0, 0, 255}));\n    end r;\n\n    model l\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-48, -50}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {0, -16}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {50, 50}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n    equation\n      connect(inductor3.n, ground1.p) annotation(\n        Line(points = {{50, 40}, {50, 40}, {50, -60}, {0, -60}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n      connect(pin3, inductor3.p) annotation(\n        Line(points = {{-100, 60}, {50, 60}, {50, 60}, {50, 60}}, color = {0, 0, 255}));\n      connect(inductor2.n, ground1.p) annotation(\n        Line(points = {{0, -26}, {0, -26}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n      connect(pin2, inductor2.p) annotation(\n        Line(points = {{-100, 0}, {0, 0}, {0, -6}}, color = {0, 0, 255}));\n      connect(inductor1.n, ground1.p) annotation(\n        Line(points = {{-48, -60}, {0, -60}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n      connect(pin1, inductor1.p) annotation(\n        Line(points = {{-100, -60}, {-78, -60}, {-78, -40}, {-48, -40}, {-48, -40}, {-48, -40}}, color = {0, 0, 255}));\n    end l;\n\n    model rlc\n      parameter SI.Resistance R1 = 20;\n      parameter SI.Resistance R2 = 20;\n      parameter SI.Resistance R3 = 20;\n      parameter SI.Capacitance C1 = 0.00001;\n      parameter SI.Capacitance C2 = 0.00001;\n      parameter SI.Capacitance C3 = 0.00001;\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {-74, -68}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {0, -30}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {74, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-74, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {0, 2}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {74, -4}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor1(R = R1) annotation(\n        Placement(visible = true, transformation(origin = {-74, -20}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor2(R = R2) annotation(\n        Placement(visible = true, transformation(origin = {0, 36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor3(R = R3) annotation(\n        Placement(visible = true, transformation(origin = {74, 42}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n    equation\n      connect(resistor1.p, pin1) annotation(\n        Line(points = {{-74, -10}, {-100, -10}, {-100, -60}}, color = {0, 0, 255}));\n      connect(pin2, resistor2.p) annotation(\n        Line(points = {{-100, 0}, {-50, 0}, {-50, 46}, {0, 46}}, color = {0, 0, 255}));\n      connect(resistor3.p, pin3) annotation(\n        Line(points = {{74, 52}, {74, 52}, {74, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255}));\n      connect(capacitor2.n, ground1.p) annotation(\n        Line(points = {{0, -40}, {0, -76}}, color = {0, 0, 255}));\n      connect(capacitor3.n, ground1.p) annotation(\n        Line(points = {{74, -56}, {74, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n      connect(capacitor1.p, inductor1.n) annotation(\n        Line(points = {{-74, -58}, {-74, -58}, {-74, -54}, {-74, -54}}, color = {0, 0, 255}));\n      connect(resistor1.n, inductor1.p) annotation(\n        Line(points = {{-74, -30}, {-74, -30}, {-74, -30}, {-74, -34}}, color = {0, 0, 255}));\n      connect(resistor2.n, inductor2.p) annotation(\n        Line(points = {{0, 26}, {0, 26}, {0, 12}, {0, 12}}, color = {0, 0, 255}));\n      connect(inductor2.n, capacitor2.p) annotation(\n        Line(points = {{0, -8}, {0, -8}, {0, -8}, {0, -20}}, color = {0, 0, 255}));\n      connect(resistor3.n, inductor3.p) annotation(\n        Line(points = {{74, 32}, {74, 32}, {74, 6}, {74, 6}}, color = {0, 0, 255}));\n      connect(capacitor3.p, inductor3.n) annotation(\n        Line(points = {{74, -36}, {74, -36}, {74, -14}, {74, -14}}, color = {0, 0, 255}));\n      connect(capacitor1.n, ground1.p) annotation(\n        Line(points = {{-74, -78}, {-46, -78}, {-46, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255}));\n    end rlc;\n\n    model lc\n      parameter SI.Capacitance C1(start = 0.00001);\n      parameter SI.Capacitance C2(start = 0.00001);\n      parameter SI.Capacitance C3(start = 0.00001);\n      parameter SI.Inductance L1(start = 0.001);\n      parameter SI.Inductance L2(start = 0.001);\n      parameter SI.Inductance L3(start = 0.001);\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {-56, -18}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {0, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {56, 42}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-56, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {0, -24}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {56, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n    equation\n      connect(capacitor3.n, inductor3.p) annotation(\n        Line(points = {{56, 32}, {56, 32}, {56, 18}, {56, 18}}, color = {0, 0, 255}));\n      connect(inductor3.n, ground1.p) annotation(\n        Line(points = {{56, -2}, {56, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n      connect(capacitor1.n, inductor1.p) annotation(\n        Line(points = {{-56, -28}, {-56, -28}, {-56, -34}, {-56, -34}}, color = {0, 0, 255}));\n      connect(inductor1.n, ground1.p) annotation(\n        Line(points = {{-56, -54}, {-56, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n      connect(capacitor1.p, pin1) annotation(\n        Line(points = {{-56, -8}, {-56, 0}, {-88, 0}, {-88, -60}, {-100, -60}}, color = {0, 0, 255}));\n      connect(capacitor2.p, pin2) annotation(\n        Line(points = {{0, 18}, {0, 24}, {-94, 24}, {-94, 0}, {-100, 0}}, color = {0, 0, 255}));\n      connect(capacitor2.n, inductor2.p) annotation(\n        Line(points = {{0, -2}, {0, -2}, {0, -14}, {0, -14}}, color = {0, 0, 255}));\n      connect(inductor2.n, ground1.p) annotation(\n        Line(points = {{0, -34}, {0, -76}}, color = {0, 0, 255}));\n      connect(pin3, capacitor3.p) annotation(\n        Line(points = {{-100, 60}, {56, 60}, {56, 52}}, color = {0, 0, 255}));\n      connect(capacitor3.p, pin3) annotation(\n        Line(points = {{56, 52}, {56, 60}, {-100, 60}}, color = {0, 0, 255}));\n    end lc;\n\n    model rl\n      parameter SI.Resistance R1 = 20;\n      parameter SI.Resistance R2 = 20;\n      parameter SI.Resistance R3 = 20;\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-40, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {0, -18}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {60, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor1(R=R1) annotation(\n        Placement(visible = true, transformation(origin = {-40, -20}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor2(R=R2) annotation(\n        Placement(visible = true, transformation(origin = {0, 12}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      grid.components.resistor resistor3(R=R3) annotation(\n        Placement(visible = true, transformation(origin = {60, 34}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n    equation\n      connect(resistor1.n, inductor1.p) annotation(\n        Line(points = {{-40, -30}, {-40, -30}, {-40, -36}, {-40, -36}}, color = {0, 0, 255}));\n      connect(pin3, resistor3.p) annotation(\n        Line(points = {{-100, 60}, {60, 60}, {60, 44}, {60, 44}}, color = {0, 0, 255}));\n      connect(resistor3.n, inductor3.p) annotation(\n        Line(points = {{60, 24}, {60, 24}, {60, 24}, {60, 18}}, color = {0, 0, 255}));\n      connect(resistor2.n, inductor2.p) annotation(\n        Line(points = {{0, 2}, {0, 2}, {0, -8}, {0, -8}}, color = {0, 0, 255}));\n      connect(pin2, resistor2.p) annotation(\n        Line(points = {{-100, 0}, {-66, 0}, {-66, 22}, {0, 22}}, color = {0, 0, 255}));\n      connect(pin1, resistor1.p) annotation(\n        Line(points = {{-100, -60}, {-74, -60}, {-74, -10}, {-40, -10}, {-40, -10}}, color = {0, 0, 255}));\n      connect(inductor3.n, ground1.p) annotation(\n        Line(points = {{60, -2}, {60, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n      connect(inductor1.n, ground1.p) annotation(\n        Line(points = {{-40, -56}, {-40, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255}));\n      connect(inductor2.n, ground1.p) annotation(\n        Line(points = {{0, -28}, {0, -76}}, color = {0, 0, 255}));\n    end rl;\n  end loads;\n\n  package inverters\n    model inverter\n      //  input Real regler1;\n      //  input Real regler2;\n      //  input Real regler3;\n      parameter Real v_DC = 1000;\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {-74, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage1 annotation(\n        Placement(visible = true, transformation(origin = {-74, -42}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage2 annotation(\n        Placement(visible = true, transformation(origin = {-74, 18}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage3 annotation(\n        Placement(visible = true, transformation(origin = {-74, 78}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Blocks.Interfaces.RealInput u1 annotation(\n        Placement(visible = true, transformation(origin = {-104, -60}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -60}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n      Modelica.Blocks.Interfaces.RealInput u3 annotation(\n        Placement(visible = true, transformation(origin = {-104, 60}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 60}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n      Modelica.Blocks.Interfaces.RealInput u2 annotation(\n        Placement(visible = true, transformation(origin = {-104, 4.44089e-16}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 4.44089e-16}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Blocks.Math.Gain gain3(k = v_DC/2) annotation(\n        Placement(visible = true, transformation(origin = {-26, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Blocks.Math.Gain gain1(k = v_DC/2) annotation(\n        Placement(visible = true, transformation(origin = {-26, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Blocks.Math.Gain gain2(k = v_DC/2) annotation(\n        Placement(visible = true, transformation(origin = {-26, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    equation\n      connect(signalVoltage2.p, pin2) annotation(\n        Line(points = {{-74, 28}, {80, 28}, {80, 0}, {100, 0}}, color = {0, 0, 255}));\n      connect(signalVoltage3.p, pin3) annotation(\n        Line(points = {{-74, 88}, {80, 88}, {80, 60}, {100, 60}}, color = {0, 0, 255}));\n      connect(signalVoltage1.p, pin1) annotation(\n        Line(points = {{-74, -32}, {80, -32}, {80, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255}));\n      connect(signalVoltage3.n, ground1.p) annotation(\n        Line(points = {{-74, 68}, {-74, 48}, {-82, 48}, {-82, -72}, {-74, -72}}, color = {0, 0, 255}));\n      connect(signalVoltage2.n, ground1.p) annotation(\n        Line(points = {{-74, 8}, {-82, 8}, {-82, -72}, {-74, -72}}, color = {0, 0, 255}));\n      connect(signalVoltage1.n, ground1.p) annotation(\n        Line(points = {{-74, -52}, {-74, -72}}, color = {0, 0, 255}));\n/*  connect(signalVoltage1.v, regler1) annotation(\n        Line);\n      connect(signalVoltage2.v, regler2) annotation(\n        Line);\n      connect(signalVoltage3.v, regler3) annotation(\n        Line);\n    */\n      connect(u1, gain1.u) annotation(\n        Line(points = {{-104, -60}, {-38, -60}, {-38, -60}, {-38, -60}}, color = {0, 0, 127}));\n      connect(gain1.y, signalVoltage1.v) annotation(\n        Line(points = {{-14, -60}, {-6, -60}, {-6, -42}, {-60, -42}, {-60, -42}, {-62, -42}}, color = {0, 0, 127}));\n      connect(u2, gain2.u) annotation(\n        Line(points = {{-104, 0}, {-38, 0}, {-38, 0}, {-38, 0}}, color = {0, 0, 127}));\n      connect(gain2.y, signalVoltage2.v) annotation(\n        Line(points = {{-14, 0}, {-6, 0}, {-6, 18}, {-62, 18}, {-62, 18}}, color = {0, 0, 127}));\n      connect(u3, gain3.u) annotation(\n        Line(points = {{-104, 60}, {-38, 60}, {-38, 60}, {-38, 60}}, color = {0, 0, 127}));\n      connect(gain3.y, signalVoltage3.v) annotation(\n        Line(points = {{-14, 60}, {-6, 60}, {-6, 78}, {-62, 78}, {-62, 78}}, color = {0, 0, 127}));\n      annotation(\n        uses(Modelica(version = \"3.2.3\")));\n    end inverter;\n  end inverters;\n\n  package ideal_filter\n    model pi\n      parameter SI.Capacitance C1 = 0.00001;\n      parameter SI.Capacitance C2 = 0.00001;\n      parameter SI.Capacitance C3 = 0.00001;\n      parameter SI.Capacitance C4 = 0.00001;\n      parameter SI.Capacitance C5 = 0.00001;\n      parameter SI.Capacitance C6 = 0.00001;\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {2, -16}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {2, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {0, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {-70, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {-48, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {-26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {0, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n        Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n        Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n        Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation(\n        Placement(visible = true, transformation(origin = {26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation(\n        Placement(visible = true, transformation(origin = {46, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation(\n        Placement(visible = true, transformation(origin = {66, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n    equation\n      connect(inductor3.n, capacitor6.p) annotation(\n        Line(points = {{10, 60}, {66, 60}, {66, -28}, {66, -28}}, color = {0, 0, 255}));\n      connect(capacitor3.p, pin3) annotation(\n        Line(points = {{-26, -28}, {-26, -28}, {-26, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255}));\n      connect(inductor1.n, pin4) annotation(\n        Line(points = {{12, -16}, {70, -16}, {70, -60}, {100, -60}}, color = {0, 0, 255}));\n      connect(inductor1.n, capacitor4.p) annotation(\n        Line(points = {{12, -16}, {26, -16}, {26, -28}}, color = {0, 0, 255}));\n      connect(pin1, inductor1.p) annotation(\n        Line(points = {{-100, -60}, {-80, -60}, {-80, -16}, {-8, -16}}, color = {0, 0, 255}));\n      connect(inductor3.n, pin6) annotation(\n        Line(points = {{10, 60}, {100, 60}}, color = {0, 0, 255}));\n      connect(pin3, inductor3.p) annotation(\n        Line(points = {{-100, 60}, {-10, 60}}, color = {0, 0, 255}));\n      connect(inductor2.n, pin5) annotation(\n        Line(points = {{12, 0}, {100, 0}}, color = {0, 0, 255}));\n      connect(inductor2.n, capacitor5.p) annotation(\n        Line(points = {{12, 0}, {46, 0}, {46, -28}}, color = {0, 0, 255}));\n      connect(inductor2.p, pin2) annotation(\n        Line(points = {{-8, 0}, {-100, 0}}, color = {0, 0, 255}));\n      connect(capacitor1.p, pin1) annotation(\n        Line(points = {{-70, -28}, {-80, -28}, {-80, -60}, {-100, -60}}, color = {0, 0, 255}));\n      connect(capacitor2.p, pin2) annotation(\n        Line(points = {{-48, -28}, {-48, -28}, {-48, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255}));\n      connect(capacitor3.n, ground1.p) annotation(\n        Line(points = {{-26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255}));\n      connect(capacitor4.n, ground1.p) annotation(\n        Line(points = {{26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255}));\n      connect(capacitor2.n, capacitor3.n) annotation(\n        Line(points = {{-48, -48}, {-26, -48}, {-26, -48}, {-26, -48}}, color = {0, 0, 255}));\n      connect(capacitor1.n, capacitor2.n) annotation(\n        Line(points = {{-70, -48}, {-48, -48}, {-48, -48}, {-48, -48}}, color = {0, 0, 255}));\n      connect(capacitor5.n, capacitor4.n) annotation(\n        Line(points = {{46, -48}, {26, -48}, {26, -48}, {26, -48}}, color = {0, 0, 255}));\n      connect(capacitor6.n, capacitor5.n) annotation(\n        Line(points = {{66, -48}, {46, -48}, {46, -48}, {46, -48}}, color = {0, 0, 255}));\n    end pi;\n\n    model lcl\n      parameter SI.Capacitance C1 = 0.00001;\n      parameter SI.Capacitance C2 = 0.00001;\n      parameter SI.Capacitance C3 = 0.00001;\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      parameter SI.Inductance L4 = 0.001;\n      parameter SI.Inductance L5 = 0.001;\n      parameter SI.Inductance L6 = 0.001;\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n        Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n        Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n        Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation(\n        Placement(visible = true, transformation(origin = {68, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation(\n        Placement(visible = true, transformation(origin = {74, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation(\n        Placement(visible = true, transformation(origin = {64, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    equation\n      connect(inductor2.n, inductor5.p) annotation(\n        Line(points = {{-50, 44}, {-50, 44}, {-50, 44}, {64, 44}}, color = {0, 0, 255}));\n      connect(inductor2.n, capacitor2.p) annotation(\n        Line(points = {{-50, 44}, {12, 44}, {12, -26}, {12, -26}}, color = {0, 0, 255}));\n      connect(inductor1.n, inductor4.p) annotation(\n        Line(points = {{-50, 20}, {-50, 20}, {-50, 20}, {58, 20}}, color = {0, 0, 255}));\n      connect(inductor1.n, capacitor1.p) annotation(\n        Line(points = {{-50, 20}, {32, 20}, {32, -26}, {32, -26}}, color = {0, 0, 255}));\n      connect(inductor3.n, capacitor3.p) annotation(\n        Line(points = {{-50, 70}, {-8, 70}, {-8, -26}, {-8, -26}}, color = {0, 0, 255}));\n      connect(inductor3.n, inductor6.p) annotation(\n        Line(points = {{-50, 70}, {54, 70}, {54, 70}, {54, 70}}, color = {0, 0, 255}));\n      connect(inductor4.n, pin4) annotation(\n        Line(points = {{78, 20}, {80, 20}, {80, -60}, {100, -60}}, color = {0, 0, 255}));\n      connect(inductor6.n, pin6) annotation(\n        Line(points = {{74, 70}, {84, 70}, {84, 60}, {100, 60}}, color = {0, 0, 255}));\n      connect(pin1, inductor1.p) annotation(\n        Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n      connect(pin3, inductor3.p) annotation(\n        Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255}));\n      connect(inductor5.n, pin5) annotation(\n        Line(points = {{84, 44}, {88, 44}, {88, 0}, {100, 0}}, color = {0, 0, 255}));\n      connect(pin2, inductor2.p) annotation(\n        Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255}));\n      connect(capacitor2.n, ground1.p) annotation(\n        Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255}));\n      connect(capacitor2.n, capacitor1.n) annotation(\n        Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255}));\n      connect(capacitor3.n, capacitor2.n) annotation(\n        Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255}));\n    end lcl;\n\n    model lc\n      parameter SI.Capacitance C1 = 0.00001;\n      parameter SI.Capacitance C2 = 0.00001;\n      parameter SI.Capacitance C3 = 0.00001;\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n        Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n        Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n        Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n    equation\n      connect(inductor1.n, pin4) annotation(\n        Line(points = {{-50, 20}, {54, 20}, {54, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255}));\n      connect(inductor2.n, pin5) annotation(\n        Line(points = {{-50, 44}, {68, 44}, {68, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255}));\n      connect(inductor3.n, pin6) annotation(\n        Line(points = {{-50, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255}));\n      connect(inductor3.n, capacitor3.p) annotation(\n        Line(points = {{-50, 70}, {-8, 70}, {-8, -26}, {-8, -26}}, color = {0, 0, 255}));\n      connect(inductor2.n, capacitor2.p) annotation(\n        Line(points = {{-50, 44}, {12, 44}, {12, -26}, {12, -26}}, color = {0, 0, 255}));\n      connect(inductor1.n, capacitor1.p) annotation(\n        Line(points = {{-50, 20}, {32, 20}, {32, -26}, {32, -26}}, color = {0, 0, 255}));\n      connect(capacitor3.n, capacitor2.n) annotation(\n        Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255}));\n      connect(capacitor2.n, ground1.p) annotation(\n        Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255}));\n      connect(capacitor2.n, capacitor1.n) annotation(\n        Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255}));\n      connect(pin1, inductor1.p) annotation(\n        Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n      connect(pin3, inductor3.p) annotation(\n        Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255}));\n      connect(pin2, inductor2.p) annotation(\n        Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255}));\n    end lc;\n\n    model lclc\n      parameter SI.Capacitance C1 = 0.00001;\n      parameter SI.Capacitance C2 = 0.00001;\n      parameter SI.Capacitance C3 = 0.00001;\n      parameter SI.Capacitance C4 = 0.00001;\n      parameter SI.Capacitance C5 = 0.00001;\n      parameter SI.Capacitance C6 = 0.00001;\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      parameter SI.Inductance L4 = 0.001;\n      parameter SI.Inductance L5 = 0.001;\n      parameter SI.Inductance L6 = 0.001;\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-82, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {-82, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {-84, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation(\n        Placement(visible = true, transformation(origin = {-2, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation(\n        Placement(visible = true, transformation(origin = {-22, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation(\n        Placement(visible = true, transformation(origin = {-42, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Ground ground1 annotation(\n        Placement(visible = true, transformation(origin = {16, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n        Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n        Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n        Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation(\n        Placement(visible = true, transformation(origin = {34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation(\n        Placement(visible = true, transformation(origin = {34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation(\n        Placement(visible = true, transformation(origin = {36, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation(\n        Placement(visible = true, transformation(origin = {72, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation(\n        Placement(visible = true, transformation(origin = {52, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n      Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation(\n        Placement(visible = true, transformation(origin = {32, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n    equation\n      connect(inductor4.n, capacitor4.p) annotation(\n        Line(points = {{44, 20}, {72, 20}, {72, -28}, {72, -28}}, color = {0, 0, 255}));\n      connect(inductor6.n, capacitor6.p) annotation(\n        Line(points = {{46, 70}, {64, 70}, {64, 4}, {32, 4}, {32, -28}}, color = {0, 0, 255}));\n      connect(capacitor3.p, inductor3.n) annotation(\n        Line(points = {{-42, -28}, {-42, -28}, {-42, 70}, {-74, 70}, {-74, 70}}, color = {0, 0, 255}));\n      connect(capacitor2.p, inductor2.n) annotation(\n        Line(points = {{-22, -28}, {-22, -28}, {-22, 44}, {-72, 44}, {-72, 44}}, color = {0, 0, 255}));\n      connect(inductor5.n, capacitor5.p) annotation(\n        Line(points = {{44, 44}, {52, 44}, {52, -28}, {52, -28}}, color = {0, 0, 255}));\n      connect(inductor3.n, inductor6.p) annotation(\n        Line(points = {{-74, 70}, {26, 70}, {26, 70}, {26, 70}}, color = {0, 0, 255}));\n      connect(inductor2.n, inductor5.p) annotation(\n        Line(points = {{-72, 44}, {-72, 44}, {-72, 44}, {24, 44}}, color = {0, 0, 255}));\n      connect(inductor1.n, inductor4.p) annotation(\n        Line(points = {{-72, 20}, {24, 20}, {24, 20}, {24, 20}}, color = {0, 0, 255}));\n      connect(inductor1.n, capacitor1.p) annotation(\n        Line(points = {{-72, 20}, {-2, 20}, {-2, -28}, {-2, -28}}, color = {0, 0, 255}));\n      connect(inductor4.n, pin4) annotation(\n        Line(points = {{44, 20}, {92, 20}, {92, -60}, {100, -60}}, color = {0, 0, 255}));\n      connect(inductor6.n, pin6) annotation(\n        Line(points = {{46, 70}, {76, 70}, {76, 60}, {100, 60}}, color = {0, 0, 255}));\n      connect(capacitor6.n, ground1.p) annotation(\n        Line(points = {{32, -48}, {16, -48}, {16, -60}}, color = {0, 0, 255}));\n      connect(capacitor6.n, capacitor5.n) annotation(\n        Line(points = {{32, -48}, {52, -48}, {52, -48}, {52, -48}}, color = {0, 0, 255}));\n      connect(capacitor5.n, capacitor4.n) annotation(\n        Line(points = {{52, -48}, {72, -48}, {72, -48}, {72, -48}}, color = {0, 0, 255}));\n      connect(capacitor1.n, ground1.p) annotation(\n        Line(points = {{-2, -48}, {16, -48}, {16, -60}, {16, -60}}, color = {0, 0, 255}));\n      connect(inductor5.n, pin5) annotation(\n        Line(points = {{44, 44}, {94, 44}, {94, 0}, {100, 0}}, color = {0, 0, 255}));\n      connect(capacitor3.n, capacitor2.n) annotation(\n        Line(points = {{-42, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255}));\n      connect(capacitor1.n, capacitor2.n) annotation(\n        Line(points = {{-2, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255}));\n      connect(pin3, inductor3.p) annotation(\n        Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-94, 70}}, color = {0, 0, 255}));\n      connect(pin2, inductor2.p) annotation(\n        Line(points = {{-100, 0}, {-95, 0}, {-95, 44}, {-92, 44}}, color = {0, 0, 255}));\n      connect(pin1, inductor1.p) annotation(\n        Line(points = {{-100, -60}, {-93, -60}, {-93, 20}, {-92, 20}}, color = {0, 0, 255}));\n    end lclc;\n\n    model l\n      parameter SI.Inductance L1 = 0.001;\n      parameter SI.Inductance L2 = 0.001;\n      parameter SI.Inductance L3 = 0.001;\n      Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation(\n        Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation(\n        Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation(\n        Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation(\n        Placement(visible = true, transformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation(\n        Placement(visible = true, transformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation(\n        Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation(\n        Placement(visible = true, transformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation(\n        Placement(visible = true, transformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation(\n        Placement(visible = true, transformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    equation\n      connect(inductor3.n, pin6) annotation(\n        Line(points = {{-50, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255}));\n      connect(inductor2.n, pin5) annotation(\n        Line(points = {{-50, 44}, {78, 44}, {78, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255}));\n      connect(inductor1.n, pin4) annotation(\n        Line(points = {{-50, 20}, {60, 20}, {60, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255}));\n      connect(pin1, inductor1.p) annotation(\n        Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255}));\n      connect(pin3, inductor3.p) annotation(\n        Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255}));\n      connect(pin2, inductor2.p) annotation(\n        Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255}));\n    end l;\n  end ideal_filter;\n\n  package components\n    model resistor\n      parameter SI.Resistance R(start = 1);\n      extends Modelica.Electrical.Analog.Interfaces.OnePort;\n    equation\n      v = R * i;\n      annotation(\n        Documentation(info = \"<html>\n<p>The linear resistor connects the branch voltage <em>v</em> with the branch current <em>i</em> by <em>i*R = v</em>. The Resistance <em>R</em> is allowed to be positive, zero, or negative.</p>\n</html>\", revisions = \"<html>\n<ul>\n<li><em> August 07, 2009   </em>\n       by Anton Haumer<br> temperature dependency of resistance added<br>\n       </li>\n<li><em> March 11, 2009   </em>\n       by Christoph Clauss<br> conditional heat port added<br>\n       </li>\n<li><em> 1998   </em>\n       by Christoph Clauss<br> initially implemented<br>\n       </li>\n</ul>\n</html>\"),\n        Icon(coordinateSystem(preserveAspectRatio = true, extent = {{-100, -100}, {100, 100}}), graphics = {Rectangle(extent = {{-70, 30}, {70, -30}}, lineColor = {0, 0, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Line(points = {{-90, 0}, {-70, 0}}, color = {0, 0, 255}), Line(points = {{70, 0}, {90, 0}}, color = {0, 0, 255}), Text(extent = {{-150, -40}, {150, -80}}, textString = \"R=%R\"), Line(visible = useHeatPort, points = {{0, -100}, {0, -30}}, color = {127, 0, 0}, pattern = LinePattern.Dot), Text(extent = {{-150, 90}, {150, 50}}, textString = \"%name\", lineColor = {0, 0, 255})}));\n    end resistor;\n  end components;\n\n  package plls\n    model pll\n      Modelica.Electrical.Analog.Interfaces.Pin a annotation(\n        Placement(visible = true, transformation(origin = {-100, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin b annotation(\n        Placement(visible = true, transformation(origin = {-100, 16}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 16}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Interfaces.Pin c annotation(\n        Placement(visible = true, transformation(origin = {-100, -14}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, -14}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Electrical.Analog.Basic.Ground ground annotation(\n        Placement(visible = true, transformation(origin = {-86, 62}, extent = {{-6, -6}, {6, 6}}, rotation = 180)));\n      Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_c annotation(\n        Placement(visible = true, transformation(origin = {-88, -8}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n      Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_a annotation(\n        Placement(visible = true, transformation(origin = {-86, 50}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n      Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_b annotation(\n        Placement(visible = true, transformation(origin = {-88, 22}, extent = {{-6, -6}, {6, 6}}, rotation = 90)));\n      grid.transforms.abc2AlphaBeta abc2AlphaBeta annotation(\n        Placement(visible = true, transformation(origin = {-62, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Blocks.Math.Sin sin annotation(\n        Placement(visible = true, transformation(origin = {-10, -6}, extent = {{-4, -4}, {4, 4}}, rotation = 180)));\n      Modelica.Blocks.Math.Cos cos annotation(\n        Placement(visible = true, transformation(origin = {-10, -18}, extent = {{-4, -4}, {4, 4}}, rotation = 180)));\n      Modelica.Blocks.Math.Gain Norm_U_ref_alpha(k = 1 / (230 * 1.414)) annotation(\n        Placement(visible = true, transformation(origin = {-33, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n      Modelica.Blocks.Math.Gain Norm_U_ref_beta(k = 1 / (230 * 1.414)) annotation(\n        Placement(visible = true, transformation(origin = {-33, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n      Modelica.Blocks.Math.Product alphaSin annotation(\n        Placement(visible = true, transformation(origin = {-7, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n      Modelica.Blocks.Math.Product betaCos annotation(\n        Placement(visible = true, transformation(origin = {-9, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0)));\n      Modelica.Blocks.Math.Add add(k1 = -1, k2 = +1) annotation(\n        Placement(visible = true, transformation(origin = {12, 24}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n      Modelica.Blocks.Continuous.PI pi(T = 0.2, k = 15) annotation(\n        Placement(visible = true, transformation(origin = {26, 24}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n      Modelica.Blocks.Math.Add add_freq_nom_delta_f(k1 = +1, k2 = +1) annotation(\n        Placement(visible = true, transformation(origin = {48, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n      Modelica.Blocks.Sources.Constant f_nom(k = 50) annotation(\n        Placement(visible = true, transformation(origin = {28, 4}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n      Modelica.Blocks.Continuous.Integrator f2theta(y_start = 0) annotation(\n        Placement(visible = true, transformation(origin = {64, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n      Modelica.Blocks.Math.Gain deg2rad(k = 2 * 3.1416) annotation(\n        Placement(visible = true, transformation(origin = {78, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0)));\n    equation\n      connect(a, voltageSensor_a.p) annotation(\n        Line(points = {{-100, 44}, {-86, 44}}, color = {0, 0, 255}));\n      connect(b, voltageSensor_b.p) annotation(\n        Line(points = {{-100, 16}, {-88, 16}}, color = {0, 0, 255}));\n      connect(c, voltageSensor_c.p) annotation(\n        Line(points = {{-100, -14}, {-88, -14}}, color = {0, 0, 255}));\n      connect(voltageSensor_a.n, ground.p) annotation(\n        Line(points = {{-86, 56}, {-86, 56}}, color = {0, 0, 255}));\n      connect(voltageSensor_b.n, ground.p) annotation(\n        Line(points = {{-88, 28}, {-88, 42}, {-86, 42}, {-86, 56}}, color = {0, 0, 255}));\n      connect(voltageSensor_c.n, ground.p) annotation(\n        Line(points = {{-88, -2}, {-88, 27}, {-86, 27}, {-86, 56}}, color = {0, 0, 255}));\n      connect(abc2AlphaBeta.b, voltageSensor_b.v) annotation(\n        Line(points = {{-72, 21}, {-74, 21}, {-74, 22}, {-82, 22}}, color = {0, 0, 127}));\n      connect(abc2AlphaBeta.a, voltageSensor_a.v) annotation(\n        Line(points = {{-72, 24}, {-76, 24}, {-76, 50}, {-80, 50}}, color = {0, 0, 127}));\n      connect(abc2AlphaBeta.c, voltageSensor_c.v) annotation(\n        Line(points = {{-72, 18}, {-76, 18}, {-76, -8}, {-82, -8}}, color = {0, 0, 127}));\n      connect(Norm_U_ref_alpha.u, abc2AlphaBeta.alpha) annotation(\n        Line(points = {{-37, 29}, {-40, 29}, {-40, 26}, {-52, 26}}, color = {0, 0, 127}));\n      connect(Norm_U_ref_beta.u, abc2AlphaBeta.beta) annotation(\n        Line(points = {{-37, 15}, {-42, 15}, {-42, 17}, {-52, 17}}, color = {0, 0, 127}));\n      connect(Norm_U_ref_alpha.y, alphaSin.u1) annotation(\n        Line(points = {{-30, 30}, {-11, 30}, {-11, 31}}, color = {0, 0, 127}));\n      connect(Norm_U_ref_beta.y, betaCos.u1) annotation(\n        Line(points = {{-30, 16}, {-13, 16}, {-13, 17}}, color = {0, 0, 127}));\n      connect(sin.y, alphaSin.u2) annotation(\n        Line(points = {{-14, -6}, {-22, -6}, {-22, 27}, {-11, 27}}, color = {0, 0, 127}));\n      connect(cos.y, betaCos.u2) annotation(\n        Line(points = {{-14, -18}, {-18, -18}, {-18, 13}, {-13, 13}}, color = {0, 0, 127}));\n      connect(add.u1, alphaSin.y) annotation(\n        Line(points = {{7, 26}, {3.5, 26}, {3.5, 30}, {-4, 30}}, color = {0, 0, 127}));\n      connect(betaCos.y, add.u2) annotation(\n        Line(points = {{-6, 16}, {4, 16}, {4, 22}, {7, 22}}, color = {0, 0, 127}));\n      connect(pi.u, add.y) annotation(\n        Line(points = {{19, 24}, {16, 24}}, color = {0, 0, 127}));\n      connect(add_freq_nom_delta_f.u1, pi.y) annotation(\n        Line(points = {{43, 24}, {33, 24}}, color = {0, 0, 127}));\n      connect(f_nom.y, add_freq_nom_delta_f.u2) annotation(\n        Line(points = {{32, 4}, {36, 4}, {36, 20}, {43, 20}}, color = {0, 0, 127}));\n      connect(f2theta.u, add_freq_nom_delta_f.y) annotation(\n        Line(points = {{59, 22}, {52, 22}}, color = {0, 0, 127}));\n      connect(deg2rad.u, f2theta.y) annotation(\n        Line(points = {{74, 22}, {68, 22}, {68, 22}, {68, 22}}, color = {0, 0, 127}));\n      connect(deg2rad.y, sin.u) annotation(\n        Line(points = {{82, 22}, {92, 22}, {92, -6}, {-6, -6}, {-6, -6}}, color = {0, 0, 127}));\n      connect(cos.u, deg2rad.y) annotation(\n        Line(points = {{-6, -18}, {4, -18}, {4, -6}, {92, -6}, {92, 22}, {82, 22}, {82, 22}, {82, 22}}, color = {0, 0, 127}));\n    end pll;\n  end plls;\n\n  package transforms\n    model abc2AlphaBeta\n      Modelica.Blocks.Interfaces.RealInput a annotation(\n        Placement(visible = true, transformation(origin = {-104, 40}, extent = {{-12, -12}, {12, 12}}, rotation = 0), iconTransformation(origin = {-104, 40}, extent = {{-12, -12}, {12, 12}}, rotation = 0)));\n      Modelica.Blocks.Interfaces.RealInput b annotation(\n        Placement(visible = true, transformation(origin = {-104, 12}, extent = {{-12, -12}, {12, 12}}, rotation = 0), iconTransformation(origin = {-104, 12}, extent = {{-12, -12}, {12, 12}}, rotation = 0)));\n      Modelica.Blocks.Interfaces.RealInput c annotation(\n        Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-12, -12}, {12, 12}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-12, -12}, {12, 12}}, rotation = 0)));\n      Modelica.Blocks.Math.Gain gain(k = 2 / 3) annotation(\n        Placement(visible = true, transformation(origin = {-40, 78}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));\n      Modelica.Blocks.Math.Gain gain1(k = -1 / 3) annotation(\n        Placement(visible = true, transformation(origin = {-39, 55}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n      Modelica.Blocks.Math.Gain gain2(k = -1 / 3) annotation(\n        Placement(visible = true, transformation(origin = {-39, 29}, extent = {{-7, -7}, {7, 7}}, rotation = 0)));\n      Modelica.Blocks.Math.MultiSum multiSum(nu = 3) annotation(\n        Placement(visible = true, transformation(origin = {58, 66}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Blocks.Math.Gain gain3(k = -1 / sqrt(3)) annotation(\n        Placement(visible = true, transformation(origin = {-32, -18}, extent = {{-14, -14}, {14, 14}}, rotation = 0)));\n      Modelica.Blocks.Math.Gain gain4(k = 1 / sqrt(3)) annotation(\n        Placement(visible = true, transformation(origin = {-32, -64}, extent = {{-14, -14}, {14, 14}}, rotation = 0)));\n      Modelica.Blocks.Math.MultiSum multiSum1(nu = 2) annotation(\n        Placement(visible = true, transformation(origin = {48, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Blocks.Interfaces.RealOutput alpha annotation(\n        Placement(visible = true, transformation(origin = {102, 64}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {102, 64}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n      Modelica.Blocks.Interfaces.RealOutput beta annotation(\n        Placement(visible = true, transformation(origin = {102, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {102, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    equation\n      connect(a, gain.u) annotation(\n        Line(points = {{-104, 40}, {-77, 40}, {-77, 78}, {-48, 78}}, color = {0, 0, 127}));\n      connect(gain1.u, b) annotation(\n        Line(points = {{-48, 56}, {-71, 56}, {-71, 12}, {-104, 12}}, color = {0, 0, 127}));\n      connect(c, gain2.u) annotation(\n        Line(points = {{-104, -18}, {-62, -18}, {-62, 30}, {-48, 30}}, color = {0, 0, 127}));\n      connect(gain.y, multiSum.u[1]) annotation(\n        Line(points = {{-34, 78}, {48, 78}, {48, 66}}, color = {0, 0, 127}));\n      connect(gain1.y, multiSum.u[2]) annotation(\n        Line(points = {{-32, 56}, {48, 56}, {48, 66}, {48, 66}}, color = {0, 0, 127}));\n      connect(gain2.y, multiSum.u[3]) annotation(\n        Line(points = {{-32, 30}, {48, 30}, {48, 66}, {48, 66}}, color = {0, 0, 127}));\n      connect(gain4.u, b) annotation(\n        Line(points = {{-48, -64}, {-73, -64}, {-73, -62}, {-72, -62}, {-72, 12}, {-104, 12}}, color = {0, 0, 127}));\n      connect(gain3.u, c) annotation(\n        Line(points = {{-48, -18}, {-96, -18}, {-96, -18}, {-104, -18}}, color = {0, 0, 127}));\n      connect(gain3.y, multiSum1.u[1]) annotation(\n        Line(points = {{-16, -18}, {38, -18}, {38, -34}, {38, -34}}, color = {0, 0, 127}));\n      connect(gain4.y, multiSum1.u[2]) annotation(\n        Line(points = {{-16, -64}, {38, -64}, {38, -34}, {38, -34}}, color = {0, 0, 127}));\n      connect(multiSum.y, alpha) annotation(\n        Line(points = {{70, 66}, {96, 66}, {96, 64}, {102, 64}}, color = {0, 0, 127}));\n      connect(multiSum1.y, beta) annotation(\n        Line(points = {{60, -34}, {102, -34}}, color = {0, 0, 127}));\n    end abc2AlphaBeta;\n  end transforms;\n\n  model network\n    grid.inverters.inverter inverter1 annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.ideal_filter.lc lc1 annotation(\n      Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.inverters.inverter inverter2 annotation(\n      Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    ideal_filter.lcl lcl1 annotation(\n      Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.loads.rl rl1 annotation(\n      Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  ideal_filter.lc lc2 annotation(\n      Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n    connect(inverter1.pin3, lc1.pin3) annotation(\n      Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255}));\n    connect(inverter1.pin2, lc1.pin2) annotation(\n      Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255}));\n    connect(inverter1.pin1, lc1.pin1) annotation(\n      Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255}));\n    connect(i1p1, inverter1.u1) annotation(\n      Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n    connect(i1p2, inverter1.u2) annotation(\n      Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n    connect(i1p3, inverter1.u3) annotation(\n      Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n    connect(i2p3, inverter2.u3) annotation(\n      Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127}));\n    connect(i2p2, inverter2.u2) annotation(\n      Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127}));\n    connect(i2p1, inverter2.u1) annotation(\n      Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127}));\n    connect(inverter2.pin3, lcl1.pin3) annotation(\n      Line(points = {{-60, -24}, {-42, -24}, {-42, -24}, {-42, -24}}, color = {0, 0, 255}));\n    connect(inverter2.pin2, lcl1.pin2) annotation(\n      Line(points = {{-60, -30}, {-42, -30}, {-42, -30}, {-42, -30}}, color = {0, 0, 255}));\n    connect(inverter2.pin1, lcl1.pin1) annotation(\n      Line(points = {{-60, -36}, {-42, -36}, {-42, -36}, {-42, -36}}, color = {0, 0, 255}));\n    connect(lc2.pin4, rl1.pin1) annotation(\n      Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255}));\n    connect(lc2.pin5, rl1.pin2) annotation(\n      Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255}));\n    connect(lc2.pin6, rl1.pin3) annotation(\n      Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255}));\n  connect(lc1.pin6, lc2.pin3) annotation(\n      Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255}));\n  connect(lc1.pin5, lc2.pin2) annotation(\n      Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255}));\n  connect(lc1.pin4, lc2.pin1) annotation(\n      Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255}));\n  connect(lcl1.pin4, lc2.pin1) annotation(\n      Line(points = {{-22, -36}, {6, -36}, {6, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255}));\n  connect(lcl1.pin5, lc2.pin2) annotation(\n      Line(points = {{-22, -30}, {0, -30}, {0, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255}));\n  connect(lcl1.pin6, lc2.pin3) annotation(\n      Line(points = {{-22, -24}, {-6, -24}, {-6, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255}));\n    annotation(\n      Diagram);\n  end network;\n\n  model pll_network\n    grid.inverters.inverter inverter1 annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.ideal_filter.lc lc1 annotation(\n      Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.loads.rc rc1 annotation(\n      Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.inverters.inverter inverter2 annotation(\n      Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.ideal_filter.lc lc2 annotation(\n      Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    ideal_filter.lcl lcl1 annotation(\n      Placement(visible = true, transformation(origin = {-30, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.plls.pll pll annotation(\n      Placement(visible = true, transformation(origin = {20, -62}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n    connect(lc2.pin4, rc1.pin1) annotation(\n      Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255}));\n    connect(lc2.pin5, rc1.pin2) annotation(\n      Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255}));\n    connect(lc2.pin6, rc1.pin3) annotation(\n      Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255}));\n    connect(lc1.pin6, lc2.pin3) annotation(\n      Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255}));\n    connect(lc1.pin5, lc2.pin2) annotation(\n      Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255}));\n    connect(lc1.pin4, lc2.pin1) annotation(\n      Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255}));\n    connect(inverter1.pin3, lc1.pin3) annotation(\n      Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255}));\n    connect(inverter1.pin2, lc1.pin2) annotation(\n      Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255}));\n    connect(inverter1.pin1, lc1.pin1) annotation(\n      Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255}));\n    connect(i1p1, inverter1.u1) annotation(\n      Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n    connect(i1p2, inverter1.u2) annotation(\n      Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n    connect(i1p3, inverter1.u3) annotation(\n      Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n    connect(i2p3, inverter2.u3) annotation(\n      Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127}));\n    connect(i2p2, inverter2.u2) annotation(\n      Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127}));\n    connect(i2p1, inverter2.u1) annotation(\n      Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127}));\n    connect(inverter2.pin3, lcl1.pin3) annotation(\n      Line(points = {{-60, -24}, {-40, -24}, {-40, -24}, {-40, -24}}, color = {0, 0, 255}));\n    connect(inverter2.pin2, lcl1.pin2) annotation(\n      Line(points = {{-60, -30}, {-40, -30}, {-40, -30}, {-40, -30}}, color = {0, 0, 255}));\n    connect(inverter2.pin1, lcl1.pin1) annotation(\n      Line(points = {{-60, -36}, {-40, -36}, {-40, -36}, {-40, -36}}, color = {0, 0, 255}));\n    connect(lcl1.pin6, lc2.pin3) annotation(\n      Line(points = {{-20, -24}, {-4, -24}, {-4, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255}));\n    connect(lcl1.pin5, lc2.pin2) annotation(\n      Line(points = {{-20, -30}, {0, -30}, {0, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255}));\n    connect(lcl1.pin4, lc2.pin1) annotation(\n      Line(points = {{-20, -36}, {6, -36}, {6, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255}));\n    connect(pll.a, lcl1.pin6) annotation(\n      Line(points = {{10, -58}, {-14, -58}, {-14, -24}, {-20, -24}}, color = {0, 0, 255}));\n    connect(pll.b, lcl1.pin5) annotation(\n      Line(points = {{10, -60}, {-16, -60}, {-16, -30}, {-20, -30}}, color = {0, 0, 255}));\n    connect(pll.c, lcl1.pin4) annotation(\n      Line(points = {{10, -63}, {-18, -63}, {-18, -36}, {-20, -36}}, color = {0, 0, 255}));\n    annotation(\n      Diagram);\n  end pll_network;\n\n  model rlc_network\n    grid.inverters.inverter inverter1 annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.ideal_filter.lc lc1 annotation(\n      Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.inverters.inverter inverter2 annotation(\n      Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.ideal_filter.lc lc2 annotation(\n      Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    ideal_filter.lcl lcl1 annotation(\n      Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.loads.rlc rlc1 annotation(\n      Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n    connect(lc1.pin6, lc2.pin3) annotation(\n      Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255}));\n    connect(lc1.pin5, lc2.pin2) annotation(\n      Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255}));\n    connect(lc1.pin4, lc2.pin1) annotation(\n      Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255}));\n    connect(inverter1.pin3, lc1.pin3) annotation(\n      Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255}));\n    connect(inverter1.pin2, lc1.pin2) annotation(\n      Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255}));\n    connect(inverter1.pin1, lc1.pin1) annotation(\n      Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255}));\n    connect(i1p1, inverter1.u1) annotation(\n      Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n    connect(i1p2, inverter1.u2) annotation(\n      Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n    connect(i1p3, inverter1.u3) annotation(\n      Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n    connect(i2p3, inverter2.u3) annotation(\n      Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127}));\n    connect(i2p2, inverter2.u2) annotation(\n      Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127}));\n    connect(i2p1, inverter2.u1) annotation(\n      Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127}));\n    connect(inverter2.pin3, lcl1.pin3) annotation(\n      Line(points = {{-60, -24}, {-42, -24}, {-42, -24}, {-42, -24}}, color = {0, 0, 255}));\n    connect(inverter2.pin2, lcl1.pin2) annotation(\n      Line(points = {{-60, -30}, {-42, -30}, {-42, -30}, {-42, -30}}, color = {0, 0, 255}));\n    connect(inverter2.pin1, lcl1.pin1) annotation(\n      Line(points = {{-60, -36}, {-42, -36}, {-42, -36}, {-42, -36}}, color = {0, 0, 255}));\n    connect(lcl1.pin6, lc2.pin3) annotation(\n      Line(points = {{-22, -24}, {-6, -24}, {-6, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255}));\n    connect(lcl1.pin5, lc2.pin2) annotation(\n      Line(points = {{-22, -30}, {0, -30}, {0, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255}));\n    connect(lcl1.pin4, lc2.pin1) annotation(\n      Line(points = {{-22, -36}, {6, -36}, {6, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255}));\n    connect(lc2.pin4, rlc1.pin1) annotation(\n      Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255}));\n    connect(lc2.pin5, rlc1.pin2) annotation(\n      Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255}));\n    connect(lc2.pin6, rlc1.pin3) annotation(\n      Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255}));\n    annotation(\n      Diagram);\n  end rlc_network;\n\n  model pll_Test\n    transforms.abc2AlphaBeta abc2AlphaBeta annotation(\n      Placement(visible = true, transformation(origin = {-14, 4}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Sources.Sine sine(amplitude = 230 * 1.414, freqHz = 50) annotation(\n      Placement(visible = true, transformation(origin = {-90, 34}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Sources.Sine sine1(amplitude = 230 * 1.414, freqHz = 50, phase = -2.0944) annotation(\n      Placement(visible = true, transformation(origin = {-88, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Sources.Sine sine2(amplitude = 230 * 1.414, freqHz = 50, phase = -4.18879) annotation(\n      Placement(visible = true, transformation(origin = {-88, -26}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.inverters.inverter inverter annotation(\n      Placement(visible = true, transformation(origin = {-14, 58}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.plls.pll pll annotation(\n      Placement(visible = true, transformation(origin = {28, 56}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n    connect(abc2AlphaBeta.a, sine.y) annotation(\n      Line(points = {{-24, 8}, {-44, 8}, {-44, 34}, {-78, 34}, {-78, 34}}, color = {0, 0, 127}));\n    connect(abc2AlphaBeta.b, sine1.y) annotation(\n      Line(points = {{-24, 6}, {-77, 6}}, color = {0, 0, 127}));\n    connect(sine2.y, abc2AlphaBeta.c) annotation(\n      Line(points = {{-76, -26}, {-44, -26}, {-44, 2}, {-24, 2}, {-24, 2}}, color = {0, 0, 127}));\n    connect(inverter.u3, sine.y) annotation(\n      Line(points = {{-24, 64}, {-70, 64}, {-70, 34}, {-78, 34}}, color = {0, 0, 127}));\n    connect(inverter.u2, sine1.y) annotation(\n      Line(points = {{-24, 58}, {-60, 58}, {-60, 6}, {-76, 6}}, color = {0, 0, 127}));\n    connect(inverter.u1, sine2.y) annotation(\n      Line(points = {{-24, 52}, {-54, 52}, {-54, -26}, {-76, -26}}, color = {0, 0, 127}));\n    connect(pll.a, inverter.pin3) annotation(\n      Line(points = {{18, 60}, {4, 60}, {4, 64}, {-4, 64}, {-4, 64}}, color = {0, 0, 255}));\n    connect(pll.b, inverter.pin2) annotation(\n      Line(points = {{18, 58}, {-4, 58}, {-4, 58}, {-4, 58}}, color = {0, 0, 255}));\n    connect(pll.c, inverter.pin1) annotation(\n      Line(points = {{18, 54}, {4, 54}, {4, 52}, {-4, 52}, {-4, 52}}, color = {0, 0, 255}));\n  end pll_Test;\n\n  model sine_Test\n  \n    Modelica.Blocks.Sources.Sine sine(amplitude = 230, freqHz = 50) annotation(\n      Placement(visible = true, transformation(origin = {-76, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Sources.Sine sine1(amplitude = 230, freqHz = 50, phase = 2.0944) annotation(\n      Placement(visible = true, transformation(origin = {-76, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Sources.Sine sine2(amplitude = 230, freqHz = 50, phase = 4.18879) annotation(\n      Placement(visible = true, transformation(origin = {-76, -16}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.loads.rl rl(L1 = 0.002, L2 = 0.002, L3 = 0.002, R1 = 1, R2 = 1, R3 = 1) annotation(\n      Placement(visible = true, transformation(origin = {64, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.filter.lc lc(C1 = 0.0000136, C2 = 0.0000136, C3 = 0.0000136, L1 = 0.0023, L2 = 0.0023, L3 = 0.0023, R1 = 0.4, R2 = 0.4, R3 = 0.4, R4 = 0, R5 = 0, R6 = 0) annotation(\n      Placement(visible = true, transformation(origin = {14, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.inverters.inverter inverter1(v_DC = 60) annotation(\n      Placement(visible = true, transformation(origin = {-24, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n  connect(lc.pin5, rl.pin2) annotation(\n      Line(points = {{24, -48}, {54, -48}, {54, -48}, {54, -48}}, color = {0, 0, 255}));\n  connect(lc.pin4, rl.pin1) annotation(\n      Line(points = {{24, -54}, {54, -54}, {54, -54}, {54, -54}}, color = {0, 0, 255}));\n  connect(lc.pin3, inverter1.pin3) annotation(\n      Line(points = {{4, -42}, {-14, -42}, {-14, -42}, {-14, -42}}, color = {0, 0, 255}));\n  connect(lc.pin6, rl.pin3) annotation(\n      Line(points = {{24, -42}, {52, -42}, {52, -42}, {54, -42}}, color = {0, 0, 255}));\n  connect(lc.pin2, inverter1.pin2) annotation(\n      Line(points = {{4, -48}, {-14, -48}, {-14, -48}, {-14, -48}}, color = {0, 0, 255}));\n  connect(lc.pin1, inverter1.pin1) annotation(\n      Line(points = {{4, -54}, {-14, -54}, {-14, -54}, {-14, -54}}, color = {0, 0, 255}));\n  connect(inverter1.u2, sine1.y) annotation(\n      Line(points = {{-34, -48}, {-64, -48}, {-64, -48}, {-64, -48}}, color = {0, 0, 127}));\n  connect(inverter1.u3, sine2.y) annotation(\n      Line(points = {{-34, -42}, {-48, -42}, {-48, -16}, {-64, -16}, {-64, -16}}, color = {0, 0, 127}));\n  connect(inverter1.u1, sine.y) annotation(\n      Line(points = {{-34, -54}, {-48, -54}, {-48, -82}, {-64, -82}, {-64, -82}}, color = {0, 0, 127}));\n  end sine_Test;\n  \n  model network_singleInverter\n    grid.inverters.inverter inverter1 annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.ideal_filter.lc lc1(C1 = 0.00002, C2 = 0.00002, C3 = 0.00002, L1 = 0.002, L2 = 0.002, L3 = 0.002)  annotation(\n      Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  grid.loads.rl rl1 annotation(\n      Placement(visible = true, transformation(origin = {24, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n  connect(inverter1.pin3, lc1.pin3) annotation(\n      Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255}));\n  connect(inverter1.pin2, lc1.pin2) annotation(\n      Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255}));\n  connect(inverter1.pin1, lc1.pin1) annotation(\n      Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255}));\n    connect(i1p1, inverter1.u1) annotation(\n      Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n    connect(i1p2, inverter1.u2) annotation(\n      Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n    connect(i1p3, inverter1.u3) annotation(\n      Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n  connect(lc1.pin6, rl1.pin3) annotation(\n      Line(points = {{-20, 36}, {14, 36}}, color = {0, 0, 255}));\n  connect(lc1.pin5, rl1.pin2) annotation(\n      Line(points = {{-20, 30}, {14, 30}}, color = {0, 0, 255}));\n  connect(lc1.pin4, rl1.pin1) annotation(\n      Line(points = {{-20, 24}, {14, 24}}, color = {0, 0, 255}));\n    annotation(\n      Diagram);\n  end network_singleInverter;\n  \n  model singleModel\n    grid.inverters.inverter inverter1 annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.inverters.inverter inverter2 annotation(\n      Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  grid.loads.r r(R1 = 100, R2 = 100, R3 = 100)  annotation(\n      Placement(visible = true, transformation(origin = {8, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.ideal_filter.l l annotation(\n      Placement(visible = true, transformation(origin = {-40, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n    connect(i1p1, inverter1.u1) annotation(\n      Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n    connect(i1p2, inverter1.u2) annotation(\n      Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n    connect(i1p3, inverter1.u3) annotation(\n      Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n    connect(i2p3, inverter2.u3) annotation(\n      Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127}));\n    connect(i2p2, inverter2.u2) annotation(\n      Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127}));\n    connect(i2p1, inverter2.u1) annotation(\n      Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127}));\n    connect(inverter1.pin3, r.pin3) annotation(\n      Line(points = {{-60, 36}, {-2, 36}, {-2, 36}, {-2, 36}}, color = {0, 0, 255}));\n    connect(inverter1.pin2, r.pin2) annotation(\n      Line(points = {{-60, 30}, {-2, 30}, {-2, 30}, {-2, 30}}, color = {0, 0, 255}));\n    connect(inverter1.pin1, r.pin1) annotation(\n      Line(points = {{-60, 24}, {-2, 24}, {-2, 24}, {-2, 24}}, color = {0, 0, 255}));\n  connect(inverter2.pin3, l.pin3) annotation(\n      Line(points = {{-60, -24}, {-50, -24}}, color = {0, 0, 255}));\n  connect(inverter2.pin2, l.pin2) annotation(\n      Line(points = {{-60, -30}, {-50, -30}}, color = {0, 0, 255}));\n  connect(inverter2.pin1, l.pin1) annotation(\n      Line(points = {{-60, -36}, {-50, -36}}, color = {0, 0, 255}));\n  connect(l.pin6, r.pin3) annotation(\n      Line(points = {{-30, -24}, {-20, -24}, {-20, 36}, {-2, 36}, {-2, 36}, {-2, 36}}, color = {0, 0, 255}));\n  connect(l.pin5, r.pin2) annotation(\n      Line(points = {{-30, -30}, {-14, -30}, {-14, 30}, {-2, 30}, {-2, 30}}, color = {0, 0, 255}));\n  connect(l.pin4, r.pin1) annotation(\n      Line(points = {{-30, -36}, {-8, -36}, {-8, 24}, {-2, 24}, {-2, 24}, {-2, 24}}, color = {0, 0, 255}));\n    annotation(\n      Diagram);\n  end singleModel;\n  \n  model microgrid\n    grid.inverters.inverter inverter1 annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.ideal_filter.lc lc1 annotation(\n      Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    grid.inverters.inverter inverter2 annotation(\n      Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i2p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    ideal_filter.lcl lcl1 annotation(\n      Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.loads.rl rl1 annotation(\n      Placement(visible = true, transformation(origin = {92, 2}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.ideal_filter.l l12 annotation(\n      Placement(visible = true, transformation(origin = {2, 0}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));\n  grid.ideal_filter.l l13 annotation(\n      Placement(visible = true, transformation(origin = {46, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.ideal_filter.l l23 annotation(\n      Placement(visible = true, transformation(origin = {48, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n    connect(inverter1.pin3, lc1.pin3) annotation(\n      Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255}));\n    connect(inverter1.pin2, lc1.pin2) annotation(\n      Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255}));\n    connect(inverter1.pin1, lc1.pin1) annotation(\n      Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255}));\n    connect(i1p1, inverter1.u1) annotation(\n      Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n    connect(i1p2, inverter1.u2) annotation(\n      Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n    connect(i1p3, inverter1.u3) annotation(\n      Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n    connect(i2p3, inverter2.u3) annotation(\n      Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127}));\n    connect(i2p2, inverter2.u2) annotation(\n      Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127}));\n    connect(i2p1, inverter2.u1) annotation(\n      Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127}));\n    connect(inverter2.pin3, lcl1.pin3) annotation(\n      Line(points = {{-60, -24}, {-42, -24}, {-42, -24}, {-42, -24}}, color = {0, 0, 255}));\n    connect(inverter2.pin2, lcl1.pin2) annotation(\n      Line(points = {{-60, -30}, {-42, -30}, {-42, -30}, {-42, -30}}, color = {0, 0, 255}));\n    connect(inverter2.pin1, lcl1.pin1) annotation(\n      Line(points = {{-60, -36}, {-42, -36}, {-42, -36}, {-42, -36}}, color = {0, 0, 255}));\n  connect(lc1.pin6, l12.pin3) annotation(\n      Line(points = {{-20, 36}, {8, 36}, {8, 10}}, color = {0, 0, 255}));\n  connect(lc1.pin5, l12.pin2) annotation(\n      Line(points = {{-20, 30}, {2, 30}, {2, 10}}, color = {0, 0, 255}));\n  connect(lc1.pin4, l12.pin1) annotation(\n      Line(points = {{-20, 24}, {-4, 24}, {-4, 10}}, color = {0, 0, 255}));\n  connect(l12.pin6, lcl1.pin6) annotation(\n      Line(points = {{8, -10}, {8, -24}, {-22, -24}}, color = {0, 0, 255}));\n  connect(l12.pin5, lcl1.pin5) annotation(\n      Line(points = {{2, -10}, {2, -30}, {-22, -30}}, color = {0, 0, 255}));\n  connect(l12.pin4, lcl1.pin4) annotation(\n      Line(points = {{-4, -10}, {-4, -36}, {-22, -36}}, color = {0, 0, 255}));\n  connect(l13.pin3, lc1.pin6) annotation(\n      Line(points = {{36, 36}, {-20, 36}}, color = {0, 0, 255}));\n  connect(l13.pin2, lc1.pin5) annotation(\n      Line(points = {{36, 30}, {-20, 30}, {-20, 30}, {-20, 30}, {-20, 30}}, color = {0, 0, 255}));\n  connect(l13.pin1, lc1.pin4) annotation(\n      Line(points = {{36, 24}, {-20, 24}, {-20, 24}, {-20, 24}}, color = {0, 0, 255}));\n  connect(l23.pin3, lcl1.pin6) annotation(\n      Line(points = {{38, -24}, {-22, -24}, {-22, -24}, {-22, -24}}, color = {0, 0, 255}));\n  connect(l23.pin2, lcl1.pin5) annotation(\n      Line(points = {{38, -30}, {-22, -30}, {-22, -30}, {-22, -30}}, color = {0, 0, 255}));\n  connect(l23.pin1, lcl1.pin4) annotation(\n      Line(points = {{38, -36}, {-22, -36}, {-22, -36}, {-22, -36}}, color = {0, 0, 255}));\n  connect(l13.pin6, rl1.pin3) annotation(\n      Line(points = {{56, 36}, {72, 36}, {72, 8}, {82, 8}, {82, 8}}, color = {0, 0, 255}));\n  connect(l13.pin5, rl1.pin2) annotation(\n      Line(points = {{56, 30}, {66, 30}, {66, 2}, {82, 2}, {82, 2}}, color = {0, 0, 255}));\n  connect(l13.pin4, rl1.pin1) annotation(\n      Line(points = {{56, 24}, {62, 24}, {62, -4}, {82, -4}, {82, -4}}, color = {0, 0, 255}));\n  connect(l23.pin5, rl1.pin2) annotation(\n      Line(points = {{58, -30}, {66, -30}, {66, 2}, {82, 2}, {82, 2}}, color = {0, 0, 255}));\n  connect(l23.pin6, rl1.pin3) annotation(\n      Line(points = {{58, -24}, {72, -24}, {72, 8}, {82, 8}, {82, 8}}, color = {0, 0, 255}));\n  connect(l23.pin4, rl1.pin1) annotation(\n      Line(points = {{58, -36}, {62, -36}, {62, -4}, {82, -4}, {82, -4}}, color = {0, 0, 255}));\n    annotation(\n      Diagram);\n  end microgrid;\n  \n  model testbench_SC2\n    grid.inverters.inverter inverter1(v_DC = 60)  annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  grid.filter.l rl(L1 = 0.0023, L2 = 0.0023, L3 = 0.0023, R1 = 0.170, R2 = 0.170, R3 = 0.170)  annotation(\n      Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n    connect(i1p1, inverter1.u1) annotation(\n      Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n    connect(i1p2, inverter1.u2) annotation(\n      Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n    connect(i1p3, inverter1.u3) annotation(\n      Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n  connect(inverter1.pin3, rl.pin3) annotation(\n      Line(points = {{-60, 36}, {-40, 36}, {-40, 36}, {-40, 36}}, color = {0, 0, 255}));\n  connect(inverter1.pin2, rl.pin2) annotation(\n      Line(points = {{-60, 30}, {-40, 30}, {-40, 30}, {-40, 30}}, color = {0, 0, 255}));\n  connect(inverter1.pin1, rl.pin1) annotation(\n      Line(points = {{-60, 24}, {-40, 24}, {-40, 24}, {-40, 24}}, color = {0, 0, 255}));\n  connect(rl.pin6, rl.pin4) annotation(\n      Line(points = {{-20, 36}, {-14, 36}, {-14, 24}, {-20, 24}, {-20, 24}}, color = {0, 0, 255}));\n  connect(rl.pin5, rl.pin6) annotation(\n      Line(points = {{-20, 30}, {-14, 30}, {-14, 36}, {-20, 36}, {-20, 36}}, color = {0, 0, 255}));\n  connect(rl.pin4, rl.pin5) annotation(\n      Line(points = {{-20, 24}, {-14, 24}, {-14, 30}, {-20, 30}, {-20, 30}}, color = {0, 0, 255}));\n    annotation(\n      Diagram);\n  end testbench_SC2;\n  \n  model paper\n    grid.inverters.inverter inverter1(v_DC = 60)  annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  grid.filter.lc lc(C1 = 0.0000136, C2 = 0.0000136, C3 = 0.0000136, L1 = 0.0023, L2 = 0.0023, L3 = 0.0023, R1 = 0.4, R2 = 0.4, R3 = 0.4, R4 = 0.0000000001, R5 = 0.0000000001, R6 = 0.0000000001)  annotation(\n      Placement(visible = true, transformation(origin = {-32, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground5 annotation(\n      Placement(visible = true, transformation(origin = {6, 14}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n    connect(i1p1, inverter1.u1) annotation(\n      Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n    connect(i1p2, inverter1.u2) annotation(\n      Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n    connect(i1p3, inverter1.u3) annotation(\n      Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n    connect(lc.pin3, inverter1.pin3) annotation(\n      Line(points = {{-42, 36}, {-60, 36}, {-60, 36}, {-60, 36}}, color = {0, 0, 255}));\n    connect(lc.pin2, inverter1.pin2) annotation(\n      Line(points = {{-42, 30}, {-60, 30}, {-60, 30}, {-60, 30}}, color = {0, 0, 255}));\n    connect(lc.pin1, inverter1.pin1) annotation(\n      Line(points = {{-42, 24}, {-60, 24}, {-60, 24}, {-60, 24}}, color = {0, 0, 255}));\n  connect(lc.pin6, ground5.p) annotation(\n      Line(points = {{-22, 36}, {6, 36}, {6, 24}, {6, 24}}, color = {0, 0, 255}));\n  connect(lc.pin5, ground5.p) annotation(\n      Line(points = {{-22, 30}, {6, 30}, {6, 24}, {6, 24}}, color = {0, 0, 255}));\n  connect(lc.pin4, ground5.p) annotation(\n      Line(points = {{-22, 24}, {6, 24}, {6, 24}, {6, 24}}, color = {0, 0, 255}));\n    annotation(\n      Diagram);\n  end paper;\n  \n  model paper_loadstep\n    grid.inverters.inverter inverter1(v_DC = 60)  annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  grid.filter.lc lc(C1 = 0.0000136, C2 = 0.0000136, C3 = 0.0000136, L1 = 0.0023, L2 = 0.0023, L3 = 0.0023, R1 = 0.4, R2 = 0.4, R3 = 0.4, R4 = 0.0000000001, R5 = 0.0000000001, R6 = 0.0000000001)  annotation(\n      Placement(visible = true, transformation(origin = {-32, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.loads.r r_load(R1 = 7.15, R2 = 7.15, R3 = 7.15)  annotation(\n      Placement(visible = true, transformation(origin = {10, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n    connect(i1p1, inverter1.u1) annotation(\n      Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n    connect(i1p2, inverter1.u2) annotation(\n      Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n    connect(i1p3, inverter1.u3) annotation(\n      Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n    connect(lc.pin3, inverter1.pin3) annotation(\n      Line(points = {{-42, 36}, {-60, 36}, {-60, 36}, {-60, 36}}, color = {0, 0, 255}));\n    connect(lc.pin2, inverter1.pin2) annotation(\n      Line(points = {{-42, 30}, {-60, 30}, {-60, 30}, {-60, 30}}, color = {0, 0, 255}));\n    connect(lc.pin1, inverter1.pin1) annotation(\n      Line(points = {{-42, 24}, {-60, 24}, {-60, 24}, {-60, 24}}, color = {0, 0, 255}));\n  connect(r_load.pin3, lc.pin6) annotation(\n      Line(points = {{0, 36}, {-22, 36}, {-22, 36}, {-22, 36}}, color = {0, 0, 255}));\n  connect(r_load.pin2, lc.pin5) annotation(\n      Line(points = {{0, 30}, {-22, 30}, {-22, 30}, {-22, 30}}, color = {0, 0, 255}));\n  connect(r_load.pin1, lc.pin4) annotation(\n      Line(points = {{0, 24}, {-22, 24}, {-22, 24}, {-22, 24}}, color = {0, 0, 255}));\n    annotation(\n      Diagram);\n  end paper_loadstep;\n  \n  model paper_stepfuncion\n    grid.inverters.inverter inverter1(v_DC = 60)  annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.filter.lc lc(C1 = 0.0000136, C2 = 0.0000136, C3 = 0.0000136, L1 = 0.0023, L2 = 0.0023, L3 = 0.0023, R1 = 0.4, R2 = 0.4, R3 = 0.4, R4 = 0, R5 = 0, R6 = 0)  annotation(\n      Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Electrical.Analog.Basic.Ground ground5 annotation(\n      Placement(visible = true, transformation(origin = {6, 14}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Step step annotation(\n      Placement(visible = true, transformation(origin = {-122, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n  connect(lc.pin3, inverter1.pin3) annotation(\n      Line(points = {{-40, 36}, {-60, 36}}, color = {0, 0, 255}));\n  connect(lc.pin2, inverter1.pin2) annotation(\n      Line(points = {{-40, 30}, {-60, 30}}, color = {0, 0, 255}));\n  connect(lc.pin1, inverter1.pin1) annotation(\n      Line(points = {{-40, 24}, {-60, 24}}, color = {0, 0, 255}));\n  connect(lc.pin6, ground5.p) annotation(\n      Line(points = {{-20, 36}, {-7, 36}, {-7, 24}, {6, 24}}, color = {0, 0, 255}));\n  connect(lc.pin5, ground5.p) annotation(\n      Line(points = {{-20, 30}, {-5, 30}, {-5, 24}, {6, 24}}, color = {0, 0, 255}));\n  connect(lc.pin4, ground5.p) annotation(\n      Line(points = {{-20, 24}, {6, 24}}, color = {0, 0, 255}));\n  connect(step.y, inverter1.u2) annotation(\n      Line(points = {{-110, 44}, {-94, 44}, {-94, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n  connect(inverter1.u3, step.y) annotation(\n      Line(points = {{-80, 36}, {-108, 36}, {-108, 44}, {-110, 44}, {-110, 44}}, color = {0, 0, 127}));\n  connect(inverter1.u1, step.y) annotation(\n      Line(points = {{-80, 24}, {-104, 24}, {-104, 44}, {-112, 44}, {-112, 44}, {-110, 44}}, color = {0, 0, 127}));\n    annotation(\n      Diagram);\n  end paper_stepfuncion;\n  \n  model step\n  Modelica.Blocks.Sources.Step step annotation(\n      Placement(visible = true, transformation(origin = {-22, 34}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n\n    annotation(\n      Diagram);\n  end step;\n  \n  model paper_loadstep_stepfu\n    grid.inverters.inverter inverter1(v_DC = 60)  annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.filter.lc lc(C1 = 0.0000136, C2 = 0.0000136, C3 = 0.0000136, L1 = 0.0023, L2 = 0.0023, L3 = 0.0023, R1 = 0.4, R2 = 0.4, R3 = 0.4, R4 = 0, R5 = 0, R6 = 0)  annotation(\n      Placement(visible = true, transformation(origin = {-32, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  Modelica.Blocks.Sources.Step step annotation(\n      Placement(visible = true, transformation(origin = {-104, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  loads.r r_load(R1 = 7.15, R2 = 7.15, R3 = 7.15) annotation(\n      Placement(visible = true, transformation(origin = {10, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n    connect(lc.pin3, inverter1.pin3) annotation(\n      Line(points = {{-42, 36}, {-60, 36}, {-60, 36}, {-60, 36}}, color = {0, 0, 255}));\n    connect(lc.pin2, inverter1.pin2) annotation(\n      Line(points = {{-42, 30}, {-60, 30}, {-60, 30}, {-60, 30}}, color = {0, 0, 255}));\n    connect(lc.pin1, inverter1.pin1) annotation(\n      Line(points = {{-42, 24}, {-60, 24}, {-60, 24}, {-60, 24}}, color = {0, 0, 255}));\n    connect(step.y, inverter1.u3) annotation(\n      Line(points = {{-92, 44}, {-86, 44}, {-86, 36}, {-80, 36}, {-80, 36}}, color = {0, 0, 127}));\n    connect(inverter1.u2, step.y) annotation(\n      Line(points = {{-80, 30}, {-86, 30}, {-86, 44}, {-92, 44}, {-92, 44}}, color = {0, 0, 127}));\n    connect(inverter1.u1, step.y) annotation(\n      Line(points = {{-80, 24}, {-86, 24}, {-86, 44}, {-92, 44}, {-92, 44}, {-92, 44}}, color = {0, 0, 127}));\n    connect(r_load.pin1, lc.pin4) annotation(\n      Line(points = {{0, 24}, {-22, 24}, {-22, 24}, {-22, 24}}, color = {0, 0, 255}));\n    connect(r_load.pin2, lc.pin5) annotation(\n      Line(points = {{0, 30}, {-22, 30}, {-22, 30}, {-22, 30}}, color = {0, 0, 255}));\n    connect(r_load.pin3, lc.pin6) annotation(\n      Line(points = {{0, 36}, {-22, 36}, {-22, 36}, {-22, 36}}, color = {0, 0, 255}));\n    annotation(\n      Diagram);\n  end paper_loadstep_stepfu;\n  \n  model testbench_SC_load\n    grid.inverters.inverter inverter1(v_DC = 60)  annotation(\n      Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p1 annotation(\n      Placement(visible = true, transformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 18}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p2 annotation(\n      Placement(visible = true, transformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 30}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n    Modelica.Blocks.Interfaces.RealInput i1p3 annotation(\n      Placement(visible = true, transformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-104, 42}, extent = {{-8, -8}, {8, 8}}, rotation = 0)));\n  grid.filter.l rl(L1 = 0.0023, L2 = 0.0023, L3 = 0.0023, R1 = 0.170, R2 = 0.170, R3 = 0.170)  annotation(\n      Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  grid.loads.r r(R1 = 20, R2 = 20, R3 = 20)  annotation(\n      Placement(visible = true, transformation(origin = {2, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));\n  equation\n    connect(i1p1, inverter1.u1) annotation(\n      Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127}));\n    connect(i1p2, inverter1.u2) annotation(\n      Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127}));\n    connect(i1p3, inverter1.u3) annotation(\n      Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127}));\n  connect(inverter1.pin3, rl.pin3) annotation(\n      Line(points = {{-60, 36}, {-40, 36}, {-40, 36}, {-40, 36}}, color = {0, 0, 255}));\n  connect(inverter1.pin2, rl.pin2) annotation(\n      Line(points = {{-60, 30}, {-40, 30}, {-40, 30}, {-40, 30}}, color = {0, 0, 255}));\n  connect(inverter1.pin1, rl.pin1) annotation(\n      Line(points = {{-60, 24}, {-40, 24}, {-40, 24}, {-40, 24}}, color = {0, 0, 255}));\n  connect(rl.pin6, r.pin3) annotation(\n      Line(points = {{-20, 36}, {-8, 36}}, color = {0, 0, 255}));\n  connect(rl.pin5, r.pin2) annotation(\n      Line(points = {{-20, 30}, {-8, 30}}, color = {0, 0, 255}));\n  connect(rl.pin4, r.pin1) annotation(\n      Line(points = {{-20, 24}, {-8, 24}}, color = {0, 0, 255}));\n    annotation(\n      Diagram);\n  end testbench_SC_load;\n  annotation(\n    uses(Modelica(version = \"3.2.3\")));\nend grid;"
  },
  {
    "path": "omg_grid/merge_fmus.py",
    "content": "from os import listdir, walk, chdir\nfrom os.path import join, basename, isdir\nfrom shutil import copytree\nfrom tempfile import TemporaryDirectory\nfrom tkinter.filedialog import askopenfilenames, Tk, asksaveasfilename\nfrom zipfile import ZipFile\nimport xml.etree.ElementTree as ET\nimport re\n\nfrom more_itertools import collapse\n\n\ndef get_fmu_key(path):\n    \"\"\"some identifier to derive a unique identifier from the fmu\"\"\"\n    root = ET.parse(join(path, \"modelDescription.xml\")).getroot()\n    s = ''  # root.attrib['guid']\n    s += ET.tostring(root.find('ModelVariables')).decode('utf-8')\n    s += ET.tostring(root.find('ModelStructure')).decode('utf-8')\n\n    # replace all floats\n    s = re.sub('\"[-+]?(\\d+([.,]\\d*)?|[.,]\\d+)([eE][-+]?\\d+)?\"', lambda f: str(float(f[0][1:-1])), s)\n    return s\n\n\ndef copy_binaries(wd, fmus, binaries):\n    primary = basename(fmus[0])\n    for fmu, binaries_ in zip(fmus[1:], binaries[1:]):\n        for binary in binaries_:\n            copytree(join(wd, basename(fmu), 'binaries', binary), join(wd, primary, 'binaries', binary))\n\n\nif __name__ == '__main__':\n    tk = Tk()\n    tk.withdraw()\n\n    fmus = askopenfilenames(title='Select FMUs to merge', filetypes=['Functional\\u00A0Mockup\\u00A0Unit {*.fmu}'])\n\n    if len(fmus) < 2:\n        raise ValueError('Please select multiple FMUs')\n\n    # create tmp directory\n    with TemporaryDirectory() as dir:\n        # unpack fmus to tmp directory\n        binaries = []\n        for fmu in fmus:\n            with ZipFile(fmu, 'r') as z:\n                z.extractall(join(dir, basename(fmu)))\n            # add all subdirectories of \"binaries\" in the zip files\n            binaries.append(list(filter(lambda f: not isdir(f), listdir(join(dir, basename(fmu), \"binaries\")))))\n\n        # check if all fmus provide different binaries (binary/*)\n        if len(set(collapse(binaries))) != len(list(collapse(binaries))):\n            raise ValueError(f'The provided binary folders are not unique: {list(zip(fmus, binaries))}')\n\n        # check if their xmls are sufficiently similar\n        if len(set([get_fmu_key(join(dir, basename(fmu))) for fmu in fmus])) > 1:\n            raise ValueError('The FMUs appear to be generated from different model files.' +\n                             str([get_fmu_key(join(dir, basename(fmu))) for fmu in fmus]))\n\n        # copy content of binary folder from secondary fmus to the folder of the primary\n        copy_binaries(dir, fmus, binaries)\n\n        # change the working directory into the primary fmu folder which we want to package\n        chdir(join(dir, basename(fmus[0])))\n        # zip the primary folder, delete tempfolder\n        with ZipFile(asksaveasfilename(filetypes=['Functional\\u00A0Mockup\\u00A0Unit {*.fmu}']), 'w') as zipObj:\n            # Iterate over all the files in directory\n            for folderName, subfolders, filenames in walk('.'):\n                for filename in filenames:\n                    # Add file to zip\n                    zipObj.write(join(folderName, filename))\n"
  },
  {
    "path": "omg_grid/package.mo",
    "content": "within ;\npackage omg_grid\n  extends Modelica.Icons.Package;\n  // Import mathematical constants and functions\n  import SI = Modelica.SIunits;\n  import Modelica.Constants.pi;\n  import Modelica.Math;\n  import Modelica.ComplexMath.'abs';\n  import Modelica.ComplexMath.arg;\n  import Modelica.ComplexMath.fromPolar;\n  import Modelica.ComplexMath.real;\n  import Modelica.ComplexMath.imag;\n  import Modelica.ComplexMath.conj;\n  import Modelica.ComplexMath.j;\n\n\n  annotation (\n  preferredView=\"info\",\n  uses(Modelica(version=\"3.2.3\"), Complex(version=\"3.2.3\"), ModelicaServices(version = \"3.2.3\")),\n  Documentation(info=\"<html>\n  <p><b>omg_grid</b> is a free package that is developed with the Modelica&reg; language from the\n  Modelica Association, see <a href=\\\"https://www.Modelica.org\\\">https://www.Modelica.org</a>.</p>  It was designed in the scope of the OpenModelica Microgrid Gym (OMG) project  see <a href=\\\"https://github.com/upb-lea/openmodelica-microgrid-gym\\\">https://github.com/upb-lea/openmodelica-microgrid-gym</a>\n  <p>It provides model components for microgrids.</p> \n  <p><b>Licensed under the Modelica License 2</b><br>\n  Copyright &copy; 2020, chair of Power Electronics and Electrical Drives, Paderborn University.</p>  \n  <p><i>This Modelica package is <u>free</u> software and the use is completely at <u>your own risk</u>; \n  it can be redistributed and/or modified under the terms of the Modelica License 2. \n  For license conditions (including the disclaimer of warranty) \n  see <a href=\\\"modelica://Modelica.UsersGuide.ModelicaLicense2\\\">Modelica.UsersGuide.ModelicaLicense2</a> \n  or visit <a href=\\\"https://www.modelica.org/licenses/ModelicaLicense2\\\"> https://www.modelica.org/licenses/ModelicaLicense2</a>.</i></p>  \n  <p/>\n  </html>\"));\nend omg_grid;\n"
  },
  {
    "path": "omg_grid/package.order",
    "content": "UsersGuide\nFilter\nLoads\nActiveLoads\nInverters\nPLLs\nTransformations\nComponents\nGrids\nExamples\n"
  },
  {
    "path": "openmodelica_microgrid_gym/__init__.py",
    "content": "import logging\n\nfrom gym.envs.registration import register\n\nfrom openmodelica_microgrid_gym.agents import Agent\nfrom openmodelica_microgrid_gym.env import ModelicaEnv\nfrom openmodelica_microgrid_gym.execution import Runner\n\n__all__ = ['Agent', 'Runner']\n__version__ = '0.4.0'\n\nregister(\n    id='ModelicaEnv_test-v1',\n    entry_point='openmodelica_microgrid_gym.env:ModelicaEnv',\n    kwargs=dict(log_level=logging.DEBUG, max_episode_steps=500, viz_mode='episode', is_normalized=False)\n)\n\nregister(\n    id='ModelicaEnv-v1',\n    entry_point='openmodelica_microgrid_gym.env:ModelicaEnv',\n    kwargs=dict(is_normalized=False)\n)\n"
  },
  {
    "path": "openmodelica_microgrid_gym/agents/__init__.py",
    "content": "from openmodelica_microgrid_gym.agents.agent import Agent\nfrom openmodelica_microgrid_gym.agents.safeopt import SafeOptAgent\nfrom openmodelica_microgrid_gym.agents.staticctrl import StaticControlAgent\n\n__all__ = ['Agent', 'SafeOptAgent', 'StaticControlAgent']\n"
  },
  {
    "path": "openmodelica_microgrid_gym/agents/agent.py",
    "content": "from typing import List, Union\n\nimport numpy as np\nfrom matplotlib.figure import Figure\n\nfrom openmodelica_microgrid_gym.env import ModelicaEnv\nfrom openmodelica_microgrid_gym.util import EmptyHistory\n\n\nclass Agent:\n    def __init__(self, obs_varnames: List[str] = None, history: EmptyHistory = None, env: ModelicaEnv = None):\n        \"\"\"\n        Abstract base class for all Agents. The agent can act on the environment and observe its result.\n        This class is aims to wrap the whole learning process into a class to simplify the implementation.\n\n        This class is strongly inspired by the tensorforce library.\n\n        :param obs_varnames: list of variable names that match the values of the observations\n         passed in the act function. Will be automatically set by the Runner class\n        :param history: used to store agent internal data for monitoring\n        :param env: reference to the environment (only needed when used in internal act function)\n        \"\"\"\n        self.env = env\n        self.history = history or EmptyHistory()\n        self.obs_varnames = obs_varnames\n\n    def reset(self):\n        \"\"\"\n        Resets all agent buffers and discards unfinished episodes.\n        \"\"\"\n        self.history.reset()\n        self.prepare_episode()\n\n    def act(self, obs: np.ndarray) -> np.ndarray:\n        \"\"\"\n        Select an action with respect to the state this might update the internal state with respect to the history.\n\n        :param obs: Observation\n        :return: Actions the agent takes with respect to the observation\n        \"\"\"\n        pass\n\n    def observe(self, reward: float, terminated: bool):\n        \"\"\"\n        The observe function is might be called after the act function.\n        It might trigger the learning in some implementations.\n\n        :param reward: reward from the environment after the last action\n        :param terminated: whether the episode is finished\n        \"\"\"\n        pass\n\n    @property\n    def measurement_cols(self) -> List[Union[List, str]]:\n        \"\"\"\n        Structured columns of the measurement. Used in the Runner to setup the history columns of the Environment.\n\n        :return: structured columns of measurement\n        \"\"\"\n        return []\n\n    def measure(self, obs: np.ndarray) -> np.ndarray:\n        \"\"\"\n        Measurements the agent takes on the environment. This data is passed to the environment.\n        The values returned by this property should be fully determined by the environment.\n        This is a workaround to provide data measurement like PLL controllers in the environment even though\n        they are functionally part of the Agent.\n\n        :return: current measurement\n        \"\"\"\n        return np.empty(0)\n\n    def render(self) -> Figure:\n        \"\"\"\n        Visualisation of the agent, e.g. its learning state or similar\n        \"\"\"\n        pass\n\n    def prepare_episode(self):\n        \"\"\"\n        Prepares the next episode; resets all controllers and filters (initial value of integrators...)\n        \"\"\"\n        pass\n\n    @property\n    def has_improved(self) -> bool:\n        \"\"\"\n        Defines if the performance increased or stays constant\n        Does not learn, can never improve\n\n        \"\"\"\n        return False\n\n    @property\n    def has_worsened(self) -> bool:\n        \"\"\"\n        Defines if the performance decreased or stays constant\n        Does not learn, can never improve\n\n        \"\"\"\n        return False\n"
  },
  {
    "path": "openmodelica_microgrid_gym/agents/episodic.py",
    "content": "from openmodelica_microgrid_gym.agents import Agent\n\n\nclass EpisodicLearnerAgent(Agent):\n\n    def observe(self, reward: float, terminated: bool):\n        \"\"\"\n        Observes current reward.\n        The idea of the episodic learner is to only learn after termination, therefore update_params() must not be\n        called during the episode but only at the end.\n        \"\"\"\n        if terminated:\n            self.update_params()\n\n    def update_params(self):\n        pass\n\n    @property\n    def performance(self):\n        return None\n\n    @performance.setter\n    def performance(self, val):\n        pass\n"
  },
  {
    "path": "openmodelica_microgrid_gym/agents/safeopt.py",
    "content": "import importlib\nimport logging\nfrom typing import Dict, Union, Any, List, Mapping\n\nimport GPy\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom GPy.kern import Kern\nfrom matplotlib.figure import Figure\nfrom safeopt import SafeOptSwarm\n\nfrom openmodelica_microgrid_gym.agents.episodic import EpisodicLearnerAgent\nfrom openmodelica_microgrid_gym.agents.staticctrl import StaticControlAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableParams\nfrom openmodelica_microgrid_gym.aux_ctl import Controller\n\nlogger = logging.getLogger(__name__)\n\n\nclass SafeOptAgent(StaticControlAgent, EpisodicLearnerAgent):\n    def __init__(self, mutable_params: Union[dict, list], abort_reward: int, min_performance: float, kernel: Kern,\n                 gp_params: Dict[str, Any],\n                 ctrls: List[Controller],\n                 obs_template: Mapping[str, List[Union[List[str], np.ndarray]]], obs_varnames: List[str] = None,\n                 **kwargs):\n        \"\"\"\n        Agent to execute safeopt algorithm (https://arxiv.org/abs/1509.01066) to control the environment by using\n        auxiliary controllers and Gaussian process to adopt the controller parameters (mutable_params) to safely\n        increase the performance.\n\n        :param mutable_params: safe inital controller parameters to adopt\n        :param abort_reward: factor to multiply with the initial reward to give back an abort_reward-times higher\n               negative reward in case of limit exceeded\n        :param kernel: kernel for the Gaussian process unsing GPy\n        :param gp_params: kernel parameters like bounds and lengthscale\n        :param ctrls: Controllers that are feed with the observations and exert actions on the environment\n        :param obs_template:\n            Template describing how the observation array should be transformed and passed to the internal controllers.\n            The key must match the name field of an internal controller.\n            The values are a list of:\n                - list of strings\n                    - matching variable names of the state\n                    - must match self.obs_varnames\n                    - will be substituted by the values on runtime\n                    - will be passed as an np.array of floats to the controller\n                - np.ndarray of floats (to be passed statically to the controller)\n                - a mixture of static and dynamic values in one parameter is not supported for performance reasons.\n            The values will be passed as parameters to the controllers step function.\n        :param obs_varnames: list of variable names that match the values of the observations\n         passed in the act function. Will be automatically set by the Runner class\n         :param min_performance: Minial allowed performance to define parameters as safe\n        \"\"\"\n        self.params = MutableParams(\n            list(mutable_params.values()) if isinstance(mutable_params, dict) else mutable_params)\n        self.kernel = kernel\n        self.bounds = gp_params['bounds']\n        self.noise_var = gp_params['noise_var']\n        self.prior_mean = gp_params['prior_mean']\n        self.safe_threshold = gp_params['safe_threshold']\n        self.explore_threshold = gp_params['explore_threshold']\n\n        self.abort_reward = abort_reward\n        self.episode_return = None\n        self.optimizer = None\n        self._performance = None\n        self._min_performance = min_performance\n        self.initial_performance = min_performance / 2\n        self.last_best_performance = 0  # set to 0 due to in MC otherwise we do not get the best/worst before the\n        # first update_params after the MC loop\n        self.last_worst_performance = 0\n        self.unsafe = False\n\n        self._iterations = 0\n\n        super().__init__(ctrls, obs_template, obs_varnames, **kwargs)\n        self.history.cols = ['J', 'Params']\n\n    def reset(self):\n        \"\"\"\n        Resets the kernel, episodic reward and the optimizer\n        \"\"\"\n        # reinstantiate kernel\n\n        kernel_params = self.kernel.to_dict()\n        cls_name = kernel_params['class']\n        mod = importlib.import_module('.'.join(cls_name.split('.')[:-1]))\n        cls = getattr(mod, cls_name.split('.')[-1])\n        remaining_params = {k: v for k, v in kernel_params.items() if k not in {'class', 'useGPU'}}\n        self.kernel = cls(**remaining_params)\n\n        self.params.reset()\n        self.optimizer = None\n        self.episode_return = 0\n        self.initial_performance = 1\n        self._performance = None\n        self.last_best_performance = 0\n        self.last_worst_performance = 0\n        self.unsafe = False\n\n        self._iterations = 0\n\n        return super().reset()\n\n    def observe(self, reward, terminated):\n        \"\"\"\n        Makes an observation of the enviroment.\n        If terminated, then caclulates the performance and the next values for the parameters using safeopt\n\n        :param reward: reward of the simulation step\n        :param terminated: True if episode is over or aborted\n        :return:\n        \"\"\"\n        self._iterations += 1\n        self.episode_return += reward or 0\n        if terminated:\n\n            if self.optimizer is None:\n                self.initial_performance = self.episode_return\n\n            # self.performance = self._iterations / (self.episode_return * self.initial_performance)\n            self.performance = (self.episode_return - self._min_performance) / (\n                    self.initial_performance - self.min_performance)\n            # safeopt update step\n            self.update_params()\n            # reset for new episode\n            self.prepare_episode()\n        # on other steps we don't need to do anything\n\n    def update_params(self):\n        \"\"\"\n        Sets up the Gaussian process in the first episodes, updates the parameters in the following.\n        \"\"\"\n        if self.optimizer is None:\n            # First Iteration\n            self.last_best_performance = self.performance\n            self.last_worst_performance = self.performance\n\n            # Define Mean \"Offset\": Like BK: Assume Mean = Threshold (BK = 0, now = 20% below first (safe) J: means: if\n            # new Performance is 20 % lower than the inital we assume as unsafe)\n            mf = GPy.core.Mapping(len(self.bounds), 1)\n            mf.f = lambda x: self.prior_mean * self.performance\n            mf.update_gradients = lambda a, b: 0\n            mf.gradients_X = lambda a, b: 0\n\n            gp = GPy.models.GPRegression(np.array([self.params[:]]),  # noqa\n                                         np.array([[self.performance]]), self.kernel,\n                                         noise_var=self.noise_var)\n            self.optimizer = SafeOptSwarm(gp, self.safe_threshold * self.performance, bounds=self.bounds,\n                                          threshold=self.explore_threshold * self.performance)\n\n        else:\n            self.optimizer.add_new_data_point(self.params[:], self.performance)\n\n        if self.performance < self.safe_threshold:  # Due to nromalization tp 1 safe_threshold directly enough\n            self.unsafe = True\n        self.history.append([self.performance, self.params[:]])\n        self.params[:] = self.optimizer.optimize()\n\n        if self.has_improved:\n            # if performance has improved store the current last index of the df\n            self.best_episode = self.history.df.shape[0] - 1\n\n            self.last_best_performance = self.performance\n\n        if self.has_worsened:\n            # if performance has improved store the current last index of the df\n            self.worst_episode = self.history.df.shape[0] - 1\n\n            self.last_worst_performance = self.performance\n\n    def render(self) -> Figure:\n        \"\"\"\n        Renders the results for the performance\n        \"\"\"\n        figure, ax = plt.subplots()\n        if self.optimizer.x.shape[1] > 3:\n            # check if the dimensionality is less then 4 dimension\n            logger.info('Plotting of GP landscape not possible for then 3 dimensions')\n            return figure\n        self.optimizer.plot(1000, figure=figure, ms=1)\n\n        # mark best performance in green\n        y, x = self.history.df.loc[self.best_episode, ['J', 'Params']]\n\n        y0, x0 = self.history.df.loc[0, ['J', 'Params']]\n\n        if len(x) == 1:\n            ax.scatter([x], [y], s=20, marker='x', linewidths=3, color='g')\n            ax.scatter([x0], [y0], s=20, marker='x', linewidths=3, color='m')\n            # ax.scatter([x], [y], s=20 * 10, marker='x', linewidths=3, color='g')\n\n\n        elif len(x) == 2:\n            ax.plot(x[0], x[1], 'og')\n            ax.plot(x0[0], x0[1], 'om')\n        else:\n            logger.warning('Choose appropriate number of control parameters')\n\n        plt.show()\n        return figure\n\n    def prepare_episode(self):\n        \"\"\"\n        Prepares the next episode; reset iteration counting variable and call superclass to reset controllers\n        \"\"\"\n        self._iterations = 0\n\n        super().prepare_episode()\n\n    @property\n    def has_improved(self) -> bool:\n        \"\"\"\n        Defines if the performance increased or stays constant\n\n        :return: True, if performance was increased or equal, else False\n        \"\"\"\n        return self.performance >= self.last_best_performance\n\n    @property\n    def has_worsened(self) -> bool:\n        \"\"\"\n        Defines if the performance increased or stays constant\n\n        :return: True, if performance was increased or equal, else False\n        \"\"\"\n        return self.performance <= self.last_worst_performance\n\n    @property\n    def performance(self):\n\n        if np.isnan(self.episode_return):\n            # toDo: set reward to -inf and stop agent?\n            # warning mit logger\n\n            logger.warning('UNSAFE! Limit exceeded, epsiode abort, give a reward of {} times the '\n                           'initial reward'.format(self.abort_reward))\n            # set r to doubled (negative!) initial reward\n            self._performance = self.abort_reward\n\n            \"\"\"print('Reward exceeded bad bound! Continue?(Type Y/N)')\n            variable = input('Does this spike make sence?! (Type Y/N): ')\n            if variable == 'N':\n                self.episode_return = -300\n            elif variable == 'Y':\n                logger.warning('UNSAFE! Limit exceeded, epsiode abort, give a reward of {} times the '\n                               'initial reward'.format(self.abort_reward))\n                # set r to doubled (negative!) initial reward\n                self._performance = self.abort_reward\n            else:\n                print('Choose appropiate answer! Programm will run with J =1')\n                self.episode_return = -300\n            \"\"\"\n\n        if self._performance is None:\n            # Performance = inverse average return (return/iterations)^⁻1 normalized by initial performance\n            # self._performance = self._iterations / (self.episode_return * self.initial_performance)\n            self._performance = (self.episode_return - self._min_performance) / (\n                    self.initial_performance - self.min_performance)\n\n        return self._performance\n\n    @performance.setter\n    def performance(self, new_performance):\n        self._performance = new_performance\n        # self.episode_return = self._iterations / (new_performance*self.initial_performance)\n\n    @property\n    def min_performance(self):\n        if self._min_performance is None:\n            self._min_performance = 0.95 * self.initial_performance\n        return self._min_performance\n"
  },
  {
    "path": "openmodelica_microgrid_gym/agents/staticctrl.py",
    "content": "from itertools import chain\nfrom typing import List, Mapping, Union\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.agents import Agent\nfrom openmodelica_microgrid_gym.agents.util import MutableParams\nfrom openmodelica_microgrid_gym.aux_ctl import Controller\nfrom openmodelica_microgrid_gym.util import ObsTempl\n\n\nclass StaticControlAgent(Agent):\n    def __init__(self, ctrls: List[Controller], obs_template: Mapping[str, List[Union[List[str], np.ndarray]]],\n                 obs_varnames: List[str] = None, **kwargs):\n        \"\"\"\n        Simple agent that controls the environment by using auxiliary controllers that are fully configured.\n        The Agent does not learn anything.\n\n        :param ctrls: Controllers that are feed with the observations and exert actions on the environment\n        :param obs_template:\n            Template describing how the observation array should be transformed and passed to the internal controllers.\n            The key must match the name field of an internal controller.\n            The values are a list of:\n                - list of strings\n                    - matching variable names of the state\n                    - must match self.obs_varnames\n                    - will be substituted by the values on runtime\n                    - will be passed as an np.array of floats to the controller\n                - np.array of floats (to be passed statically to the controller)\n                - a mixture of static and dynamic values in one parameter is not supported for performance reasons.\n            The values will be passed as parameters to the controllers step function.\n        :param obs_varnames: list of variable names that match the values of the observations\n         passed in the act function. Will be automatically set by the Runner class\n        \"\"\"\n        super().__init__(obs_varnames, **kwargs)\n        self.episode_return = 0\n        self.controllers = {ctrl.name: ctrl for ctrl in ctrls}\n        self.obs_template_param = obs_template\n        self._obs_template = None\n\n    @property\n    def obs_template(self):\n        \"\"\"\n        lazy fill convert the observation template as the runner will set the\n\n        :return:\n        \"\"\"\n        if self._obs_template is None:\n            self._obs_template = {ctrl: ObsTempl(self.obs_varnames, tmpl)\n                                  for ctrl, tmpl in self.obs_template_param.items()}\n        return self._obs_template\n\n    def act(self, state: np.ndarray):\n        \"\"\"\n        Executes the actions with the observations as parameters.\n\n        :param state: the agent itself is stateless. the state is stored in the controllers.\n        Therefore we simply pass the observation from the environment into the controllers.\n        \"\"\"\n        controls = [self.controllers[key].step() for key in self.obs_template.keys()]\n        return np.concatenate(controls)\n\n    def observe(self, reward: float, terminated: bool):\n        \"\"\"\n        The observe function is might be called after the act function.\n        It might trigger the learning in some implementations.\n\n        :param reward: reward from the environment after the last action\n        :param terminated: whether the episode is finished\n        \"\"\"\n        self.episode_return += reward or 0\n        if terminated:\n            # reset episode reward\n            self.prepare_episode()\n        # on other steps we don't need to do anything\n\n    @property\n    def measurement_cols(self) -> List[Union[List, str]]:\n        \"\"\"\n        Structured columns of the measurement. Used in the Runner to setup the history columns of the Environment.\n\n        :return: structured columns of measurement\n        \"\"\"\n\n        return [ctrl.history.structured_cols(None) for ctrl in self.controllers.values()]\n\n    def measure(self, state) -> np.ndarray:\n        \"\"\"\n        Measurements the agent takes on the environment. This data is passed to the environment.\n        The values returned by this property should be fully determined by the environment.\n        This is a workaround to provide data measurement like PLL controllers in the environment even though\n        they are functionally part of the Agent.\n\n        :return: current measurement\n        \"\"\"\n        for key, tmpl in self.obs_template.items():\n            params = tmpl.fill(state)\n            self.controllers[key].prepare(*params)\n        return np.array(list(chain.from_iterable([ctrl.history.last() for ctrl in self.controllers.values()])))\n\n    def prepare_episode(self):\n        \"\"\"\n        Prepares the next episode; resets all controllers and filters (initial value of integrators...)\n        \"\"\"\n        for ctrl in self.controllers.values():\n            ctrl.reset()\n        self.episode_return = 0\n\n    @property\n    def has_improved(self) -> bool:\n        \"\"\"\n        Defines if the performance increased or stays constant\n        Does not learn, can never improve\n\n        :return: False\n        \"\"\"\n        return False\n"
  },
  {
    "path": "openmodelica_microgrid_gym/agents/util.py",
    "content": "from typing import Sequence\n\n\nclass MutableFloat:\n    def __init__(self, f: float):\n        \"\"\"\n        Wrapper object to store a float and change/modify it in a call-by-reference manner\n\n        :param f: float as initial data\n        \"\"\"\n        self._f = f\n\n    def __float__(self):\n        return float(self.val)\n\n    def __repr__(self):\n        return f'{self.__class__.__name__}({float(self)})'\n\n    @property\n    def val(self):\n        \"\"\"\n        retrieve or update internal float variable\n\n        :getter: retrieve internal variable\n        :setter: substitute internal variable\n        \"\"\"\n        return self._f\n\n    @val.setter\n    def val(self, v: float):\n        self._f = v\n\n\nclass MutableParams:\n    def __init__(self, params: Sequence[MutableFloat]):\n        \"\"\"\n        Wrapper object to access, modify and reset a sequence of float values in a call-by-reference manner\n\n        Supports slicing for getting and setting\n\n        :param params: initial values\n        \"\"\"\n        self.vars = params\n        self.defaults = [float(v) for v in params]\n\n    def reset(self):\n        \"\"\"\n        Restore initial value of all variables\n        \"\"\"\n        for var, default in zip(self.vars, self.defaults):\n            var.val = default\n\n    def __setitem__(self, key, value):\n        if isinstance(key, slice):\n            for var, val in zip(self.vars[key], value):\n                var.val = val\n        else:\n            self.vars[key].val = value\n\n    def __getitem__(self, item):\n        if isinstance(item, slice):\n            return [float(v) for v in self.vars[item]]\n        return float(self.vars[item])\n\n    def __repr__(self):\n        return str(list(self.vars))\n"
  },
  {
    "path": "openmodelica_microgrid_gym/aux_ctl/__init__.py",
    "content": "from openmodelica_microgrid_gym.aux_ctl.inverter_controllers import *\nfrom openmodelica_microgrid_gym.aux_ctl.params import *\n\n__all__ = ['PI_params', 'PLLParams', 'DroopParams', 'InverseDroopParams', 'Controller',\n           'MultiPhaseABCPIPIController', 'MultiPhaseDQCurrentController', 'MultiPhaseDQ0PIPIController',\n           'MultiPhaseDQCurrentSourcingController']\n"
  },
  {
    "path": "openmodelica_microgrid_gym/aux_ctl/base.py",
    "content": "from typing import Tuple\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.aux_ctl.params import PLLParams\nfrom openmodelica_microgrid_gym.aux_ctl.pi_controllers import PIController\nfrom openmodelica_microgrid_gym.util import abc_to_alpha_beta, cos_sin, normalise_abc, Fastqueue\n\n\nclass DDS:\n    \"\"\"\n    Implements a basic Direct Digital Synthesizer (DDS) controller.\n    Basically is a resetting integrator to provide a theta reference at a given\n    frequency.\n    \"\"\"\n\n    def __init__(self, ts: float, dds_max: float = 1, theta_0: float = 0):\n        \"\"\"\n        :param ts: Sample time\n        :param dds_max: The value at which the DDS resets the integrator\n        :param theta_0: The initial value of the DDS upon initialisation (not reset)\n        \"\"\"\n        self._integralSum = 0\n        self._ts = ts\n        self._max = dds_max\n        self._integralSum = theta_0\n\n    def reset(self):\n        \"\"\"\n        Resets the DDS integrator to 0\n        \"\"\"\n        self._integralSum = 0\n\n    def step(self, freq: float):\n        \"\"\"\n        Advances the Oscilator\n\n        :param freq: Absolute frequency to oscillate at for the next time step\n\n        :return theta: The angle in RADIANS [0:2pi]\n        \"\"\"\n        self._integralSum += self._ts * freq\n\n        # reset oscilator one phase\n        if self._integralSum > self._max:\n            self._integralSum -= self._max\n\n        return self._integralSum * 2 * np.pi\n\n\nclass LimitLoadIntegral:\n    def __init__(self, dt, freq, i_lim, i_nom=None):\n        \"\"\"\n\n        :param dt: time resolution in s\n        :param freq: nominal frequency, used to calculate timewindow of interest\n        :param i_lim: maximum energy allowed over one half frequency time in J\n        :param i_nom: nominal energy allowed over one half frequency time in J\n        \"\"\"\n        self.dt = dt\n        # number of samples for a half freq\n        self._buffer = Fastqueue(int(1 / freq / 2 / dt))\n        self.integral = 0\n        self.lim_integral = len(self._buffer) * self.dt * i_lim ** 2\n        if i_nom:\n            self.nom_integral = len(self._buffer) * self.dt * i_nom ** 2\n        else:\n            self.nom_integral = len(self._buffer) * self.dt * (i_lim * .9) ** 2\n\n    def reset(self):\n        self.integral = 0\n        self._buffer.clear()\n\n    def step(self, value):\n        self.integral += self.dt * (value ** 2 - self._buffer.shift(value) ** 2)\n\n    def risk(self):\n        return np.clip((self.integral - self.nom_integral) / (self.lim_integral - self.nom_integral), 0, 1)\n\n\nclass PLL:\n    \"\"\"\n    Implements a basic PI controller based PLL to track the angle of a three-phase\n    ABC voltage\n    \"\"\"\n\n    def __init__(self, params: PLLParams, ts: float):\n        \"\"\"\n        :param params: PI Params for controller (kP, kI, limits, kB, f_nom, theta_0)\n        :param ts: absolute sampling time for the controller\n        \"\"\"\n        self._params = params\n        self._controller = PIController(params, ts)\n\n        # Uses a DDS oscillator to keep track of the internal angle\n        self._dds = DDS(ts=ts, theta_0=params.theta_0)\n\n        self._prev_cossin = cos_sin(params.theta_0)\n        self._sqrt2 = np.sqrt(2)\n\n    def step(self, v_abc: np.ndarray) -> Tuple[np.ndarray, float, float]:\n        \"\"\"\n        Performs a discrete set of calculations for the PLL\n\n        :param v_abc: Voltages in the abc frame to track\n\n        :return _prev_cossin: The internal cos-sin of the current phase angle\n        :return freq: The frequency of the internal oscillator\n        :return theta: The internal phase angle\n        \"\"\"\n        v_abc = normalise_abc(v_abc)\n        cossin_x = abc_to_alpha_beta(v_abc)\n        dphi = self.__phase_comp(cossin_x, self._prev_cossin)\n        freq = self._controller.step(dphi) + self._params.f_nom\n\n        theta = self._dds.step(freq)\n        self._prev_cossin = cos_sin(theta)\n\n        return self._prev_cossin, freq, theta\n\n    def reset(self):\n        self._dds.reset()\n\n    @staticmethod\n    def __phase_comp(cos_sin_x: np.ndarray, cos_sin_i: np.ndarray):\n        \"\"\"\n        The phase comparison calculation uses sin(A-B)= sin(A)cos(B)-cos(A)sin(B) =  A-B (approximates for small A-B)\n\n        :param cos_sin_x: Alpha-beta components of the external angle to track, should be normalised [-1,1]\n        :param cos_sin_i: Alpha-beta components of the internal angle to compare to, should be normalised [-1,1]\n\n        :return dphi: The approximate error between the two phases\n        \"\"\"\n        dphi = (cos_sin_x[1] * cos_sin_i[0]) - (cos_sin_x[0] * cos_sin_i[1])\n\n        return dphi\n"
  },
  {
    "path": "openmodelica_microgrid_gym/aux_ctl/droop_controllers.py",
    "content": "from openmodelica_microgrid_gym.aux_ctl.filter import PT1Filter\nfrom openmodelica_microgrid_gym.aux_ctl.params import InverseDroopParams\n\n\nclass DroopController(PT1Filter):\n    \"\"\"\n    Implements a basic first order filter with gain and time constant.\n    Uses the PT1 to implement the droop but modifies the gains and outputs as\n    required to implement inverter droop\n\n    Ignores the first order element if gain is set to 0, providing a linear gain\n    \"\"\"\n\n    def __init__(self, DroopParams, ts):\n        \"\"\"\n        :type DroopParams: DroopParams\n        :param DroopParams: The droop parameters (gain, tau, nom_value)\n        :type ts: float\n        :param ts: Sample time\n        \"\"\"\n        self._droopParams = DroopParams\n        super().__init__(DroopParams, ts)\n\n    def step(self, val_in):\n        \"\"\"\n        Implements a first order response on the input, using the initialised params\n\n        :type val_in: float\n        :param val_in: Input - instantaneous power/reactive power\n        :return f/V: frequency or voltage, depending on the load and nominal value\n        \"\"\"\n\n        return super().step(val_in) + self._droopParams.nom_val\n\n\nclass InverseDroopController(DroopController):\n    \"\"\"\n    Implements an inverse Droop controller. For the use in grid following inverters\n    as opposed to grid forming inverters\n    Uses the frequency to determine the power output.\n    Contains a derivative elements and an input filter.\n\n    Ignores the first order element if gain is set to 0, providing a linear gain\n    \"\"\"\n\n    def __init__(self, DroopParams: InverseDroopParams, ts: float):\n        \"\"\"\n\n        :param DroopParams: The InverseDroopControllerParams for the droop controller\n        :param ts: Sample step size\n        \"\"\"\n        super().__init__(DroopParams, ts)\n        self._params = DroopParams\n        self._prev_val = 0\n        self._ts = ts\n        self._droop_filt = PT1Filter(DroopParams.derivativeFiltParams, ts)\n\n    def step(self, val_in: float):\n        \"\"\"\n        Implements a inverse of the first order system\n\n        :param val_in: The result of a first order response to be reversed\n        :return f/V: frequency or voltage, depending on the load and nominal value\n        \"\"\"\n        val_in = self._droop_filt.step(val_in - self._params.nom_val)\n\n        derivative = (val_in - self._prev_val) / self._ts\n        derivative = derivative * self._params.tau\n\n        self._prev_val = val_in\n        if self._params.gain != 0:\n            output = (val_in / self._params.gain + derivative)\n            # print(\"Inverse val: {}, nom: {}, output: {}\".format(val_in,self._params.gain, output))\n            return output\n        else:\n            return 0\n"
  },
  {
    "path": "openmodelica_microgrid_gym/aux_ctl/filter.py",
    "content": "from openmodelica_microgrid_gym.aux_ctl.params import DroopParams\n\n\nclass Filter:\n    \"\"\"\n    An empty Filter defining a base interface for any inherenting classes\n    \"\"\"\n\n    def step(self, value):\n        pass\n\n\nclass PT1Filter(Filter):\n    \"\"\"\n    A PT1 Filter implementation\n    \"\"\"\n\n    def __init__(self, filtParams: DroopParams, ts: float):\n        \"\"\"\n        :param filtParams: The filter params\n        :param ts: Sample time\n        \"\"\"\n        self._params = filtParams\n        self._integral = 0\n        self._ts = ts\n\n    def reset(self):\n        \"\"\"\n        Resets the filter Integrator\n        \"\"\"\n        self._integral = 0\n\n    def step(self, val_in):\n        \"\"\"\n        Implements a first order PT1 filter on the input\n\n        :type val_in: float\n        :param val_in: Filter input\n        :return: Filtered output\n        \"\"\"\n\n        output = val_in * self._params.gain - self._integral\n\n        if self._params.tau != 0:\n            intIn = output / self._params.tau\n            self._integral = (self._integral + intIn * self._ts)\n            output = self._integral\n        elif self._params.gain != 0:\n            self._integral = 0\n        else:\n            output = 0\n\n        return output\n"
  },
  {
    "path": "openmodelica_microgrid_gym/aux_ctl/inverter_controllers.py",
    "content": "import logging\nfrom typing import List\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.util import SingleHistory, EmptyHistory, nested_map, inst_power, inst_reactive, \\\n    dq0_to_abc, abc_to_dq0, abc_to_dq0_cos_sin, inst_rms, dq0_to_abc_cos_sin\nfrom .base import DDS, PLL\nfrom .droop_controllers import DroopController, InverseDroopController\nfrom .observers import Observer\nfrom .params import *\nfrom .pi_controllers import MultiPhasePIController\n\nN_PHASE = 3\n\nlogger = logging.getLogger(__name__)\n\n\nclass Controller:\n    \"\"\"\n    Base class for all voltage and current controllers\n    \"\"\"\n\n    def __init__(self, IPIParams: PI_params, ts_sim: float, ts_ctrl: Optional[float] = None,\n                 history: EmptyHistory = None, name=''):\n        \"\"\"\n\n        :param IPIParams: PI parameters for the current control loop\n        :param ts_sim: positive float. absolute time resolution of the env\n        :param ts_ctrl: positive float. absolute sampling time for the controller\n        :param history: Dataframe to store internal data\n        \"\"\"\n        self.history = history or SingleHistory()\n        self._last_meas = []\n\n        self._ts = ts_ctrl\n\n        if ts_ctrl is None:\n            self._ts = ts_sim\n            self._undersample = 1\n        else:\n            self._ts = ts_ctrl\n            self._undersample = ts_ctrl / ts_sim\n            if abs(self._undersample - round(self._undersample)) > 1e-6 or self._undersample < 1:\n                raise ValueError('Controller time resolution must be a integer multiple of simulation time resolution')\n            elif abs(self._undersample - round(self._undersample)) > 0:\n                logger.warning('Controller time resolution is almost a integer multiple of simulation time resolution,'\n                               f' rounded undersampling is of {int(round(self._undersample))} is used.'\n                               ' The timeresolution will be undersampling wrt. to simulation time.')\n            self._undersample = int(round(self._undersample))\n\n        self._currentPI = MultiPhasePIController(IPIParams, self._ts)\n\n        # defining memory variables\n        self._undersampling_count = None\n        self._stored_control = None\n        self.name = name\n\n    def _set_hist_cols(self, cols):\n        \"\"\"\n        prefixes column names with controller name and append a entry for the clipped values\n        :param cols:\n        :return:\n        \"\"\"\n        self.history.cols = nested_map(lambda col: '.'.join([self.name, col]), cols + [f'm{s}_clipped' for s in 'abc'])\n\n    def reset(self):\n        \"\"\"\n        Resets the controller to initialization state. Must be called before usage.\n        \"\"\"\n        self.history.reset()\n        # enforce the first step call to calculate the set point\n        self._undersampling_count = 0\n        self._stored_control = np.zeros(N_PHASE)\n\n        self._currentPI.reset()\n\n    def step(self):\n        \"\"\"\n        Will use precalculated action and handle undersampling.\n        The function will replay the last control action for the duration of the undersampling.\n\n        :return: most up to date control action\n        \"\"\"\n\n        return self._stored_control\n\n    def prepare(self, *args, **kwargs):\n        \"\"\"\n        Performs the calculations for a discrete step of the controller and stores control response\n        \"\"\"\n\n        if self._undersampling_count == 0:\n            abc_action = self.control(*args, **kwargs)\n            abc_action = np.clip(abc_action, -1, 1)\n            self._last_meas.extend(abc_action)\n            self.history.append(self._last_meas)\n            self._stored_control = abc_action\n        self._undersampling_count = (self._undersampling_count + 1) % self._undersample\n\n    def control(self, *args, **kwargs) -> np.ndarray:\n        \"\"\"\n        Performs the calculations for a discrete step of the controller\n\n        :param currentCV: 1d-array with 3 entries, one for each phase. The feedback values for current\n        :param voltageCV:  1d-array with 3 entries, one for each phase. The feedback values for voltage\n        :param idq0SP:\n\n        :return: The controller output for the current calculation in the ABC frame\n        \"\"\"\n        pass\n\n\nclass VoltageCtl(Controller):\n    def __init__(self, VPIParams: PI_params, IPIParams: PI_params, Pdroop_param: DroopParams, Qdroop_param: DroopParams,\n                 ts_sim: float, ts_ctrl: Optional[float] = None, *args, **kwargs):\n        \"\"\"\n        Defines the controller for a voltage forming inverter (Master)\n\n        :param VPIParams: PI parameters for the voltage control loop\n        :param IPIParams: PI_parameter for the current control loop\n        :param Pdroop_param: Droop parameters for P-droop\n        :param Qdroop_param: Droop parameters for Q-droop\n        :param ts_sim: positive float. absolute time resolution of the env\n        :param ts_ctrl: positive float. absolute sampling time for the controller\n        \"\"\"\n        super().__init__(IPIParams, ts_sim=ts_sim, ts_ctrl=ts_ctrl, *args, **kwargs)\n        self._integralSum = 0\n\n        self._PdroopController = DroopController(Pdroop_param, self._ts)\n        self._QdroopController = DroopController(Qdroop_param, self._ts)\n\n        self._voltagePI = MultiPhasePIController(VPIParams, self._ts)\n        self._phaseDDS = DDS(self._ts)\n\n    def reset(self):\n        super().reset()\n        self._voltagePI.reset()\n        self._phaseDDS.reset()\n        self._PdroopController.reset()\n        self._QdroopController.reset()\n\n\nclass CurrentCtl(Controller):\n    def __init__(self, IPIParams: PI_params, pllPIParams: PLLParams, i_limit: float,\n                 Pdroop_param: InverseDroopParams, Qdroop_param: InverseDroopParams,\n                 ts_sim: float, ts_ctrl: Optional[float] = None, *args, **kwargs):\n        \"\"\"\n        Defines the controller for a current sourcing inverter (Slave)\n\n        :param IPIParams: PI_parameter for the current control loop\n        :param pllPIParams: PI parameters for the PLL controller\n        :param i_limit: Current limit\n        :param Pdroop_param: Droop parameters for P-droop\n        :param Qdroop_param: Droop parameters for Q-droop\n        :param ts_sim: positive float. absolute time resolution of the env\n        :param ts_ctrl: positive float. absolute sampling time for the controller\n        \"\"\"\n        super().__init__(IPIParams, ts_sim=ts_sim, ts_ctrl=ts_ctrl, *args, **kwargs)\n\n        self._i_limit = i_limit\n\n        self._PdroopController = InverseDroopController(Pdroop_param, self._ts)\n        self._QdroopController = InverseDroopController(Qdroop_param, self._ts)\n\n        # Three controllers  for each axis (d,q,0)\n        self._pll = PLL(pllPIParams, self._ts)\n\n    def reset(self):\n        super().reset()\n        self._pll.reset()\n        self._PdroopController.reset()\n        self._QdroopController.reset()\n\n\nclass MultiPhaseABCPIPIController(VoltageCtl):\n    \"\"\"\n    Implements a discrete multiphase PI-PI voltage forming control with current\n    limiting in ABC. Has its own internal oscillator to keep track of the internal angle\n    \n    Controls each phase individually in the abc axis.\n    \"\"\"\n\n    def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, **kwargs):\n        instPow = -inst_power(voltageCV, currentCV)\n        freq = self._PdroopController.step(instPow)\n        # Get the next phase rotation angle to implement\n        phase = self._phaseDDS.step(freq)\n\n        instQ = -inst_reactive(voltageCV, currentCV)\n        voltage = self._QdroopController.step(instQ)\n\n        VSP = voltage * 1.732050807568877\n        # Voltage SP in dq0 (static for the moment)\n        SPVdq0 = np.array([VSP, 0, 0])\n\n        # Get the voltage SPs in abc vector\n        # print(\"SPVdq0: {}, phase: {}\".format(SPVdq0,phase))\n        SPV = dq0_to_abc(SPVdq0, phase)\n\n        # print(\"QInst: {}, Volt {}\".format(instQ,VSP))\n        SPI = self._voltagePI.step(SPV, voltageCV)\n\n        # Average voltages from modulation indices created by current controller\n        return self._currentPI.step(SPI, currentCV)\n\n\nclass MultiPhaseDQ0PIPIController(VoltageCtl):\n    \"\"\"\n    Implements a discrete multiphase PI-PI voltage forming control with current\n    limiting. Has its own internal oscillator to keep track of the internal angle.\n    \n    Controls each phase individualy in the dq0 axis.\n    \"\"\"\n\n    def __init__(self, VPIParams: PI_params, IPIParams: PI_params,\n                 Pdroop_param: DroopParams, Qdroop_param: DroopParams,\n                 ts_sim: float, ts_ctrl: Optional[float] = None, observer: Optional[List[Observer]] = None,\n                 *args, **kwargs):\n        \"\"\"\n\n        :param VPIParams: PI parameters for the voltage control loop\n        :param IPIParams: PI_parameter for the current control loop\n        :param Pdroop_param: Droop parameters for P-droop\n        :param Qdroop_param: Droop parameters for Q-droop\n        :param ts_sim: positive float. absolute time resolution of the env\n        :param ts_ctrl: positive float. absolute sampling time for the controller\n        :param observer: list of Observers\n        \"\"\"\n        super().__init__(VPIParams, IPIParams, ts_sim=ts_sim, ts_ctrl=ts_ctrl, Pdroop_param=Pdroop_param,\n                         Qdroop_param=Qdroop_param, *args, **kwargs)\n        self._set_hist_cols(['phase', 'instPow', 'instQ', 'freq',\n                             [f'SPV{s}' for s in 'dq0'], [f'SPV{s}' for s in 'abc'],\n                             [f'SPI{s}' for s in 'dq0'], [f'SPI{s}' for s in 'abc'],\n                             [f'CVV{s}' for s in 'dq0'],\n                             [f'CVi{s}' for s in 'dq0'],\n                             [f'I_hat{s}' for s in 'dq0'], [f'I_hat{s}' for s in 'abc'],\n                             [f'm{s}' for s in 'dq0'], [f'm{s}' for s in 'abc']])\n\n        self._prev_CV = np.zeros(N_PHASE)\n        self._lastMabc = np.zeros(N_PHASE)\n        self._observer = observer\n\n    def reset(self):\n        self._prev_CV = np.zeros(N_PHASE)\n        self._lastMabc = np.zeros(N_PHASE)\n        super().reset()\n\n    def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, **kwargs):\n        \"\"\"\n        Performs the calculations for a discrete step of the controller\n\n        :param currentCV: 1d-array with 3 entries, one for each phase in abc. The feedback values for current\n        :param voltageCV:  1d-array with 3 entries, one for each phase in abc. The feedback values for voltage\n\n        :return: Modulation index for the inverter in abc\n        \"\"\"\n\n        instPow = -inst_power(voltageCV, currentCV)\n        freq = self._PdroopController.step(instPow)\n        # Get the next phase rotation angle to implement\n        self.phase = phase = self._phaseDDS.step(freq)\n\n        instQ = -inst_reactive(voltageCV, currentCV)\n        voltage = self._QdroopController.step(instQ)\n\n        # Transform the feedback to the dq0 frame\n        CVIdq0 = abc_to_dq0(currentCV, phase)\n        CVVdq0 = abc_to_dq0(voltageCV, phase)\n\n        # If available, calulate load current using observer\n        iabc_out_etimate = np.empty(N_PHASE)\n        if self._observer is None:\n            iabc_out_etimate = np.zeros(N_PHASE)\n        else:\n            for j in range(N_PHASE):\n                iabc_out_etimate[j] = self._observer[j].cal_estimate(y=[currentCV[j], voltageCV[j]],\n                                                                     u=self._lastMabc[j])\n\n        i_dq0_out_estimat = abc_to_dq0(iabc_out_etimate, phase)\n\n        # Voltage controller calculations\n        VSP = voltage\n        # Voltage SP in dq0 (static for the moment)\n        SPVdq0 = np.array([VSP, 0, 0])\n        SPVabc = dq0_to_abc(SPVdq0, phase)\n        SPIdq0 = self._voltagePI.step(SPVdq0, CVVdq0, i_dq0_out_estimat)\n\n        SPIabc = dq0_to_abc(SPIdq0, phase)\n\n        # Current controller calculations\n        MVdq0 = self._currentPI.step(SPIdq0, CVIdq0)\n        MVabc = dq0_to_abc(MVdq0, phase)  # Transform the MVs back to the abc frame\n        self._lastMabc = MVabc\n        # Add intern measurment\n        self._last_meas = [phase, instPow, instQ, freq,\n                           *SPVdq0, *SPVabc,\n                           *SPIdq0, *SPIabc,\n                           *CVVdq0,\n                           *CVIdq0,\n                           *i_dq0_out_estimat, *iabc_out_etimate,\n                           *MVdq0, *MVabc]\n\n        return MVabc\n\n\nclass MultiPhaseDQCurrentController(CurrentCtl):\n    \"\"\"\n    Implements a discrete 3-phase current sourcing inverter, using a PLL to \n    keep track of the external phase angle\n    \n    Controls the currents dq0 axis, aligned to the external voltage vector,\n    d-axis is aligned with the A phase. Rotating frame aligned with A axis at #\n    t = 0, that is, at t = 0, the d-axis is aligned with the a-axis. \n    \n    DOES NOT wait for PLL lock before activating\n    \"\"\"\n\n    def __init__(self, IPIParams: PI_params, pllPIParams: PLLParams, i_limit: float,\n                 Pdroop_param: InverseDroopParams, Qdroop_param: InverseDroopParams,\n                 ts_sim: float, ts_ctrl: Optional[float] = None,\n                 lower_droop_voltage_threshold: float = 150., *args, **kwargs):\n        \"\"\"\n        :param IPIParams: PI_parameter for the current control loop\n        :param pllPIParams: PI parameters for the PLL controller\n        :param i_limit: Current limit\n        :param Pdroop_param: Droop parameters for P-droop\n        :param Qdroop_param: Droop parameters for Q-droop\n        :param lower_droop_voltage_threshold: Grid voltage threshold from where the controller starts to react on the\n               voltage and frequency in the grid\n        :param ts_sim: positive float. absolute time resolution of the env\n        :param ts_ctrl: positive float. absolute sampling time for the controller\n        :param history: Dataframe to store internal data\n        \"\"\"\n        super().__init__(IPIParams, pllPIParams, ts_sim=ts_sim, ts_ctrl=ts_ctrl, i_limit=i_limit,\n                         Pdroop_param=Pdroop_param, Qdroop_param=Qdroop_param, *args, **kwargs)\n        self._set_hist_cols(['phase', 'instPow', 'instQ', 'freq',\n                             [f'CVI{s}' for s in 'dq0'],\n                             [f'SPI{s}' for s in 'dq0'],\n                             [f'm{s}' for s in 'dq0'], [f'm{s}' for s in 'abc']])\n\n        # Populate the previous values with 0's\n        self._prev_cossine = np.zeros(2)\n        self._lastIDQ = np.zeros(N_PHASE)\n        self._prev_theta = 0\n        self._prev_freq = 0\n        self.lower_droop_voltage_threshold = lower_droop_voltage_threshold\n\n    def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, idq0SP: np.ndarray = np.zeros(3), **kwargs):\n        \"\"\"\n        Performs the calculations for a discrete step of the controller\n        Droop-control is started if Vgrid_rms exceeds 200 V, to avoid oscillation with the grid forming inverter\n\n        :param currentCV: 1d-array with 3 entries, one for each phase. The feedback values for current\n        :param voltageCV:  1d-array with 3 entries, one for each phase. The feedback values for voltage\n        :param idq0SP: The peak current setpoints in the dq0 frame (Additional power output to droop, if == 0, than\n            only droop is applied\n\n        :return: Modulation indices for the current sourcing inverter in ABC\n        \"\"\"\n        # Calulate P&Q for slave\n        instPow = -inst_power(voltageCV, currentCV)\n        instQ = -inst_reactive(voltageCV, currentCV)\n\n        Vinst = inst_rms(voltageCV)\n        # Get current phase information from the voltage measurement\n        self._prev_cossine, self._prev_freq, self._prev_theta = self._pll.step(voltageCV)\n\n        # Transform the current feedback to the DQ0 frame\n        self._lastIDQ = abc_to_dq0_cos_sin(currentCV, *self._prev_cossine)\n\n        droop = np.zeros(2)\n        if Vinst > self.lower_droop_voltage_threshold:\n            # Determine the droop power setpoints\n            droopPI = (self._PdroopController.step(self._prev_freq) / inst_rms(voltageCV))\n\n            # Determine the droop reactive power set points\n            droopQI = (self._QdroopController.step(Vinst) / Vinst)\n            droop = np.array([droopPI, droopQI]) / 3  # devide by 3 due to here dq, but we want to set the e.g.\n            # d-setpoint in the sum of the phases abc\n\n            droop = droop * 1.4142135623730951  # RMS to Peak\n            droop = np.clip(droop, -self._i_limit, self._i_limit)\n\n        idq0SP = idq0SP + np.array([-droop[0], +droop[1], 0])\n        # Calculate the control applied to the DQ0 currents\n        # action space is limited to [-1,1]\n\n        MVdq0 = self._currentPI.step(idq0SP, self._lastIDQ)\n        # Transform the outputs from the controllers (dq0) to abc\n        # also divide by SQRT(2) to ensure the transform is limited to [-1,1]\n\n        MVabc = dq0_to_abc_cos_sin(MVdq0, *self._prev_cossine)\n        self._last_meas = [self._prev_theta, instPow, instQ, self._prev_freq,\n                           *self._lastIDQ,\n                           *idq0SP,\n                           *MVdq0, *MVabc]\n        return MVabc\n\n\nclass MultiPhaseDQCurrentSourcingController(Controller):\n    \"\"\"\n    Implements a discrete multiphase PI current controller to supply an amount of current to a load / the grid which can\n    be chosen via state\"extension\" using idq0SP at a frequency of f_nom.\n    Has its own internal oscillator to keep track of the internal angle.\n\n    Controls each phase individually in the dq0 axis.\n    \"\"\"\n\n    def __init__(self, IPIParams: PI_params, ts_sim: float, ts_ctrl: Optional[float] = None, f_nom: float = 50.0,\n                 *args, **kwargs):\n        \"\"\"\n        :param IPIParams: PI_parameter for the current control loop\n        :param ts_sim: positive float. absolute time resolution of the env\n        :param ts_ctrl: positive float. absolute sampling time for the controller\n        :param f_nom: nominal frequency for current sourcing ctrl\n        \"\"\"\n        super().__init__(IPIParams, ts_sim=ts_sim, ts_ctrl=ts_ctrl, *args, **kwargs)\n        self._phaseDDS = DDS(self._ts)\n        self.f_nom = f_nom\n        self._set_hist_cols(['phase',\n                             [f'CVI{s}' for s in 'dq0'],\n                             [f'SPI{s}' for s in 'dq0'], [f'SPI{s}' for s in 'abc'],\n                             [f'm{s}' for s in 'dq0'], [f'm{s}' for s in 'abc']])\n\n        self._prev_CV = np.zeros(N_PHASE)\n\n    def reset(self):\n        super().reset()\n        self._phaseDDS.reset()\n\n    def control(self, currentCV: np.ndarray, idq0SP: np.ndarray = np.zeros(3), **kwargs):\n        \"\"\"\n        Performs the calculations for a discrete step of the controller\n\n        :param currentCV: 1d-array with 3 entries, one for each phase in abc. The feedback values for current\n        :param idq0SP: 1d-array with 3 entries, one for each phase in dq0. Current set points for current sourcing ctrl\n\n        :return: Modulation index for the inverter in abc\n        \"\"\"\n\n        # Get the next phase rotation angle to implement\n        phase = self._phaseDDS.step(self.f_nom)\n\n        # Transform the feedback to the dq0 frame\n        CVIdq0 = abc_to_dq0(currentCV, phase)\n\n        # current setpoint\n        SPIdq0 = np.array(idq0SP[:])\n        SPIabc = dq0_to_abc(SPIdq0, phase)\n        # Current controller calculations\n        MVdq0 = self._currentPI.step(SPIdq0, CVIdq0)\n        MVabc = dq0_to_abc(MVdq0, phase)\n\n        # Add intern measurment\n        self._last_meas = [phase,\n                           *CVIdq0,\n                           *SPIdq0, *SPIabc,\n                           *MVdq0, *MVabc]\n\n        # Transform the MVs back to the abc frame\n        return MVabc\n"
  },
  {
    "path": "openmodelica_microgrid_gym/aux_ctl/observers.py",
    "content": "import numpy as np\n\nN_PHASE = 3\n\n\nclass Observer():\n    def __init__(self):\n        pass\n\n    def cal_estimate(self, y, u):\n        \"\"\"\n        Estimtates output values depending on measured outputs y and intputs u\n\n        Our exemplary application:\n        x_hat = [iL_abc, vC_abc, iLoad_abc]\n        y = [iL_abc_mess, vC_abc_mess, 0]\n        C = [[1, 0, 0], [0, 1, 0], [0, 0, 0]] -> y_hat = [iL_hat, vC_hat, 0]\n        \"\"\"\n        pass\n\n\nclass Lueneberger(Observer):\n\n    def __init__(self, A, B, C, L, ts, vDC):\n        super().__init__()\n        self.A = A\n        self.B = B\n        self.C = C\n        self.L = L\n        self._ts = ts\n        self.vDC = vDC\n        self.last_x_estimate = np.zeros([3, 1])\n\n    def cal_estimate(self, y, u):\n        \"\"\"\n        Estimtates output values depending on measured outputs y and intputs u\n\n        Our exemplary application:\n        x_hat = [iL_abc, vC_abc, iLoad_abc]\n        y = [iL_abc_mess, vC_abc_mess, 0]\n        C = [[1, 0, 0], [0, 1, 0], [0, 0, 0]] -> y_hat = [iL_hat, vC_hat, 0]\n        \"\"\"\n\n        y_estimate = self.C @ self.last_x_estimate\n\n        uB = self.L @ (np.array(y).reshape([2, 1]) - y_estimate)\n\n        # times vCD because vDC was shifted to the OM-env\n        model_input = self.B * u * self.vDC\n\n        dx_estimate = model_input + uB + self.A @ self.last_x_estimate\n\n        self.last_x_estimate = self.last_x_estimate + dx_estimate * self._ts\n\n        return self.last_x_estimate[-1]\n"
  },
  {
    "path": "openmodelica_microgrid_gym/aux_ctl/params.py",
    "content": "\"\"\"\nThe parameter classes wrap controller parameters.\nThe fields are wrapped into properties in order to allow transparent usage of the MutableFloat wrapper\n\n\"\"\"\n\nfrom typing import Tuple, Union, Optional\n\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat\n\n\nclass FilterParams:\n\n    def __init__(self, gain: Union[MutableFloat, float], tau: Union[MutableFloat, float]):\n        \"\"\"\n        Defines Filter Parameters\n\n        :param gain: Filter gain\n        :param tau: Filter time constant\n        \"\"\"\n        self._gain = gain\n        self._tau = tau\n\n    @property\n    def gain(self):\n        return float(self._gain)\n\n    @property\n    def tau(self):\n        return float(self._tau)\n\n\nclass DroopParams(FilterParams):\n    \"\"\"\n    Defines droop parameters needed for the droop-controller for a voltage forming inverter\n    \"\"\"\n\n    def __init__(self, gain: Union[MutableFloat, float], tau: Union[MutableFloat, float], nom_value: float = 0):\n        \"\"\"\n        e.g. for a P-f droop controller (for voltage forming inverter)\n            Inverter of 10 kW, droop of 10% , tau of 1 sec, 50 Hz\n            Droop = gain = 1000 [W/Hz]\n            tau = 1\n            nomValue = 50 [Hz]\n\n        :param gain: The droop gain [W/Hz or VA/V], gets inverted\n        :param tau: The first order time constant [s]\n        :param nom_value: An offset to add to the output of the droop (e.g. f = 50 Hz)\n        \"\"\"\n        super().__init__(gain, tau)\n        self.nom_val = nom_value\n\n    @property\n    def gain(self):\n        if float(self._gain) != 0:\n            return 1 / float(self._gain)\n        return 0\n\n\nclass InverseDroopParams(DroopParams):\n    \"\"\"\n    Defines droop parameters needed for the droop-controller for a current sourcing inverter\n    \"\"\"\n\n    def __init__(self, gain: Union[MutableFloat, float], tau: Union[MutableFloat, float], nom_value: float = 0,\n                 tau_filt: Union[MutableFloat, float] = 0):\n        \"\"\"\n        e.g. for a f-P droop controller (for current sourcing inverter)\n            Inverter of 10 kW, droop of 10% , tau of 1 sec, 50 Hz\n            Droop = gain = 1000 [W/Hz]\n            tau = 1\n            nomValue = 50 [Hz]\n        :param gain: The droop gain [W/Hz or VA/V] - Defines the power output due to the frequency/voltage change from\n                      nom_val\n        :param tau: The first order time constant [s]\n        :param nom_value: An offset to add to the output of the droop (e.g. f = 50 Hz)\n        :param tau_filt: timeresolution for filter\n        \"\"\"\n\n        super().__init__(gain, tau, nom_value)\n        self.derivativeFiltParams = FilterParams(1, tau_filt)\n\n\nclass PI_params:\n    \"\"\"\n    The params for a basic PI Controller\n    All fields are represented by properties to allow passing MutableFloats\n    \"\"\"\n\n    def __init__(self, kP: Union[MutableFloat, float], kI: Union[MutableFloat, float],\n                 limits: Union[Tuple[MutableFloat, MutableFloat], Tuple[float, float]], kB: float = 1):\n        \"\"\"\n        :param kP: Proportional gain\n        :param kI: Intergral gain\n        :param limits: Controller limits\n        :param kB: Anti-windup (back calculation)\n        \"\"\"\n        self._kP = kP\n        self._kI = kI\n        self._limits = limits\n        self._kB = kB\n\n    @property\n    def kP(self):\n        return float(self._kP)\n\n    @property\n    def kI(self):\n        return float(self._kI)\n\n    @property\n    def limits(self):\n        if self._limits is None:\n            return [-float('inf'),float('inf')]\n        return [float(limit) for limit in self._limits]\n\n    @property\n    def kB(self):\n        return float(self._kB)\n\n\nclass PLLParams(PI_params):\n    \"\"\"\n    The params for a Phase Lock Loop (PLL) to measure the frequency\n    \"\"\"\n\n    def __init__(self, kP: Union[MutableFloat, float], kI: Union[MutableFloat, float],\n                 limits: Optional[Union[Tuple[MutableFloat, MutableFloat], Tuple[float, float]]] = None,\n                 kB: Union[MutableFloat, float] = 1, f_nom: float = 50, theta_0: float = 0):\n        \"\"\"\n        :param kP: Proportional gain\n        :param kI: Intergral gain\n        :param limits: Controller limits\n        :param kB: Anti-windup (back calculation)\n        :param f_nom: Nominal grid frequency to track (e.g. 50 Hz)\n        :param theta_0: Inital angle\n        \"\"\"\n        super().__init__(kP, kI, limits, kB)\n        self._f_nom = f_nom\n        self._theta_0 = theta_0\n\n    @property\n    def f_nom(self):\n        return float(self._f_nom)\n\n    @property\n    def theta_0(self):\n        return float(self._theta_0)\n"
  },
  {
    "path": "openmodelica_microgrid_gym/aux_ctl/pi_controllers.py",
    "content": "import logging\nfrom typing import Optional\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.aux_ctl.params import PI_params\n\nN_phase = 3\n\n\nclass PIController:\n    \"\"\"\n    Implements a basic, discrete PI controller.\n    Uses back calculation for anti-windup.\n    \"\"\"\n\n    def __init__(self, PI_param: PI_params, ts: float):\n        \"\"\"\n        :param PI_param: The PI_Parameters object with the PI controller parameters (kP, kI, kB for the gains of the\n            proportional, integral and anti-windup part and the limits of the output)\n        :param ts: Sample time\n        \"\"\"\n        self._params = PI_param\n        self.integralSum = 0\n        self.windup_compensation = 0\n        self._ts = ts\n\n    def reset(self):\n        \"\"\"\n        Resets the Integrator\n        \"\"\"\n        self.integralSum = 0\n\n    def step(self, error: float, feedforward: float = 0) -> float:\n        \"\"\"\n        Implements a step of a basic PI controller with anti-windup by back-calculation\n\n        :param error: Control error to act on\n        :param feedforward: feed forward term\n        :return: The calculated PI controller response to the error, using the\n                PI_Parameters provided during initialisation, clipped due to the defined limits\n        \"\"\"\n\n        self.integralSum += (self._params.kI * error + self.windup_compensation) * self._ts\n        output = self._params.kP * error + self.integralSum\n        clipped = np.clip(output + feedforward, *self._params.limits)\n        self.windup_compensation = (output + feedforward - clipped) * self._params.kB\n        return clipped.squeeze()\n\n\nclass MultiPhasePIController:\n    \"\"\"\n    Implements a number of PI controllers for use in multiphase systems\n    Number of phases is set to N_phase = 3\n    \"\"\"\n\n    def __init__(self, PI_param: PI_params, ts: float):\n        \"\"\"\n\n        :param PI_param: The PI_Parameters object with the PI controller parameters (kP, kI, kB for the gains of the\n            proportional, integral and anti-windup part and the limits of the output)\n        :param ts: Sample time\n        \"\"\"\n        self.controllers = [PIController(PI_param, ts) for _ in range(N_phase)]\n\n    def reset(self):\n        \"\"\"\n        Resets all controllers\n        \"\"\"\n        for ctl in self.controllers:\n            ctl.reset()\n\n    def step(self, SP: np.ndarray, CV: np.ndarray, feedforward: Optional[np.ndarray] = None) -> np.ndarray:\n        \"\"\"\n        Performs a controller step calculating the error itself using the array of\n        Setpoints (SP) and Controlled Variables (CV, feedback)\n\n        :param SP: Floats of setpoints\n        :param CV: Floats of system state to be controlled (feedback)\n        \n        :return output: An array of the controller outputs.\n        \"\"\"\n        if feedforward is None:\n            feedforward = np.zeros(len(SP))\n\n        error = SP - CV\n\n        if len(error) != len(self.controllers):\n            message = f'List of values for error inputs should be of the length {len(self.controllers)}, '\n            f'equal to the number of model inputs. Actual length {len(error)}'\n            logging.error(message)\n            raise ValueError(message)\n\n        # perform all the steps for each phase\n        return np.array([ctl.step(error[i], feedforward[i]) for i, ctl in enumerate(self.controllers)])\n"
  },
  {
    "path": "openmodelica_microgrid_gym/env/__init__.py",
    "content": "from .modelica import ModelicaEnv\nfrom .plot import PlotTmpl\n\n__all__ = ['ModelicaEnv', 'PlotTmpl']\n"
  },
  {
    "path": "openmodelica_microgrid_gym/env/modelica.py",
    "content": "import logging\nimport re\nfrom fnmatch import translate\nfrom functools import partial\nfrom typing import Sequence, Callable, List, Union, Tuple, Optional, Mapping, Dict, Any\n\nimport gym\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport scipy\nfrom matplotlib.figure import Figure\nfrom scipy import integrate\n\nfrom openmodelica_microgrid_gym.env.plot import PlotTmpl\nfrom openmodelica_microgrid_gym.env.pyfmi import PyFMI_Wrapper\nfrom openmodelica_microgrid_gym.net.base import Network\nfrom openmodelica_microgrid_gym.util import FullHistory, EmptyHistory, Fastqueue, ObsTempl\n\nlogger = logging.getLogger(__name__)\n\n\nclass ModelicaEnv(gym.Env):\n    \"\"\"\n    OpenAI gym Environment encapsulating an FMU model.\n    \"\"\"\n\n    viz_modes = {'episode', 'step', None}\n    \"\"\"Set of all valid visualisation modes\"\"\"\n\n    def __init__(self, net: Union[str, Network], time_start: float = 0,\n                 reward_fun: Callable[[List[str], np.ndarray, float], float] = lambda cols, obs, risk: 1,\n                 abort_reward: Optional[float] = -np.inf,\n                 is_normalized=True,\n                 log_level: int = logging.WARNING, solver_method: str = 'LSODA', max_episode_steps: Optional[int] = 200,\n                 model_params: Optional[Dict[str, Union[Callable[[float], Optional[float]], float]]] = None,\n                 model_path: str = '../omg_grid/grid.network.fmu',\n                 viz_mode: Optional[str] = 'episode', viz_cols: Optional[Union[str, List[Union[str, PlotTmpl]]]] = None,\n                 history: EmptyHistory = FullHistory(),\n                 action_time_delay: int = 0,\n                 on_episode_reset_callback: Optional[Callable[[], None]] = None,\n                 obs_output: List[str] = None):\n        \"\"\"\n        Initialize the Environment.\n        The environment can only be used after reset() is called.\n\n        :param time_start: offset of the time in seconds\n        :param reward_fun:\n            The function receives as a list of variable names and a np.ndarray of the values\n            of the current observation as well as a risk value between 0 and 1.\n            The separation is mainly for performance reasons, such that the resolution of data indices can be cached.\n            It must return the reward of this timestep as float.\n            It should return np.nan or -np.inf or None in case of a failiure.\n            It should have no side-effects\n        :param abort_reward: reward returned on episode abort\n        :param log_level: logging granularity. see logging in stdlib\n        :param solver_method: solver of the scipy.integrate.solve_ivp function\n        :param max_episode_steps: maximum number of episode steps.\n            After one episodes there are max_episode_steps+1 states available (one additionally caused by the reset)\n            and max_episode_steps actions, so the env.step() is executed max_episode_steps-times\n            The end time of the episode is calculated by the time resolution and the number of steps.\n\n            If set to None, the environment will never finish because of step sizes, but it might still stop because of\n            system failure (-inf reward)\n        :param model_params: parameters of the FMU.\n            dictionary of variable names and scalars or callables.\n            If a callable is provided it is called on reset with t==-1 and in every time step with the current time.\n            This callable must return a float or None\n            The float is passed to the fmu, None values are discarded.\n            This allows setting initial values using like::\n\n                model_params={'lc1.capacitor1.v': lambda t: -10 if t==-1 else None}\n\n            as well as continuosly changing values using like::\n\n                model_params={'lc1.capacitor1.v': lambda t: np.random.random()}\n        :param net: Path to the network configuration file passed to the net.Network.load() function\n        :param model_path: Path to the FMU\n        :param viz_mode: specifies how and if to render\n\n            - 'episode': render after the episode is finished\n            - 'step': render after each time step\n            - None: disable visualization\n        :param viz_cols: enables specific columns while plotting\n\n             - None: all columns will be used for visualization (default)\n             - string: will be interpret as regex. all fully matched columns names will be enabled\n             - list of strings: Each string might be a unix-shell style wildcard like \"*.i\"\n                                to match all data series ending with \".i\".\n             - list of PlotTmpl: Each template will result in a plot\n        :param history: history to store observations and measurement (from the agent) after each step\n        :param action_time_delay: Defines how many time steps the controller needs before the action is applied; action\n            is buffered in an array\n        :param on_episode_reset_callback: Callable which is executed during the environment reset. Can be used for\n            example to reset external processes like random process used in model params and to synchronize their reset\n            to the env reset\n        :param obs_output: List of strings of observations given to the agent. obs_output is compared to history.cols (\n            variable names have to fit)\n        \"\"\"\n        if viz_mode not in self.viz_modes:\n            raise ValueError(f'Please select one of the following viz_modes: {self.viz_modes}')\n\n        self.viz_mode = viz_mode\n        self._register_render = False\n        logger.setLevel(log_level)\n        self.solver_method = solver_method\n\n        # load model from fmu\n        self.model = PyFMI_Wrapper.load(model_path)\n\n        # if you reward policy is different from just reward/penalty - implement custom step method\n        self.reward = reward_fun\n        self.abort_reward = abort_reward\n        self._failed = False\n\n        # Parameters required by this implementation\n        self.max_episode_steps = max_episode_steps\n        if time_start < 0:\n            raise RuntimeError('Starttime must not negative! -1 is used for initialization of model params functions!'\n                               ' See ModelicaEnv.__init__() parameter \"model_params\".')\n        self.time_start = time_start\n        if isinstance(net, Network):\n            self.net = net\n        else:\n            self.net = Network.load(net)\n        self.time_step_size = self.net.ts\n        self.time_end = np.inf if max_episode_steps is None \\\n            else self.time_start + (max_episode_steps + 1) * self.time_step_size  # + 1 to execute env.step()\n        # max_episode_step-times\n\n        # if there are parameters, we will convert all scalars to constant functions.\n        model_params = model_params or dict()\n        # the \"partial\" is needed because of some absurd python behaviour https://stackoverflow.com/a/34021333/13310191\n        self.model_parameters = {var: (val if callable(val) else partial(lambda t, val_: val_, val_=val)) for var, val\n                                 in\n                                 model_params.items()}\n\n        self.sim_time_interval = None\n        self._state = np.empty(0)\n        self.measure = lambda obs: np.empty(0)  # type : Callable([np.ndarray],np.ndarray)\n        self.record_states = viz_mode == 'episode'\n        self.history = history\n        self.is_normalized = is_normalized\n        # also add the augmented values to the history\n        self.history.cols = self.net.out_vars(True, False)\n        self.model_input_names = self.net.in_vars()\n        # variable names are flattened to a list if they have specified in the nested dict manner)\n        self.model_output_names = self.net.out_vars(False, True)\n\n        self.viz_col_tmpls = []\n        if viz_cols is None:\n            logger.info('Provide the option \"viz_cols\" if you wish to select only specific plots. '\n                        'The default behaviour is to plot all data series')\n            self.viz_col_regex = '.*'\n        elif isinstance(viz_cols, list):\n            # strings are glob patterns that can be used in the regex\n            patterns, tmpls = [], []\n            for elem in viz_cols:\n                if isinstance(elem, str):\n                    patterns.append(translate(elem))\n                elif isinstance(elem, PlotTmpl):\n                    tmpls.append(elem)\n                else:\n                    raise ValueError('\"viz_cols\" list must contain only strings or PlotTmpl objects not'\n                                     f' {type(viz_cols)}')\n\n            self.viz_col_regex = '|'.join(patterns)\n            self.viz_col_tmpls = tmpls\n        elif isinstance(viz_cols, str):\n            # is directly interpret as regex\n            self.viz_col_regex = viz_cols\n        else:\n            raise ValueError('\"viz_cols\" must be one type Optional[Union[str, List[Union[str, PlotTmpl]]]]'\n                             f'and not {type(viz_cols)}')\n\n        # OpenAI Gym requirements\n        d_i, d_o = len(self.model_input_names), len(obs_output or self.history.cols)\n        self.action_space = gym.spaces.Box(low=np.full(d_i, -1), high=np.full(d_i, 1))\n        self.observation_space = gym.spaces.Box(low=np.full(d_o, -np.inf), high=np.full(d_o, np.inf))\n\n        self.used_action = np.zeros(self.action_space.shape)\n        self.action_time_delay = action_time_delay\n        if self.action_time_delay == 0:\n            self.delay_buffer = None\n        else:\n            self.delay_buffer = Fastqueue(self.action_time_delay, self.action_space.shape[0])\n\n        if on_episode_reset_callback is None:\n            self.on_episode_reset_callback = lambda: None\n        else:\n            self.on_episode_reset_callback = on_episode_reset_callback\n\n        if obs_output is None:\n            # if not defined all hist.cols are used as observations\n            self._out_obs_tmpl = ObsTempl(self.history.cols, None)\n        else:\n            self._out_obs_tmpl = ObsTempl(self.history.cols, [obs_output])\n\n    def _calc_jac(self, t, x) -> np.ndarray:  # noqa\n        \"\"\"\n        Compose Jacobian matrix from the directional derivatives of the FMU model.\n        This function will be called by the scipy.integrate.solve_ivp solver,\n        therefore we have to obey the expected signature.\n\n        :param t: time (ignored)\n        :param x: state (ignored)\n        :return: the Jacobian matrix\n        \"\"\"\n        return self.model.jacc()\n\n    def _get_deriv(self, t: float, x: np.ndarray) -> np.ndarray:\n        \"\"\"\n        Retrieve derivatives at given time and with given state from the FMU model\n\n        :param t: time\n        :param x: 1d float array of continuous states\n        :return: 1d float array of derivatives\n        \"\"\"\n        self.model.time = t\n        self.model.states = x.copy(order='C')\n\n        # Compute the derivative\n        dx = self.model.deriv\n        return dx\n\n    def _simulate(self) -> np.ndarray:\n        \"\"\"\n        Executes simulation by FMU in the time interval [start_time; stop_time]\n        currently saved in the environment.\n\n        :return: resulting state of the environment\n        \"\"\"\n        logger.debug(f'Simulation started for time interval {self.sim_time_interval[0]}-{self.sim_time_interval[1]}')\n\n        # Advance\n        x_0 = self.model.states\n\n        # Get the output from a step of the solver\n        sol_out = scipy.integrate.solve_ivp(\n            self._get_deriv, self.sim_time_interval, x_0, method=self.solver_method, jac=self._calc_jac)\n        # get the last solution of the solver\n        self.model.states = sol_out.y[:, -1]  # noqa\n\n        obs = self.model.obs\n        return obs\n\n    @property\n    def is_done(self) -> bool:\n        \"\"\"\n        Checks if the experiment is finished using a time limit\n\n        :return: True if simulation time exceeded\n        \"\"\"\n        if self._failed:\n            logger.info(f'risk level exceeded')\n            return True\n        logger.debug(f't: {self.sim_time_interval[1]}, ')\n        return abs(self.sim_time_interval[1]) > self.time_end\n\n    def reset(self) -> np.ndarray:\n        \"\"\"\n        OpenAI Gym API. Restarts environment and sets it ready for experiments.\n        In particular, does the following:\n\n            * resets model\n            * sets simulation start time to 0\n            * sets initial parameters of the model\n                - Using the parameters defined in self.model_parameters\n            * initializes the model\n\n        :return: state of the environment after resetting. If self.ob_output is not None, the there defined outputs are\n            returned.\n        \"\"\"\n        self.net.reset()\n        logger.debug(\"Experiment reset was called. Resetting the model.\")\n        self.on_episode_reset_callback()\n\n        self.sim_time_interval = np.array([self.time_start, self.time_start + self.time_step_size])\n        self.model.setup(self.time_start, self.model_output_names, self.model_parameters)\n\n        self.history.reset()\n        self._failed = False\n        self._register_render = False\n        self.used_action = np.zeros(self.action_space.shape)\n        if self.delay_buffer is not None:\n            self.delay_buffer.clear()\n        outputs = self._create_state(is_init=True)\n        return self._out_obs_tmpl.fill(outputs)[0]\n\n    def step(self, action: Sequence) -> Tuple[np.ndarray, float, bool, Mapping]:\n        \"\"\"\n        OpenAI Gym API. Determines how one simulation step is performed for the environment.\n        Simulation step is execution of the given action in a current state of the environment.\n\n        The state also contains the measurement.\n\n        :param action: action to be executed.\n        :return: state, reward, is done, info\n        \"\"\"\n        logger.debug(\"Experiment next step was called.\")\n        if self.is_done:\n            logger.warning(\n                \"\"\"You are calling 'step()' even though this environment has already returned done = True.\n                You should always call 'reset()' once you receive 'done = True' -- any further steps are\n                undefined behavior.\"\"\")\n            return self._state, -np.inf, True, {}\n\n        # check if action is a list. If not - create list of length 1\n        try:\n            iter(action)\n        except TypeError:\n            action = [action]\n            logger.warning(\"Model input values (action) should be passed as a list\")\n\n        # Check if number of model inputs equals number of values passed\n        if len(action) != len(list(self.model_input_names)):\n            message = f'List of values for model inputs should be of the length {len(list(self.model_input_names))},'\n            f'equal to the number of model inputs. Actual length {len(action)}'\n            logger.error(message)\n            raise ValueError(message)\n\n        action = np.clip(action, self.action_space.low, self.action_space.high)\n\n        # enqueue action and get delayed/last action\n        if self.delay_buffer is not None:\n            self.used_action = self.delay_buffer.shift(action)\n        else:\n            self.used_action = action\n\n        # Set input values of the model\n        logger.debug('model input: %s, values: %s', self.model_input_names, self.used_action)\n        self.model.set(**dict(zip(self.model_input_names, self.used_action)))\n\n        if self.model_parameters:\n            values = {var: f(self.sim_time_interval[0]) for var, f in self.model_parameters.items()}\n        else:\n            values = {}\n\n        params = {**values, **self.net.params(self.used_action)}\n        # delete None values to make model initialization possible (take care in model_params definition!)\n        params = {k: v for k, v in params.items() if v is not None}\n        if params:\n            self.model.set_params(**params)\n        risk = self.net.risk()\n\n        # Simulate and observe result state\n        outputs = self._create_state()\n\n        logger.debug(\"model output: %s, values: %s\", self.model_output_names, self._state)\n\n        # Check if experiment has finished\n        # Move simulation time interval if experiment continues\n        if not self.is_done:\n            logger.debug(\"Experiment step done, experiment continues.\")\n            self.sim_time_interval += self.time_step_size\n        else:\n            logger.debug(\"Experiment step done, experiment done.\")\n\n        reward = self.reward(self.history.cols, outputs, risk)\n        self._failed = risk >= 1 or reward is None or np.isnan(reward) or (np.isinf(reward) and reward < 0)\n\n        if self._failed:\n            reward = self.abort_reward\n\n        # only return the state, the agent does not need the measurement\n        # if self.obs_output is defined, obs_tmpl is used to filter out wanted observations, otherwise all states are\n        # passed\n        return self._out_obs_tmpl.fill(outputs)[0], reward, self.is_done, dict(risk=risk)\n\n    def _create_state(self, is_init: bool = False):\n        # Simulate and observe result state\n        if is_init:\n            self._state = self.model.obs\n            raw, normalized = self.net.augment(self._state, -1)\n        else:\n            self._state = self._simulate()\n            raw, normalized = self.net.augment(self._state, self.sim_time_interval[0])\n        outputs = normalized if self.is_normalized else raw\n        measurements = self.measure(outputs)\n\n        raw = np.hstack([raw, measurements])\n        self.history.append(raw)\n        outputs = np.hstack([outputs, measurements])\n        return outputs\n\n    def render(self, mode: str = 'human', close: bool = False) -> List[Figure]:\n        \"\"\"\n        OpenAI Gym API. Determines how current environment state should be rendered.\n        Does nothing at the moment\n\n        :param mode: (ignored) rendering mode. Read more in Gym docs.\n        :param close: flag if rendering procedure should be finished and resources cleaned. Used, when environment is closed.\n        \"\"\"\n        if self.viz_mode is None:\n            return []\n        elif self.viz_mode == 'step':\n            if close:\n                # TODO close plot\n                pass\n            pass\n\n        elif self.viz_mode == 'episode':\n            # TODO update plot\n            if not close:\n                self._register_render = True\n            elif self._register_render:\n                figs = []\n\n                # plot cols by theirs structure filtered by the vis_cols param\n                for cols in self.history.structured_cols():\n                    if not isinstance(cols, list):\n                        cols = [cols]\n                    cols = [col for col in cols if re.fullmatch(self.viz_col_regex, col)]\n                    if not cols:\n                        continue\n                    df = self.history.df[cols].copy()\n                    df.index = self.history.df.index * self.time_step_size + self.time_start\n\n                    fig, ax = plt.subplots()\n                    df.plot(legend=True, figure=fig, ax=ax)\n                    plt.show()\n                    figs.append(fig)\n\n                # plot all templates\n                for tmpl in self.viz_col_tmpls:\n                    fig, ax = plt.subplots()\n\n                    for series, kwargs in tmpl:\n                        ser = self.history.df[series].copy()\n                        ser.index = self.history.df.index * self.time_step_size + self.time_start\n                        ser.plot(figure=fig, ax=ax, **kwargs)\n                    tmpl.callback(fig)\n                    figs.append(fig)\n\n                return figs\n\n    def close(self) -> Tuple[bool, Any]:\n        \"\"\"\n        OpenAI Gym API. Closes environment and all related resources.\n        Closes rendering.\n\n        :return: True on success\n        \"\"\"\n        figs = self.render(close=True)\n        return True, figs\n"
  },
  {
    "path": "openmodelica_microgrid_gym/env/plot.py",
    "content": "from typing import List, Union, Callable, Optional\n\nfrom matplotlib.figure import Figure\nimport matplotlib.pyplot as plt\nfrom more_itertools import collapse\n\nfrom openmodelica_microgrid_gym.util import flatten_together\n\n\nclass PlotTmpl:\n    def __init__(self, variables: List[Union[List, str]], callback: Optional[Callable[[Figure], None]] = None,\n                 **kwargs):\n        \"\"\"\n        Provides an iterable of variables and plot parameters like ('induction1', {'color':'green', 'style': '--'}).\n        It contains logic to automatically match up the variables and provided kwargs to allow for a simple syntax.\n\n        e.g. when called with [['a','b'],['c','d']] and style = [['.', None],'--'] it will detect the grouping and apply\n        the dotted style to 'a' and the dashed style to 'c' and 'd'\n\n        :param vars: nested list of strings.\n         Each string represents a variable of the FMU or a measurement that should be plotted by the environment.\n        :param callback: if provided, it is executed after the plot is finished.\n         Will get the generated figure as parameter to allow further modifications.\n        :param kwargs: those arguments are merged (see omg.util.flatten_together) with the variables\n         and than provided to the pd.DataFrame.plot(·) function\n        \"\"\"\n\n        self.vars = list(collapse(variables))\n        self._callback = callback\n\n        # set colors None if not provided\n        colorkey = ({'c', 'color'} & set(kwargs.keys()))\n        if not colorkey:\n            kwargs['c'] = None\n            colorkey = 'c'\n        elif len(colorkey) > 1:\n            raise ValueError(f'Multiple color parameters provided \"{colorkey}\"')\n        else:\n            colorkey = colorkey.pop()\n\n        args = dict()\n        for k, v in dict(kwargs).items():\n            args[k] = flatten_together(variables, v)\n\n        # apply to a group only if all color values are none inside that group\n        if colorkey:\n            # if all elements in the variables are lists and they are all of equal length\n            lengths = set([isinstance(l, list) and len(l) for l in variables])\n            if len(lengths) == 1:\n                # set contains either the length of all lists or false if all values where non-list values\n                length = lengths.pop()\n                if length:\n                    for groups in range(len(variables)):\n                        for i in range(length):\n                            if args[colorkey][length * groups + i] is None:\n                                args[colorkey][length * groups + i] = 'C' + str(i + 1)\n            else:\n                # all elements are single values\n                for i, c in enumerate(args[colorkey]):\n                    if c is None:\n                        args[colorkey][i] = 'C' + str(i + 1)\n\n        # merge parameters to the variables for indexing access\n        self.kwargs = []\n        for i, _ in enumerate(self.vars):\n            args_ = dict()\n            for k, arg in args.items():\n                v = arg[i]\n                if v is not None:\n                    args_[k] = v\n            self.kwargs.append(args_)\n\n    def callback(self, fig: Figure):\n        \"\"\"\n        Will be called in the ModelicaEnv.render() once all plotting is finished.\n        This function enables the user to specify more modifications to apply to the figure.\n        The function will call the callable passed in the constructor.\n        Additionally the figure is plotted by this function.\n\n        :param fig: Finished figure that is one might want to modify.\n        :return:\n        \"\"\"\n        if self._callback is not None:\n            self._callback(fig)\n        else:\n            plt.show()\n\n    def __iter__(self):\n        self.i = -1\n        return self\n\n    def __next__(self):\n        try:\n            self.i += 1\n            return self.vars[self.i], self.kwargs[self.i]\n        except IndexError:\n            raise StopIteration\n\n    def __getitem__(self, item):\n        return self.vars[item], self.kwargs[item]\n"
  },
  {
    "path": "openmodelica_microgrid_gym/env/plotmanager.py",
    "content": "import os.path as p\n\nimport matplotlib.pyplot as plt\n\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\n\n\nclass PlotManager:\n    def __init__(self, used_agent: SafeOptAgent,\n                 save_results: bool = False, save_folder: str = 'test_folder', show_plots: bool = True):\n        \"\"\"\n        Class for plot configuration, knows agent, so can include agent params to title (e.g. performance)\n        If more plots should be stored in save_folder, extend the corresponding label function with save command\n        :param used_agent: agent used for experiment\n        :param save_results: if True, saves results to save_folder\n        :param save_folder: folder name\n        :param show_plots: if True, shows plots after each run\n        \"\"\"\n\n        self.agent = used_agent\n        self.save_results = save_results\n        self.save_folder = save_folder\n        self.show_plots = show_plots\n\n    def xylables_v_abc(self, fig):\n        self.update_axes(fig,\n                         ylabel='$v_{\\mathrm{abc}}\\,/\\,\\mathrm{V}$',\n                         filename=f'{self.agent.history.df.shape[0]}_J_{self.agent.performance}_v_abc0',\n                         legend=dict(handle_slice=slice(None, None, 3), labels=('Measurement', 'Setpoint'), loc='best'))\n\n    def xylables_v_dq0(self, fig):\n        self.update_axes(fig,\n                         ylabel='$v_{\\mathrm{dq0}}\\,/\\,\\mathrm{V}$',\n                         filename=f'{self.agent.history.df.shape[0]}_J_{self.agent.performance}_v_dq0',\n                         legend=dict(handle_slice=slice(None, None, 3), labels=('Measurement', 'Setpoint'), loc='best'))\n\n    def xylables_i_abc(self, fig):\n        self.update_axes(fig,\n                         ylabel='$i_{\\mathrm{abc}}\\,/\\,\\mathrm{A}$',\n                         filename=f'{self.agent.history.df.shape[0]}_J_{self.agent.performance}_i_abc',\n                         legend=dict(handle_slice=slice(None, None, 3), labels=('Measurement', 'Setpoint'), loc='best'))\n\n    def xylables_i_dq0(self, fig):\n        self.update_axes(fig,\n                         ylabel='$i_{\\mathrm{dq0}}\\,/\\,\\mathrm{A}$',\n                         filename=f'{self.agent.history.df.shape[0]}_J_{self.agent.performance}_i_dq0',\n                         legend=dict(handle_slice=slice(None, None, 3), labels=('Measurement', 'Setpoint'), loc='best'))\n\n    def update_axes(self, fig, title=None, xlabel=r'$t\\,/\\,\\mathrm{s}$', ylabel=None, legend=None, filename=None):\n        \"\"\"\n        General function to handle most of the standard modifications\n        :param fig: figure to change\n        :param title: optional title\n        :param xlabel: optional label, time in seconds is default\n        :param ylabel: optional ylabel\n        :param legend: optional legend, can have optional key \"handle_slice\" which is used to slice the line handles for legend and set labels accordingly.\n        :param filename: optional filename\n        \"\"\"\n        ax = fig.gca()\n        if title is not None:\n            plt.title(title)\n        if xlabel is not None:\n            ax.set_xlabel(xlabel)\n        if ylabel is not None:\n            ax.set_ylabel(ylabel)\n        ax.grid(which='both')\n        if legend is not None:\n            if 'handle_slice' in legend:\n                _slice = legend['handle_slice']\n                del legend['handle_slice']\n                plt.legend(handles=ax.lines[_slice], **legend)\n            else:\n                plt.legend(**legend)\n\n        if self.save_results and filename is not None:\n            for filetype in ['pgf', 'pdf']:\n                fig.savefig(p.join(self.save_folder, f'{filename}.{filetype}'))\n        if self.show_plots:\n            plt.show()\n        else:\n            plt.close(fig)\n"
  },
  {
    "path": "openmodelica_microgrid_gym/env/pyfmi.py",
    "content": "import logging\nfrom datetime import datetime\nfrom os.path import basename\nfrom typing import Dict, Callable\n\nimport numpy as np\nfrom pyfmi import load_fmu\n\nlogger = logging.getLogger(__name__)\n\n\nclass PyFMI_Wrapper:\n    \"\"\" convenience class\"\"\"\n\n    def __init__(self, model):\n        self.model = model\n\n    @classmethod\n    def load(cls, path):\n        model_name = basename(path)\n        logger.debug('Loading model \"%s\"', model_name)\n        model = cls(load_fmu(path, log_file_name=datetime.now().strftime(f'%Y-%m-%d_{model_name}.txt')))\n        logger.debug('Successfully loaded model \"%s\"', model_name)\n        return model\n\n    def setup(self, time_start, output_names, model_params: Dict[str, Callable]):\n        self.model.reset()\n        self.model.setup_experiment(start_time=time_start)\n\n        # This is needed, because otherwise setting new values seems not to work\n        self.model.initialize()\n        if model_params:\n            # set to -1 for initial evaluation of params. See documentation of ModelicaEnv.__init__().\n            values = {var: f(-1) for var, f in model_params.items()}\n            # values = {var: f(time_start) for var, f in model_params.items()}\n            # list of keys and list of values\n            self.set_params(**values)\n\n        e_info = self.model.get_event_info()\n        e_info.newDiscreteStatesNeeded = True\n        # Event iteration\n        while e_info.newDiscreteStatesNeeded:\n            self.model.enter_event_mode()\n            self.model.event_update()\n            e_info = self.model.get_event_info()\n\n        self.model.enter_continuous_time_mode()\n\n        # precalculating indices for more efficient lookup\n        self.model_output_idx = np.array([self.model.get_variable_valueref(k) for k in output_names])\n\n    @property\n    def obs(self):\n        return self.model.get_real(self.model_output_idx)\n\n    @property\n    def states(self):\n        return self.model.continuous_states\n\n    @states.setter\n    def states(self, val):\n        self.model.continuous_states = val\n\n    @property\n    def deriv(self):\n        return self.model.get_derivatives()\n\n    @property\n    def time(self):\n        return self.model.time\n\n    @time.setter\n    def time(self, val):\n        self.model.time = val\n\n    def jacc(self):\n        # get state and derivative value reference lists\n        refs = [[s.value_reference for s in getattr(self.model, attr)().values()]\n                for attr in\n                ['get_states_list', 'get_derivatives_list']]\n        jacobian = np.identity(len(refs[1]))\n        np.apply_along_axis(lambda col: self.model.get_directional_derivative(*refs, col), 0, jacobian)\n        return jacobian\n\n    def set(self, **kwargs):\n        self.model.set(*zip(*kwargs.items()))\n\n    def set_params(self, **kwargs):\n        # replacing enter and exit mode -> works to set parameters during simulation AND model get outputs\n        self.model.initialize()\n        self.model.set(*zip(*kwargs.items()))\n"
  },
  {
    "path": "openmodelica_microgrid_gym/execution/__init__.py",
    "content": "from openmodelica_microgrid_gym.execution.callbacks import Callback\nfrom openmodelica_microgrid_gym.execution.runner import Runner\n\n__all__ = ['Runner', 'Callback']\n"
  },
  {
    "path": "openmodelica_microgrid_gym/execution/callbacks.py",
    "content": "from abc import ABC\n\n\nclass Callback(ABC):\n    def reset(self):\n        pass\n\n    def __call__(self, *args, **kwargs):\n        pass\n\n\n\n"
  },
  {
    "path": "openmodelica_microgrid_gym/execution/runner.py",
    "content": "from typing import Dict, Any, Optional\n\nfrom tqdm import tqdm\n\nfrom openmodelica_microgrid_gym.agents import Agent\nfrom openmodelica_microgrid_gym.env import ModelicaEnv\nfrom openmodelica_microgrid_gym.execution.callbacks import Callback\n\n\nclass Runner:\n    \"\"\"\n    This class will execute an agent on the environment.\n    It handles communication between agent and environment and handles the execution of multiple epochs\n    \"\"\"\n\n    def __init__(self, agent: Agent, env: ModelicaEnv, callback: Optional[Callback] = None):\n        \"\"\"\n\n        :param agent: Agent that acts on the environment\n        :param env: Environment tha Agent acts on\n        \"\"\"\n        self.env = env\n        self.agent = agent\n        self.agent.env = env\n        self.run_data = dict()  # type: Dict[str,Any]\n        self.callback = callback\n        \"\"\"\n        Dictionary storing information about the experiment.\n        \n        - \"best_env_plt\": environment best plots\n        - \"best_episode_idx\": index of best episode\n        - \"agent_plt\": last agent plot\n        \"\"\"\n\n    def run(self, n_episodes: int = 10, visualise: bool = False):\n        \"\"\"\n        Trains/executes the agent on the environment for a number of epochs\n\n        :param n_episodes: number of epochs to play\n        :param visualise: turns on visualization of the environment\n        \"\"\"\n        self.agent.reset()\n        self.agent.obs_varnames = self.env.history.cols\n        self.env.history.cols = self.env.history.structured_cols(None) + self.agent.measurement_cols\n        self.env.measure=self.agent.measure\n\n        agent_fig = None\n\n        for i in tqdm(range(n_episodes), desc='episodes', unit='epoch'):\n            obs = self.env.reset()\n            if self.callback is not None:\n                self.callback.reset()\n            done, r = False, None\n            for _ in tqdm(range(self.env.max_episode_steps), desc='steps', unit='step', leave=False):\n                self.agent.observe(r, done)\n                act = self.agent.act(obs)\n                obs, r, done, info = self.env.step(act)\n                if self.callback is not None:\n                    self.callback(self.env.history.cols, self.env.history.last())\n                if visualise:\n                    self.env.render()\n                if done:\n                    break\n            # close env before calling final agent observe to see plots even if agent crashes\n            _, env_fig = self.env.close()\n            self.agent.observe(r, done)\n\n            if visualise:\n                agent_fig = self.agent.render()\n\n            self.run_data['last_agent_plt'] = agent_fig\n\n            if i == 0 or self.agent.has_improved:\n                self.run_data['best_env_plt'] = env_fig\n                self.run_data['best_episode_idx'] = i\n\n            if i == 0 or self.agent.has_worsened:\n                self.run_data['worst_env_plt'] = env_fig\n                self.run_data['worst_episode_idx'] = i\n"
  },
  {
    "path": "openmodelica_microgrid_gym/net/__init__.py",
    "content": "from .components import MasterInverter, SlaveInverter, MasterInverterCurrentSourcing\nfrom .base import Network\n\n__all__ = ['Network', 'MasterInverter', 'SlaveInverter', 'MasterInverterCurrentSourcing']\n"
  },
  {
    "path": "openmodelica_microgrid_gym/net/base.py",
    "content": "from importlib import import_module\nfrom typing import List, Dict, Optional, Union, Tuple\n\nimport numexpr as ne\nimport numpy as np\nimport yaml\nfrom more_itertools import collapse, flatten\n\n\nclass Component:\n    def __init__(self, net: 'Network', id=None, in_vars=None, out_vars=None, out_calc=None):\n        \"\"\"\n\n        :param net: Network to which component belongs to\n        :param id: Component ID\n        :param in_vars: Input variables to component\n        :param out_vars: Output variables from component\n        :param out_calc: (mapping from attr name to dimension of data vector) Adds values (e.g. references,...) to output\n        \"\"\"\n        self.net = net\n        self.id = id\n        self.in_vars = in_vars\n        self.in_idx = None  # type: Optional[dict]\n\n        self.out_calc = out_calc or {}\n        self.out_vars = out_vars\n        self.out_idx = None  # type: Optional[dict]\n\n        # has to be set via the network class\n        # net['inverter2'].post_calculate_hook = callable_func # type: Callable[[Component, float], dict]\n        # gets the component object and the current timestep\n        self.post_calculate_hook = None\n\n    def reset(self):\n        pass\n\n    def risk(self) -> float:\n        return 0\n\n    def params(self, actions):\n        \"\"\"\n        Calculate additional environment parameters\n\n        :param actions:\n        :return: mapping\n        \"\"\"\n        return {}\n\n    def get_in_vars(self):\n        \"\"\"\n        list of input variable names of this component\n        \"\"\"\n        if self.in_vars:\n            return [[self._prefix_var(val) for val in vals] for attr, vals in self.in_vars.items()]\n        return []\n\n    def get_out_vars(self, with_aug=False):\n        r = []\n        if self.out_vars:\n            r = [[self._prefix_var(val) for val in vals] for attr, vals in self.out_vars.items()]\n\n        if not with_aug:\n            return r\n        else:\n            return r + [[self._prefix_var([self.id, attr, str(i)]) for i in range(n)] for attr, n in\n                        self.out_calc.items()]\n\n    def fill_tmpl(self, state: np.ndarray):\n        if self.out_idx is None:\n            raise ValueError('call set_tmplidx before fill_tmpl. the keys must be converted to indices for efficiency')\n        for attr, idxs in self.out_idx.items():\n            # set object variables to the respective state variables\n            setattr(self, attr, state[idxs])\n\n    def set_outidx(self, keys):\n        # This pre-calculation is done mainly for performance reasons\n        keyidx = {val: idx for idx, val in enumerate(keys)}\n        self.out_idx = {}\n        try:\n            for var, keys in self.out_vars.items():\n                # lookup index in the whole state keys\n                self.out_idx[var] = [keyidx[self._prefix_var(key)] for key in keys]\n        except KeyError as e:\n            raise KeyError(f'the output variable {e!s} is not provided by your state keys')\n\n    def _prefix_var(self, strs):\n        if isinstance(strs, str):\n            strs = [strs]\n        if strs[0].startswith('.'):\n            # first string minus its prefix '.' and the remaining strs\n            strs[0] = strs[0][1:]\n            if self.id is not None:\n                # this is a complete identifier like 'lc1.inductor1.i' that should not be modified:\n                strs = [self.id] + strs\n        return '.'.join(strs)\n\n    def calculate(self) -> Dict[str, np.ndarray]:\n        \"\"\"\n        Will modify object variables (like current i of an inductor) it is called after all internal variables are set.\n        Therefore the function has side-effects.\n        The return value must be a dictionary whose keys match the keys of self.out_calc\n        and whose values are of the length of out_calcs values.\n        The returned values are hence additional values (like reference current i_ref).\n\n        ::\n            set(self.out_calc.keys()) == set(return)\n            all([len(v) == self.out_calc[k] for k,v in return.items()])\n\n        :return:\n        \"\"\"\n        pass\n\n    def normalize(self, calc_data):\n        \"\"\"\n        Will modify object variables it is called after all internal variables are set.\n        Therefore the function has side-effects, similarly to calculate().\n        \"\"\"\n        pass\n\n    def augment(self, state: np.ndarray, t: float):\n        \"\"\"\n        Stateful function that calculates additional values given the state of the environment.\n\n        :param t: timestamp from the environment\n        :param state:  state array to augment and normalize\n        :return: augmented state as tuple of raw and normalized\n        \"\"\"\n        self.fill_tmpl(state)\n        calc_data = self.calculate()\n        if callable(self.post_calculate_hook):\n            calc_data = {**calc_data, **self.post_calculate_hook(self, t)}\n        raw_data = self.extract_data(calc_data)\n\n        self.normalize(calc_data)\n        norm_data = self.extract_data(calc_data)\n\n        return raw_data, norm_data\n\n    def extract_data(self, calc_data):\n        \"\"\"\n        merge data from field variables (out_idx) and additional values (out_calc)\n        :param calc_data:\n        :return:\n        \"\"\"\n        attr = ''\n        try:\n            # concatenate internal values (like voltage) and newly calculated ones (like ref)\n            out_vals = [getattr(self, attr)\n                        for attr in self.out_idx.keys()]\n\n            # prepare newly calculated values\n            new_vals = [[calc_data[attr][i] for i in range(n)]\n                        for attr, n in self.out_calc.items()]\n\n            return np.hstack(out_vals + new_vals)\n        except KeyError as e:\n            raise ValueError(\n                f'{self.__class__} missing return key: {e!s}. did you forget to set it in the calculate method?')\n        except IndexError as e:\n            raise ValueError(f'{self.__class__}.calculate()[{attr}] has the wrong number of values')\n\n\nclass Network:\n    \"\"\"\n    This class has two main functions:\n\n    - :code:`load()`: load yaml files to instantiate an object structure of electronic components\n    - :code:`augment()`: traverses all components and uses the data from the simulation and augments or modifies it.\n    \"\"\"\n\n    def __init__(self, ts: float, v_nom: Union[int, str], freq_nom: float = 50):\n        self.ts = float(ts)\n        self.v_nom = ne.evaluate(str(v_nom))\n        self.freq_nom = freq_nom\n\n    @staticmethod\n    def _validate_load_data(data):\n        # validate that inputs are disjoined\n        components_with_inputs = [component['in'].values() for component in data['components'].values() if\n                                  'in' in component]\n        inputs = list(flatten(components_with_inputs))\n        if sum(map(len, inputs)) != len(set().union(*inputs)):\n            # all inputs are pairwise disjoint if the total number of inputs is the same as the number of elements in the union\n            raise ValueError('The inputs of the components should be disjoined')\n\n        return True\n\n    @classmethod\n    def load(cls, configurl='net.yaml'):\n        \"\"\"\n        Initialize object from config file\n        Structure of yaml-file:\n\n        .. code-block:: text\n\n            conf::             *net_params* *components*\n            net_params::       <parameters passed to Network.__init__()>\n            components::       components:\n                                 *component*\n                                 ...\n                                 *component*\n            component::        <key; has no semantic meaning, but needs to be unique>:\n                                 *component_params*\n            component_params:: cls: <ComponentCls>\n                               in:\n                                  <ComponentCls attr name>: <list of variablenames, see augment>\n                               out:\n                                  <ComponentCls attr name>: <list of variablenames, see augment>\n                               <additional parameters passed to ComponentCls.__init__()>\n\n        All 'in' and 'out' variable names together define the interaction with the environment,\n        expected cardinality and order of the vector provided to the augment().\n\n        :param configurl:\n        :return:\n        \"\"\"\n        data = yaml.safe_load(open(configurl))\n        if not cls._validate_load_data(data):\n            raise ValueError(f'loading {configurl} failed due to validation')\n        components = data['components']\n        del data['components']\n        self = cls(**data)\n\n        components_obj = []\n        for name, component in components.items():\n            # resolve class from 'cls' argument\n            comp_cls = component['cls']\n            del component['cls']\n            comp_cls = getattr(import_module('..components', __name__), comp_cls)\n\n            # rename keys (because 'in' is a reserved keyword)\n            if 'in' in component:\n                component['in_vars'] = component.pop('in')\n            if 'out' in component:\n                component['out_vars'] = component.pop('out')\n\n            # instantiate component class\n            try:\n                components_obj.append(comp_cls(net=self, **component))\n            except AttributeError as e:\n                raise AttributeError(f'{e!s}, please validate {configurl}')\n        self.components = components_obj\n\n        return self\n\n    def risk(self) -> float:\n        return np.max([comp.risk() for comp in self.components])\n\n    def reset(self):\n        for comp in self.components:\n            comp.reset()\n\n    def params(self, actions):\n        \"\"\"\n        Allows the network to add additional parameters like changing loads to the simulation\n\n        :param actions:\n        :return: mapping of additional actions and list of actions.\n        \"\"\"\n        d = {}\n        for comp in self.components:\n            params = comp.params(actions)\n            d.update(params)\n        return d\n\n    def augment(self, state: np.ndarray, t: float) -> Tuple[np.ndarray]:\n        \"\"\"\n        Allows the network to provide additional output variables in order to provide measurements and reference\n        information the RL agent needs to understand its rewards.\n\n        The function is stateful!\n\n        :param t: timestamp from the environment\n        :param state: raw state as recieved form the environment. must match the expected shape specified by :code:`in_vars()`\n        :return: augmented state in raw and normalized\n        \"\"\"\n        return tuple([np.hstack(data) for data in zip(*[comp.augment(state, t) for comp in self.components])])\n\n    def in_vars(self):\n        return list(collapse([comp.get_in_vars() for comp in self.components]))\n\n    def out_vars(self, with_aug=True, flattened=True):\n        r = [comp.get_out_vars(with_aug) for comp in self.components]\n        if flattened:\n            return list(collapse(r))\n        return r\n\n    @property\n    def components(self) -> List[Component]:\n        return self._components\n\n    @components.setter\n    def components(self, val: List[Component]):\n        self._components = val\n        keys = self.out_vars(with_aug=False, flattened=True)\n        for comp in self.components:\n            comp.set_outidx(keys)\n\n    def __getitem__(self, item):\n        \"\"\"\n        get component by id\n\n        :param item: name of the component\n        :return:\n        \"\"\"\n        for component in self.components:\n            if component.id == item:\n                return component\n        raise ValueError(f'no such component named \"{item}\"')\n"
  },
  {
    "path": "openmodelica_microgrid_gym/net/components.py",
    "content": "from functools import partial\nfrom typing import Optional\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.aux_ctl import DDS, DroopController, DroopParams, InverseDroopController, \\\n    InverseDroopParams, PLLParams, PLL\nfrom openmodelica_microgrid_gym.aux_ctl.base import LimitLoadIntegral\nfrom openmodelica_microgrid_gym.net.base import Component\nfrom openmodelica_microgrid_gym.util import dq0_to_abc, inst_power, inst_reactive\n\n\nclass Inverter(Component):\n    def __init__(self, u=None, i=None, i_noise: Optional[dict] = None, v=None, v_noise: Optional[dict] = None, i_nom=20,\n                 i_lim=30,\n                 v_lim=600, v_DC=1000,\n                 i_ref=(0, 0, 0),\n                 out_vars=None, **kwargs):\n        \"\"\"\n\n        :param u: input variable\n        :param i:\n        :param i_noise: structured like: must contain the key 'fun',\n        the key 'clip' is optional and no clipping is applied if omited\n        ::\n            {\n            'fun':\n               {<np.random function name, e.g. \"normal\">: <dict of kwargs to be passed to the func>},\n            'clip': <kwargs passed to clip>\n            }\n\n        :param v:\n        :param v_noise: similar to i_noise\n        :param i_nom:\n        :param i_lim:\n        :param v_lim:\n        :param v_DC:\n        :param i_ref:\n        :param out_vars: implicit parameter not to be passed in the net.yaml, but calculated dynamically in :code:`Network`\n        :param kwargs:\n        \"\"\"\n        self.u = u\n        self.v = v\n        self.i = i\n        # to feed static code analyser; vars will be set dynamically in the following loop\n        self.i_noise = None\n        self.v_noise = None\n        for var in ['i', 'v']:\n            # gets vars with reflection to create self.i_noise, self.v_noise\n            noise_var = locals()[f'{var}_noise']  # type:dict\n            if noise_var is None:\n                fun = partial(np.zeros, len(out_vars[var]))\n            else:\n                key, value = [i[0] for i in zip(*noise_var['fun'].items())]\n                clip_kwargs = noise_var.get('clip', dict(a_min=-float('inf'), a_max=float('inf')))\n                fun = lambda: np.clip(getattr(np.random.default_rng(), key)(**value, size=len(out_vars[var])),\n                                      **clip_kwargs)\n            setattr(self, f'{var}_noise', fun)\n\n        self.i_nom = i_nom\n        self.i_lim = i_lim\n        self.v_lim = v_lim\n        self.v_DC = v_DC\n        self.i_ref = i_ref\n        super().__init__(**{'out_calc': dict(i_ref=3), 'out_vars': out_vars, **kwargs})\n        self.limit_load_integrals = [\n            LimitLoadIntegral(self.net.ts, self.net.freq_nom, i_nom=i_nom, i_lim=i_lim) for _ in\n            range(3)]\n\n    def reset(self):\n        [integ.reset() for integ in self.limit_load_integrals]\n\n    def normalize(self, calc_data):\n        self.i = self.i / self.i_lim\n        self.v = self.v / self.v_lim\n        calc_data['i_ref'] = calc_data['i_ref'] / self.i_lim\n\n    def risk(self) -> float:\n        return max([integ.risk() for integ in self.limit_load_integrals])\n\n    def params(self, actions):\n        return {**super().params(actions), **{self._prefix_var(['.v_DC']): self.v_DC}}\n\n    def calculate(self):\n        self.i = self.i + self.i_noise()\n        self.v = self.v + self.v_noise()\n        [integ.step(i) for i, integ in zip(self.i, self.limit_load_integrals)]\n\n        # no reference values or similar added\n        return None\n\n\nclass SlaveInverter(Inverter):\n    def __init__(self, pll=None, pdroop=None, qdroop=None, **kwargs):\n        super().__init__(**kwargs)\n\n        pdroop = {**dict(gain=0.0), **(pdroop or {})}\n        qdroop = {**dict(gain=0.0), **(qdroop or {})}\n        pll = {**dict(kP=10, kI=200), **(pll or {})}\n\n        # toDo: set time Constant for droop Filter correct\n        self.pdroop_ctl = InverseDroopController(\n            InverseDroopParams(tau=self.net.ts, nom_value=self.net.freq_nom, **pdroop), self.net.ts)\n        self.qdroop_ctl = InverseDroopController(\n            InverseDroopParams(tau=self.net.ts, nom_value=self.net.v_nom, **qdroop), self.net.ts)\n        # default pll params and new ones\n        self.pll = PLL(PLLParams(f_nom=self.net.freq_nom, **pll), self.net.ts)\n\n    def reset(self):\n        super().reset()\n        self.pdroop_ctl.reset()\n        self.qdroop_ctl.reset()\n        self.pll.reset()\n\n    def calculate(self):\n        super().calculate()\n        _, _, phase = self.pll.step(self.v)\n        return dict(i_ref=dq0_to_abc(self.i_ref, phase))\n\n\nclass MasterInverter(Inverter):\n    def __init__(self, v_ref=(1, 0, 0), pdroop=None, qdroop=None, **kwargs):\n        self.v_ref = v_ref\n        super().__init__(out_calc=dict(i_ref=3, v_ref=3, phase=1), **kwargs)\n        pdroop = {**dict(gain=0.0, tau=.005), **(pdroop or {})}\n        qdroop = {**dict(gain=0.0, tau=.002), **(qdroop or {})}\n\n        self.pdroop_ctl = DroopController(DroopParams(nom_value=self.net.freq_nom, **pdroop), self.net.ts)\n        self.qdroop_ctl = DroopController(DroopParams(nom_value=self.net.v_nom, **qdroop), self.net.ts)\n        self.dds = DDS(self.net.ts)\n        self.phase = 0.0\n        self.v_refdq0 = np.array([0, 0, 0])\n\n    def reset(self):\n        super().reset()\n        self.pdroop_ctl.reset()\n        self.qdroop_ctl.reset()\n        self.dds.reset()\n        self.phase = 0.0\n        self.v_refdq0 = np.array([0, 0, 0])\n\n    def calculate(self):\n        super().calculate()\n        instPow = -inst_power(self.v, self.i)\n        freq = self.pdroop_ctl.step(instPow)\n        # Get the next phase rotation angle to implement\n        self.phase = self.dds.step(freq)\n\n        instQ = -inst_reactive(self.v, self.i)\n        v_refd = self.qdroop_ctl.step(instQ)\n        self.v_refdq0 = np.array([v_refd, 0, 0]) * self.v_ref\n\n        return dict(i_ref=dq0_to_abc(self.i_ref, self.phase), v_ref=dq0_to_abc(self.v_refdq0, self.phase),\n                    phase=np.array([self.phase]))\n\n    def normalize(self, calc_data):\n        super().normalize(calc_data),\n        calc_data['v_ref'] /= self.v_lim\n\n\nclass MasterInverter_dq0(MasterInverter):\n    \"\"\"\n    MasterInverter that returns observaton in dq0\n    \"\"\"\n\n    def calculate(self):\n        super().calculate()\n\n        return dict(i_ref=np.array(self.i_ref), v_ref=self.v_refdq0,\n                    phase=np.array([self.phase]))  # hier die phase mit rein\n\n\nclass MasterInverterCurrentSourcing(Inverter):\n    def __init__(self, f_nom=50, **kwargs):\n        super().__init__(out_calc=dict(i_ref=3), **kwargs)\n        self.dds = DDS(self.net.ts)\n        self.f_nom = f_nom\n        self.phase = 0.0\n\n    def reset(self):\n        super().reset()\n        self.dds.reset()\n        self.phase = 0.0\n\n    def calculate(self):\n        super().calculate()\n        # Get the next phase rotation angle to implement\n        self.phase = self.dds.step(self.f_nom)\n        return dict(i_ref=dq0_to_abc(self.i_ref, self.phase))\n\n\nclass Load(Component):\n    def __init__(self, i=None, **kwargs):\n        self.i = i\n        super().__init__(**kwargs)\n\n    def params(self, actions):\n        # TODO: perhaps provide modelparams that set resistance value\n        return super().params(actions)\n"
  },
  {
    "path": "openmodelica_microgrid_gym/util/__init__.py",
    "content": "from .fastqueue import Fastqueue\nfrom .itertools_ import nested_map, fill_params, nested_depth, flatten, flatten_together\nfrom .obs_template import ObsTempl\nfrom .randproc import RandProcess\nfrom .recorder import EmptyHistory, SingleHistory, FullHistory\nfrom .transforms import abc_to_alpha_beta, normalise_abc, abc_to_dq0_cos_sin, dq0_to_abc_cos_sin, abc_to_dq0, cos_sin, \\\n    dq0_to_abc, inst_power, inst_reactive, inst_rms, dq0_to_abc_cos_sin_power_inv\n\n__all__ = ['abc_to_alpha_beta', 'normalise_abc', 'abc_to_dq0_cos_sin', 'dq0_to_abc_cos_sin', 'abc_to_dq0',\n           'cos_sin', 'dq0_to_abc', 'inst_power', 'inst_reactive', 'inst_rms', 'dq0_to_abc_cos_sin_power_inv',\n           'nested_map', 'fill_params', 'nested_depth', 'flatten', 'flatten_together',\n           'EmptyHistory', 'SingleHistory', 'FullHistory', 'Fastqueue', 'RandProcess', 'ObsTempl']\n"
  },
  {
    "path": "openmodelica_microgrid_gym/util/fastqueue.py",
    "content": "from typing import Optional\n\nimport numpy as np\n\n\nclass Fastqueue:\n    def __init__(self, size: int, dim: Optional[int] = 1):\n        \"\"\"\n        Efficient numpy implementation of constant sized queue without queue-shifting (indices-based value selection).\n        Queue size of n leads to a delay of n-1.\n        :param size: Size of queue\n        \"\"\"\n        self._buffer = None\n        self._size, self._dim = size, dim\n        self._idx = 0\n\n    def shift(self, val):\n        \"\"\"\n        Pushes val into buffer and returns popped last element\n        \"\"\"\n        if self._buffer is None:\n            raise RuntimeError('please call clear() before using the object')\n        self._idx = self.wrap_index(self._idx + 1)\n        last = self._buffer[self._idx, :].copy()\n        self._buffer[self._idx, :] = val\n        return last\n\n    def __len__(self):\n        return self._size\n\n    def clear(self):\n        self._buffer = np.zeros((self._size, self._dim))\n\n    def wrap_index(self, i):\n        # ringbuffer implementation with index calculated using np.ravel... -> no shifting\n        return np.ravel_multi_index([i], (len(self),), mode='wrap')\n"
  },
  {
    "path": "openmodelica_microgrid_gym/util/itertools_.py",
    "content": "from typing import Callable, Mapping, Union, Any, List\n\nimport numpy as np\nimport pandas as pd\nfrom more_itertools import collapse\n\n\ndef flatten(data: Union[dict, list], remaining_levels: int = 0) -> list:\n    \"\"\"\n    transform this:\n\n    >>> {'a': {'b': [['i', 'v'],\n    >>>              ['k', 'h']]}}\n\n    results into\n\n    >>> ['a.b.i', 'a.b.v', 'a.b.k', 'a.b.h']\n\n    or\n\n    >>> [['a.b.i', 'a.b.v'], ['a.b.k', 'a.b.h']]\n\n\n    :param data: data to flatten. A nested dictionary containing nested lists as keys in the lowest level.\n    :param remaining_levels: number of levels to preserve in the nested list\n    :return: Flattened data as a nesteted list\n    \"\"\"\n    # collapse outer dicts\n    if isinstance(data, dict):\n        # flatten all the dicts\n        df = pd.json_normalize(data)\n        data = df.to_dict(orient='records')[0]\n        # move the key into the lists\n        for k, v in data.items():\n            data[k] = nested_map(lambda suffix: '.'.join([k, suffix]), v)\n        data = list(data.values())\n    # count levels and collapse to keep the levels as needed\n    depth = nested_depth(data)\n    if remaining_levels is None:\n        remaining_levels = depth - 1\n    return list(collapse(data, levels=depth - remaining_levels - 1))\n\n\ndef nested_map(fun: Callable, structure: Union[list, tuple, Mapping, np.ndarray]) \\\n        -> Union[list, tuple, Mapping, np.ndarray]:\n    \"\"\"\n    Traverses data structure and substitutes every element with the result of the callable\n\n    :param fun: Callable to be applied to every value\n    :param structure: Nesting of dictionaries or lists. For mappings, the callable is applied to the values.\n    :return:\n    \"\"\"\n    if isinstance(structure, Mapping):\n        return {k: nested_map(fun, v) for k, v in structure.items()}\n    if isinstance(structure, (list, tuple)):\n        return [nested_map(fun, l_) for l_ in structure]\n    if isinstance(structure, np.ndarray):\n        # empty_like would keep the datatype, with empty,\n        # we enforce that the dtype is infered from the result of the mapping function\n        a = np.empty(structure.shape)\n        for idx in np.ndindex(structure.shape):\n            a[idx] = nested_map(fun, structure[idx])\n        return a\n    return fun(structure)\n\n\ndef nested_depth(structure: Any) -> int:\n    \"\"\"\n    Calculate the maximum depth of a nested sequence.\n\n    :param structure: nested sequence. The containing data structures are currently restricted to lists and tuples,\n     because allowing any sequence would also result in traversing strings for example.\n     If a single value is passed, the return is 0\n    :return: maximum depth\n    \"\"\"\n    if isinstance(structure, (list, tuple, set)):\n        if structure:\n            # if the list contains elements\n            return 1 + max((nested_depth(l_) for l_ in structure))\n        return 1\n    return 0\n\n\ndef fill_params(template: Union[list, tuple, Mapping, np.ndarray], data: Union[pd.Series, Mapping]) \\\n        -> Union[list, tuple, Mapping, np.ndarray]:\n    \"\"\"\n    Uses a template, that can be traversed by nested_map.\n    Each entry in the template, that is a key in the mapping is replaced by the value it is mapped to.\n\n    :param template: template containing keys\n    :param data: mapping of keys to values\n    :return:\n    \"\"\"\n    if isinstance(data, pd.Series):\n        data = data.to_dict()\n    elif not isinstance(data, Mapping):\n        raise ValueError(\"must be a mapping\")\n\n    # keep key if there is no substitute\n    return nested_map(lambda k: data.get(k, k), template)\n\n\ndef flatten_together(structure: List[Union[List, Any]], values: Union[Any, List[Union[List, Any]]]):\n    \"\"\"\n    Flattens and fills a list of values relative to the groupings provided by the structure parameter.\n    The explicit values in the structure parameter are ignored. Only the nesting structure is of importance.\n    If a single value is provided it is simply repeated as often as the structure has values\n\n    e.g. when called with :code:`[[0, 0], [0, 0]]` and :code:`[[0, None], 4]` it will detect the grouping\n    and return :code:`[0, None, 4, 4]`\n\n\n    :param structure: nested list used as a template\n    :param values: values matched to the list\n    :return: flattened and filled list of values\n    \"\"\"\n    if not isinstance(structure, list):\n        if isinstance(values, list):\n            raise ValueError('There where to many nestings in the values')\n        return values\n    if not isinstance(values, list):\n        values = [values]\n    if len(structure) < len(values):\n        return flatten_together(collapse(structure, base_type=tuple, levels=1), values)\n    elif len(structure) > len(values):\n        # if structure has more elements we need to repeat value elements\n        values = values * (len(structure) // len(values))\n        if len(structure) != len(values):\n            raise ValueError('stuff does not match up')\n    return list(collapse([flatten_together(s, v) for s, v in zip(structure, values)], base_type=tuple))\n"
  },
  {
    "path": "openmodelica_microgrid_gym/util/obs_template.py",
    "content": "from typing import List, Union, Optional\nimport numpy as np\nfrom openmodelica_microgrid_gym.agents.util import MutableParams\n\n\nclass ObsTempl:\n    def __init__(self, varnames: List[str], simple_tmpl: Optional[List[Union[List[str], np.ndarray]]]):\n        \"\"\"\n        Internal dataclass to handle the conversion of dynamic observation templates for the StaticControlAgent\n\n        :param varnames: list of variable names\n        :param simple_tmpl: list of:\n                - list of strings\n                    - matching variable names of the state\n                    - must match self.obs_varnames\n                    - will be substituted by the values on runtime\n                    - will be passed as an np.array of floats to the controller\n                - np.array of floats (to be passed statically to the controller)\n                - a mixture of static and dynamic values in one parameter is not supported for performance reasons.\n                If None: self.fill() will not filter and return its input wrapped into a list\n        \"\"\"\n        idx = {v: i for i, v in enumerate(varnames)}\n        self._static_params = set()\n        self._data = []\n        self.is_tmpl_empty = simple_tmpl is None\n\n        if not self.is_tmpl_empty:\n            for i, tmpl in enumerate(simple_tmpl):\n                if isinstance(tmpl, np.ndarray) or isinstance(tmpl, MutableParams):\n                    # all np.ndarrays are considered static parameters\n                    self._static_params.add(i)\n                    self._data.append(tmpl)\n                else:\n                    # else we save the indices of the variables into an indexarray\n                    self._data.append(np.array([idx[varname] for varname in tmpl]))\n\n    def fill(self, obs: np.ndarray) -> List[np.ndarray]:\n        \"\"\"\n        generates a list of parameters by filling the dynamic values and passing the static values\n\n        :param obs: np.ndarray of values\n        :return: list of parameters\n        \"\"\"\n        if self.is_tmpl_empty:\n            return [obs]\n        params = []\n        for i, arr in enumerate(self._data):\n            # append static data or use dynamic data with indexing\n            params.append(arr if i in self._static_params else obs[arr])\n        return params\n"
  },
  {
    "path": "openmodelica_microgrid_gym/util/randproc.py",
    "content": "from typing import Type\n\nimport numpy as np\nfrom stochastic.processes import DiffusionProcess\nfrom stochastic.processes.base import BaseProcess\n\n\nclass RandProcess:\n    def __init__(self, process_cls: Type[BaseProcess], proc_kwargs=None, bounds=None, initial=0):\n        \"\"\"\n        wrapper around stochastic processes to allow easier integration\n\n        :param process_cls: class of the stochastic process\n        :param proc_kwargs: arguments passed to the class on initialization\n        :param bounds: boundaries of admissible values\n        :param initial: starting value of the process\n        :param gain: scale of the process output (of not set, results are between the clipped bounds)\n        \"\"\"\n        self.proc = process_cls(**(proc_kwargs or {}))\n        if bounds is None:\n            self.bounds = (-np.inf, np.inf)\n        else:\n            self.bounds = bounds\n\n        # will contain the previous value, hence initialized accordingly\n        self._last = initial\n        self._last_t = 0\n        self._reserve = None\n\n    def reset(self, initial):\n        self._last = initial\n        self._last_t = 0\n        self._reserve = None\n\n    def sample(self, t):\n        \"\"\"\n        calculates time differential and calculates the change in the outputs of the process\n        :param t: timestep\n        :return: value at the timestep\n        \"\"\"\n        # if not initial actually sample from processes otherwise return initial value\n        if t != self._last_t and t >= 0:\n            if self.reserve is not None:\n                self._last = self.reserve\n                self.reserve = None\n            self.proc.t = t - self._last_t\n            self._last_t = t\n            if isinstance(self.proc, DiffusionProcess):\n                self._last = np.clip(self.proc.sample(1, initial=self._last)[-1], *self.bounds).squeeze()\n            else:\n                self._last = np.clip(self._last + self.proc.sample(1)[-1], *self.bounds).squeeze()\n\n        return self._last\n\n    @property\n    def reserve(self):\n        \"\"\"\n        This variable is used to prepare for external loadsteps or other abrupt changes in the process' variables.\n        \"\"\"\n        return self._reserve\n\n    @reserve.setter\n    def reserve(self, v):\n        self._reserve = v\n"
  },
  {
    "path": "openmodelica_microgrid_gym/util/recorder.py",
    "content": "from typing import Sequence, List, Optional, Union\n\nimport pandas as pd\n\nfrom openmodelica_microgrid_gym.util.itertools_ import flatten\n\n\nclass StructuredMapping:\n    def __init__(self, cols: List[Union[List, str]] = None, data=None):\n        \"\"\"\n\n        :param cols: nested lists of strings providing column names and hierarchical structure\n        \"\"\"\n        if cols is None:\n            cols = []\n        self.cols = cols\n\n        self._data = data\n        \"\"\"internal field storing the history data\"\"\"\n\n    @property\n    def cols(self) -> List[str]:\n        \"\"\"\n        Columns of the History\n\n        :return: Flat list of the columns\n        \"\"\"\n        return self._cols\n\n    @cols.setter\n    def cols(self, val: List[Union[List, str]]):\n        \"\"\"\n        Columns of the History\n\n        :param val: Nested list of columns as string\n        \"\"\"\n        self._structured_cols = val\n        self._cols = flatten(val)\n\n    @property\n    def data(self):\n        return self._data\n\n    @property\n    def df(self):\n        # executing this conditionally only if _data is not a df is actually slower!!!\n        return pd.DataFrame([self._data], columns=self.cols)\n\n    def structured_cols(self, remaining_level: Optional[int] = 1) -> List[Union[List, str]]:\n        \"\"\"\n        Get columns with the specified amount of levels retained\n\n        :param remaining_level: number of levels to retain\n        \"\"\"\n        return flatten(self._structured_cols, remaining_level)\n\n\nclass EmptyHistory(StructuredMapping):\n    \"\"\"\n    Dummy history for recording data in the environment\n    This class will not actually store any data\n    \"\"\"\n\n    def reset(self):\n        \"\"\"\n        Removes all data, but keeps the columns.\n        \"\"\"\n        self._data = []\n\n    def pop(self):\n        \"\"\"\n        pop last item\n        :return:\n        \"\"\"\n        pass\n\n    def append(self, values: Sequence):\n        \"\"\"\n        Add new data sample to the history. The History class will determine how the data is updated\n\n        :param values: sequence of data entries\n        \"\"\"\n        pass\n\n    def last(self):\n        return self.df.tail(1).squeeze()\n\n    def __getitem__(self, item):\n        return self.df[item]\n\n\nclass SingleHistory(EmptyHistory):\n    \"\"\"\n    Single history that stores only the last added value\n    \"\"\"\n\n    def reset(self):\n        self._data = None\n\n    def pop(self):\n        val = self.last()\n        self.reset()\n        return val\n\n    def append(self, values: Sequence):\n        self._data = values\n\n    def last(self):\n        return self._data\n\n\nclass FullHistory(EmptyHistory):\n    \"\"\"\n    Full history that stores all data\n    \"\"\"\n\n    def reset(self):\n        self._data = []\n\n    def pop(self):\n        return self._data.pop(-1)\n\n    def append(self, values: Sequence):\n        self._data.append(list(values))\n\n    def last(self):\n        return self._data[-1]\n\n    @property\n    def df(self):\n        # executing this conditionally only if _data is not a df is actually slower!!!\n        return pd.DataFrame(self._data, columns=self.cols)\n"
  },
  {
    "path": "openmodelica_microgrid_gym/util/transforms.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Fri Jan 24 16:19:01 2020\n\n@author: jarren\n\nCommon static transforms used commonly in voltage/power inverter control systems\n\"\"\"\n\nimport numpy as np\n\n\ndef dq0_to_abc(dq0: np.ndarray, theta: float) -> np.ndarray:\n    \"\"\"\n    Transforms from DQ frame to the abc frame using the provided angle theta\n\n    :param dq0: The values in the dq0 reference frame\n    :param theta: The angle to transform [In Radians]\n\n    :return abc: The transformed space in the abc frame\n    \"\"\"\n    return dq0_to_abc_cos_sin(dq0, *cos_sin(theta))\n\n\ndef dq0_to_abc_cos_sin(dq0: np.ndarray, cos: float, sin: float) -> np.ndarray:\n    \"\"\"\n    Transforms from DQ frame to the abc frame using the provided cos-sin\n    This implementation tries to improve on the dq0Toabc transform by\n    utilising the provided cos-sin to minimise calls to calculate the\n    cossine etc\n\n    :param dq0: The values in the dq0 reference frame\n    :param cos: cos(theta)\n    :param sin: sin(theta)\n\n    :return abc: The transformed space in the abc frame\n    \"\"\"\n    a = (cos * dq0[0] -\n         sin * dq0[1] +\n         dq0[2])\n    # implements the cos(a-2pi/3) using cos (A+B) expansion etc\n    cos_shift = cos * (-0.5) - sin * (-0.866)\n    sin_shift = sin * (-0.5) + cos * (-0.866)\n    b = (cos_shift * dq0[0] -\n         sin_shift * dq0[1] +\n         dq0[2])\n\n    cos_shift = cos * (-0.5) - sin * (0.866)\n    sin_shift = sin * (-0.5) + cos * (0.866)\n    c = (cos_shift * dq0[0] -\n         sin_shift * dq0[1] +\n         dq0[2])\n\n    return np.array([a, b, c])\n\n\ndef dq0_to_abc_cos_sin_power_inv(dq0: np.ndarray, cos: float, sin: float) -> np.ndarray:\n    \"\"\"\n    Transforms from DQ frame to the abc frame using the provided cos-sin\n    This implementation tries to improve on the dq0Toabc transform by\n    utilising the provided cos-sin to minimise calls to calculate the\n    cossine etc.\n\n    Provides the Power Invariant transform (multiplied by\n    SQRT(3/2) = 1.224744871391589)\n\n    :param dq0: The values in the dq0 reference frame\n    :param cos: cos(theta)\n    :param sin: sin(theta)\n\n    :return abc: The transformed space in the abc frame\n    \"\"\"\n    return dq0_to_abc_cos_sin(dq0, cos, sin) * 1.224744871391589\n\n\ndef abc_to_dq0(abc: np.ndarray, theta: float) -> np.ndarray:\n    \"\"\"\n    Transforms from abc frame to the dq0 frame using the provided angle theta\n\n    :param abc: The values in the abc reference frame\n    :param theta: The angle [radians]\n\n    :return dq0: The transformed space in the abc frame\n    \"\"\"\n    return abc_to_dq0_cos_sin(abc, *cos_sin(theta))\n\n\ndef abc_to_dq0_cos_sin(abc: np.ndarray, cos: float, sin: float) -> np.ndarray:\n    \"\"\"\n    Transforms from abc frame to the dq0 frame using the provided cos-sin\n    This implementation tries to improve by utilising the provided cos-sin\n    to minimise calls to calculate the cossine etc\n\n    :param abc: The values in the abc reference frame\n    :param cos: cos(theta)\n    :param sin: sin(theta)\n\n    :return dq0: The transformed space in the abc frame\n    \"\"\"\n    # implements the cos(a-2pi/3) using cos (A+B) expansion etc\n    cos_shift_neg = cos * (-0.5) - sin * (-0.866)\n    sin_shift_neg = sin * (-0.5) + cos * (-0.866)\n    # implements the cos(a+2pi/3) using cos (A+B) expansion etc\n    cos_shift_pos = cos * (-0.5) - sin * 0.866\n    sin_shift_pos = sin * (-0.5) + cos * 0.866\n\n    # Calculation for d-axis aligned with A axis\n    d = (2 / 3) * (cos * abc[0] +\n                   cos_shift_neg * abc[1] +\n                   cos_shift_pos * abc[2])\n    q = (2 / 3) * (-sin * abc[0] -\n                   sin_shift_neg * abc[1] -\n                   sin_shift_pos * abc[2])\n\n    z = (1 / 3) * abc.sum(axis=0)\n\n    return np.array([d, q, z])\n\n\ndef abc_to_alpha_beta(abc: np.ndarray) -> np.ndarray:\n    \"\"\"\n    Transforms from abc frame to the alpha-beta frame\n\n    :param abc: The values in the abc reference frame\n    :return [alpha,beta]: The transformed alpha beta results\n    \"\"\"\n\n    alpha = (2 / 3) * (abc[0] - 0.5 * abc[1] - 0.5 * abc[2])\n    beta = (2 / 3) * (0.866 * abc[1] - 0.866 * abc[2])\n\n    return np.array([alpha, beta])\n\n\ndef cos_sin(theta: float) -> np.ndarray:\n    \"\"\"\n    Transforms from provided angle to the relevant cosine values\n\n    :param theta: The angle [In RADIANS]\n    :return: [alpha,beta] The resulting cosine\n    \"\"\"\n    return np.array([np.cos(theta), np.sin(theta)])\n\n\ndef inst_rms(arr: np.ndarray) -> float:\n    \"\"\"\n    Calculates the instantaneous RMS (root mean square) value of the input arr\n\n    :param arr: Input\n    :return: RMS value of the arr\n    \"\"\"\n    return np.linalg.norm(arr) / 1.732050807568877\n\n\ndef normalise_abc(abc: np.ndarray) -> np.ndarray:\n    \"\"\"\n    Normalises the abc magnitudes to the RMS of the 3 magnitudes\n    Determines the instantaneous RMS value of the 3 waveforms\n\n    :param abc: Three phase magnitudes input\n    :return abc_norm: abc result normalised to [-1,1]\n    \"\"\"\n    # Get the magnitude of the waveforms to normalise the PLL calcs\n    mag = inst_rms(abc)\n    if mag != 0:\n        abc = abc / mag\n\n    return abc\n\n\ndef inst_power(varr: np.ndarray, iarr: np.ndarray) -> float:\n    \"\"\"\n    Calculates the instantaneous power\n\n    :param varr: voltage\n    :param iarr: current\n    :return: instantaneous power\n    \"\"\"\n    return varr @ iarr\n\n\ndef inst_reactive(varr: np.ndarray, iarr: np.ndarray):\n    \"\"\"\n    Calculates the instantaneous reactive power\n\n    :param varr: voltage\n    :param iarr: current\n    :return: instantaneous reactive power\n    \"\"\"\n    # vline = np.array([varr[1] - varr[2], varr[2] - varr[0], varr[0] - varr[1]]) # Linevoltages cal using np.roll\n    return -0.5773502691896258 * (np.roll(varr, -1) - np.roll(varr, -2)) @ iarr\n"
  },
  {
    "path": "requirements.txt",
    "content": "## created with pipreqs\ngym>=0.15.3\nnumpy>=1.17.2\nmatplotlib>=3.1.1\nscipy>=1.3.1\npandas>=1.0.1\ntqdm>=4\nmore_itertools>=7\nnumexpr>=2.7.1\ntables>=3.6.1\nstochastic>=0.6.0\n\npyyaml~=5.4\n\nPyFMI>=2.5\n\nsafeopt>=0.16\nGPy>=1.9.9\n\nfuture~=0.18.2\npytest~=5.4.3\nsetuptools~=47.1.1\n"
  },
  {
    "path": "requirements_dev.txt",
    "content": "bump2version==0.5.11\nwatchdog==0.9.0\n\npytest>=5.2.2\npytest-cov\npytest-runner==5.1\ntables>=3.4.1\nsafeopt>=0.16\nGPy>=1.9.9\n\nSphinx==3.1\nsphinx-autodoc-typehints>=1.11\nsphinx_rtd_theme\n\ntwine==1.14.0\n"
  },
  {
    "path": "setup.cfg",
    "content": "[bumpversion]\ncurrent_version = 0.4.0\ncommit = True\ntag = True\n\n[bumpversion:file:setup.py]\nsearch = version='{current_version}'\nreplace = version='{new_version}'\n\n[bumpversion:file:openmodelica_microgrid_gym/__init__.py]\nsearch = __version__ = '{current_version}'\nreplace = __version__ = '{new_version}'\n\n[bdist_wheel]\nuniversal = 1\n\n[aliases]\ntest = pytest\n\n[tool:pytest]\ncollect_ignore = ['setup.py']\ntestpaths = \n\ttests\naddopts = \n\t--cov=openmodelica_microgrid_gym\n\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"The setup script.\"\"\"\n\nfrom setuptools import setup, find_packages\n\nwith open('README.rst') as readme_file:\n    readme = readme_file.read()\n\nwith open('HISTORY.rst') as history_file:\n    history = history_file.read()\n\nwith open('requirements.txt', 'r') as f:\n    requirements = f.read().splitlines()\n\nsetup_requirements = ['pytest-runner']\n\ntest_requirements = ['pytest>=3', 'tables>=3.4.1', 'safeopt>=0.16', 'GPy>=1.9.9']\n\nsetup(\n    author=\"LEA - Uni Paderborn\",\n    author_email='upblea@mail.upb.de',\n    python_requires='>=3.7',\n    classifiers=[\n        'Development Status :: 3 - Alpha',\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',\n        'Natural Language :: English',\n        'Framework :: Sphinx',\n        'Topic :: Scientific/Engineering',\n        'Programming Language :: Python :: 3',\n        'Programming Language :: Python :: 3.7',\n        'Programming Language :: Python :: 3.8',\n        'Operating System :: Microsoft :: Windows',\n        'Operating System :: POSIX :: Linux'        \n    ],\n    description=\"OpenModelica Microgrid Gym\",\n    install_requires=requirements,\n    license=\"GNU General Public License v3\",\n    long_description=readme + '\\n\\n' + history,\n    include_package_data=True,\n    keywords='openmodelica_microgrid_gym',\n    name='openmodelica_microgrid_gym',\n    packages=find_packages(include=['openmodelica_microgrid_gym', 'openmodelica_microgrid_gym.*']),\n    setup_requires=setup_requirements,\n    test_suite='tests',\n    tests_require=test_requirements,\n    extras_require={'examples': ['safeopt>=0.16', 'GPy>=1.9.9']},\n    url='https://github.com/upb-lea/openmodelica-microgrid-gym',\n    project_urls={\n        \"Documentation\": \"https://upb-lea.github.io/openmodelica-microgrid-gym/\",\n        \"Source Code\": \"https://github.com/upb-lea/openmodelica-microgrid-gym\",\n    },\n    version='0.4.0',\n    zip_safe=False,\n)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/aux_ctl/test_base.py",
    "content": "import numpy as np\nfrom pytest import approx\n\nfrom openmodelica_microgrid_gym.aux_ctl.base import LimitLoadIntegral\n\n\ndef test_limit_load_integral():\n    dt = .05\n    freq = 2\n    i_lim = 5\n    i2t = LimitLoadIntegral(dt, freq, i_lim=i_lim, i_nom=i_lim / 5)\n    size = int(1 / freq / dt / 2)\n    assert len(i2t._buffer) == size\n\n    i2t.reset()\n    seq = [5, 4]\n    for i in seq:\n        i2t.step(i)\n    integral = i2t.integral\n    assert integral == (np.power(seq, 2) * dt).sum()\n    assert i2t.risk() == approx(.3)\n    for i in [5, 4, 5, 5, 5, 5, 5, 5] + [0] * size + seq:\n        i2t.step(i)\n    integral = i2t.integral\n    assert integral == (np.power(seq, 2) * dt).sum()\n"
  },
  {
    "path": "tests/aux_ctl/test_inverter_control.py",
    "content": "import numpy as np\nimport pytest\nfrom pytest import approx\n\nfrom openmodelica_microgrid_gym.aux_ctl import *\n\n\n@pytest.fixture\ndef seed():\n    np.random.seed(1)\n\n\n@pytest.fixture(scope='module')\ndef droop_par():\n    return InverseDroopParams(1, 4)\n\n\n@pytest.fixture(scope='module')\ndef pll_par():\n    return PLLParams(2, 3, (-1, 1))\n\n\ndef test_step(seed, pll_par, droop_par):\n    ctl = MultiPhaseABCPIPIController(pll_par, pll_par, droop_par, droop_par, ts_sim=1)\n    ctl.reset()\n    ctl.prepare(np.random.random(3), np.random.random(3))\n    assert ctl.step() == approx([-1.0, -1.0, -1.0])\n\n\ndef test_step2(seed, droop_par, pll_par):\n    ctl = MultiPhaseDQCurrentController(pll_par, pll_par, 1, droop_par, droop_par, ts_sim=1)\n    ctl.reset()\n    ctl.prepare(np.random.random(3), np.random.random(3), np.random.random(3))\n    mv = ctl.step()\n    assert mv == approx([1, -1, 0.18577202])\n\n\ndef test_step3(seed, droop_par, pll_par):\n    ctl = MultiPhaseDQ0PIPIController(pll_par, pll_par, droop_par, droop_par, ts_sim=3)\n    ctl.reset()\n    ctl.prepare(np.random.random(3), np.random.random(3))\n    mv = ctl.step()\n    assert mv == approx([-1, -1, 0.3040774])\n"
  },
  {
    "path": "tests/helpers.py",
    "content": "import numpy\n\n\ndef nested_arrays_equal(a, b):\n    \"\"\"\n    Function to compare two (nested) lists/tuples of numpy arrays a and b\n    from: https://stackoverflow.com/questions/31662528/compare-two-nested-lists-tuples-of-numpy-arrays/31662874#31662874\n    \"\"\"\n\n    if isinstance(a, (list, tuple)) and isinstance(b, type(a)):\n        for aa, bb in zip(a, b):\n            if not nested_arrays_equal(aa, bb):\n                return False  # Short circuit\n        return True\n    else:  # numpy arrays\n        return numpy.all(a == b)\n"
  },
  {
    "path": "tests/net/test_net.py",
    "content": "import pytest\n\nfrom openmodelica_microgrid_gym.net import Network\n\n\ndef test_load():\n    Network.load('net/net_valid.yaml')\n    assert True\n\n\ndef test_load_dup_inputs():\n    with pytest.raises(ValueError):\n        Network.load('net/net_dupinputs.yaml')\n"
  },
  {
    "path": "tests/test__util_plot.py",
    "content": "import pytest\n\nfrom openmodelica_microgrid_gym.env import PlotTmpl\n\nv = [['a', 'b'], ['c', 'd']]\ntmpl = PlotTmpl(v, color=[None, ['C2', 'C1']], style=[None, '--'])\n\n\n@pytest.mark.parametrize('i,o', [[[k for k in PlotTmpl(v)],\n                                  [('a', dict(c='C1')), ('b', dict(c='C2')), ('c', dict(c='C1')), ('d', dict(c='C2'))]],\n                                 [[k for k in PlotTmpl(v, c=[None, ['C2', 'C1']])],\n                                  [('a', dict(c='C1')), ('b', dict(c='C2')), ('c', dict(c='C2')), ('d', dict(c='C1'))]],\n                                 [[k for k in PlotTmpl(v, color=[None, ['C2', 'C1']])],\n                                  [('a', dict(color='C1')), ('b', dict(color='C2')),\n                                   ('c', dict(color='C2')), ('d', dict(color='C1'))]],\n                                 [tmpl[2],\n                                  ('c', dict(color='C2', style='--'))],\n                                 [tmpl[1],\n                                  ('b', dict(color='C2'))]])\ndef test_plot_tmpl(i, o):\n    assert i == o\n"
  },
  {
    "path": "tests/test_modelica.py",
    "content": "\"\"\"\nTesting the Environment using simple action inputs and verifying the simulation result.\n\"\"\"\n\nimport gym\nimport numpy as np\nimport pytest\nfrom pytest import approx\n\n\n@pytest.fixture\ndef env():\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   viz_mode=None,\n                   model_path='omg_grid/test.fmu',\n                   net='net/net_test.yaml')\n    return env\n\n\n@pytest.fixture\ndef initialized_env():\n    initialized_env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                               viz_mode=None,\n                               model_path='omg_grid/test.fmu',\n                               net='net/net_test.yaml', model_params={\n            'lc1.capacitor1.v': lambda t: 200 if t == -1 else None,\n            'lc1.capacitor2.v': lambda t: 200 if t == -1 else None,\n            'lc1.capacitor3.v': lambda t: 200 if t == -1 else None,\n            'lc1.inductor1.i': lambda t: 5 if t == -1 else None,\n            'lc1.inductor2.i': lambda t: 5 if t == -1 else None,\n            'lc1.inductor3.i': lambda t: 5 if t == -1 else None,\n        })\n    return initialized_env\n\n\ndef test_reset(env):\n    assert env.reset() == approx(\n        [0., 0., 0., 0., 0., 0., 0., 0., 0., 325.10861867, -153.70643068, -171.40218799, 0.0314159265,\n         0., 0., 0., 0., 0., 0., 0., 0., 0.])\n\n\ndef test_step(env):\n    np.random.seed(1)\n    env.reset()\n    obs, r, done, _ = env.step(np.random.random(6))\n    assert obs == approx([172.9788, 285.5633, 3.2967, 35.4845, 61.5703, -0.0585,\n                          0.0000, 0.0000, 0.0000, 324.6152, -144.6233, -179.9919, 0.0628318531,\n                          142.6508, 87.4849, 39.0866, 25.3610, 11.9275, 7.8399,\n                          0.0000, 0.0000, 0.0000], 1e-3)\n    assert r == 1\n    assert not done\n\n\ndef test_proper_reset(env):\n    # Test using initial values in env which are zero\n    np.random.seed(1)\n    actions = np.random.random((100, 6))\n    env.reset()\n    for a in actions:\n        env.step(a)\n    state = str(env) + str(env.history.df)\n\n    env.reset()\n    for a in actions:\n        env.step(a)\n    assert state == str(env) + str(env.history.df)\n\n\ndef test_proper_reset_init_env(initialized_env):\n    # Test using initial values in env which are not zero\n    np.random.seed(1)\n    actions = np.random.random((100, 6))\n    initialized_env.reset()\n    for a in actions:\n        initialized_env.step(a)\n    state = str(initialized_env) + str(initialized_env.history.df)\n\n    initialized_env.reset()\n    for a in actions:\n        initialized_env.step(a)\n    assert state == str(initialized_env) + str(initialized_env.history.df)\n\n\ndef test_reset_after_init(initialized_env):\n    \"\"\"\n    Check reset. Are the values to be reset (see initialized_env.make) used in first step?\n    \"\"\"\n    np.random.seed(1)\n    initialized_env.reset()\n\n    assert all(initialized_env.model.obs[0:6] == [200, 200, 200, 5, 5, 5])\n"
  },
  {
    "path": "tests/test_pd_convert.py",
    "content": ""
  },
  {
    "path": "tests/test_recorder.py",
    "content": "import numpy as np\nimport pandas as pd\n\nfrom openmodelica_microgrid_gym.util import FullHistory\n\n\ndef test__append():\n    rec = FullHistory(['a b c'.split()])\n    rec.reset()\n    rec.append(np.array([1, 2, 3]))\n    rec.append([3, 3, 3])\n\n    assert rec.df.equals(pd.DataFrame([dict(a=1, b=2, c=3), dict(a=3, b=3, c=3)]))\n"
  },
  {
    "path": "tests/test_runner.py",
    "content": "\"\"\"\nThe Tests in this file are high level integration tests.\nBased on a pre-packed and fixed application example it is verified that the simulated state and action trajectories match expected values. \nIf the simulated system behavior changes against baseline a problem within the software toolchain is likely. \n\"\"\"\n\nimport gym\nimport numpy as np\nimport pandas as pd\nimport pytest\nfrom pytest import approx\n\nfrom openmodelica_microgrid_gym import Runner, Agent\nfrom openmodelica_microgrid_gym.agents import StaticControlAgent\nfrom openmodelica_microgrid_gym.agents.util import MutableFloat\nfrom openmodelica_microgrid_gym.aux_ctl import *\nfrom openmodelica_microgrid_gym.net import Network\nfrom openmodelica_microgrid_gym.util import flatten\n\n\n@pytest.fixture\ndef agent():\n    delta_t = 1e-4\n    nomFreq = 50\n    nomVoltPeak = 230 * 1.414\n    iLimit = 30\n    DroopGain = 40000.0  # W/Hz\n    QDroopGain = 1000.0  # VAR/V\n\n    mutable_params = dict(voltP=MutableFloat(25e-3), voltI=MutableFloat(60))\n\n    ctrl = []\n    # Voltage PI parameters for the current sourcing inverter\n    voltage_dqp_iparams = PI_params(kP=mutable_params['voltP'], kI=mutable_params['voltI'], limits=(-iLimit, iLimit))\n    # Current PI parameters for the voltage sourcing inverter\n    current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1))\n    # Droop of the active power Watt/Hz, delta_t\n    droop_param = DroopParams(DroopGain, 0.005, nomFreq)\n    # Droop of the reactive power VAR/Volt Var.s/Volt\n    qdroop_param = DroopParams(QDroopGain, 0.002, nomVoltPeak)\n    ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param,\n                                            ts_sim=delta_t, name='master'))\n\n    # Discrete controller implementation for a DQ based Current controller for the current sourcing inverter\n    # Current PI parameters for the current sourcing inverter\n    current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1))\n    # PI params for the PLL in the current forming inverter\n    pll_params = PLLParams(kP=10, kI=200, limits=(-10000, 10000), f_nom=nomFreq)\n    # Droop of the active power Watts/Hz, W.s/Hz\n    droop_param = InverseDroopParams(DroopGain, 0, nomFreq, tau_filt=0.04)\n    # Droop of the reactive power VAR/Volt Var.s/Volt\n    qdroop_param = InverseDroopParams(100, 0, nomVoltPeak, tau_filt=0.01)\n    ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, iLimit, droop_param, qdroop_param,\n                                              ts_sim=delta_t, name='slave'))\n\n    # validate that parameters can be changed later on\n    agent = StaticControlAgent(ctrl, {'master': [[f'lc1.inductor{i + 1}.i' for i in range(3)],\n                                                 [f'lc1.capacitor{i + 1}.v' for i in range(3)]],\n                                      'slave': [[f'lcl1.inductor{i + 1}.i' for i in range(3)],\n                                                [f'lcl1.capacitor{i + 1}.v' for i in range(3)],\n                                                np.zeros(3)]})\n    return mutable_params, agent\n\n\n@pytest.fixture\ndef agent_undersample():\n    delta_t = 1e-4\n    nomFreq = 50\n    nomVoltPeak = 230 * 1.414\n    iLimit = 30\n    DroopGain = 40000.0  # W/Hz\n    QDroopGain = 1000.0  # VAR/V\n\n    mutable_params = dict(voltP=MutableFloat(25e-3), voltI=MutableFloat(60))\n\n    ctrl = []\n    # Voltage PI parameters for the current sourcing inverter\n    voltage_dqp_iparams = PI_params(kP=mutable_params['voltP'], kI=mutable_params['voltI'], limits=(-iLimit, iLimit))\n    # Current PI parameters for the voltage sourcing inverter\n    current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1))\n    # Droop of the active power Watt/Hz, delta_t\n    droop_param = DroopParams(DroopGain, 0.005, nomFreq)\n    # Droop of the reactive power VAR/Volt Var.s/Volt\n    qdroop_param = DroopParams(QDroopGain, 0.002, nomVoltPeak)\n    ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param,\n                                            ts_sim=delta_t, ts_ctrl=2 * delta_t, name='master'))\n\n    # Discrete controller implementation for a DQ based Current controller for the current sourcing inverter\n    # Current PI parameters for the current sourcing inverter\n    current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1))\n    # PI params for the PLL in the current forming inverter\n    pll_params = PLLParams(kP=10, kI=200, limits=(-10000, 10000), f_nom=nomFreq)\n    # Droop of the active power Watts/Hz, W.s/Hz\n    droop_param = InverseDroopParams(DroopGain, 0, nomFreq, tau_filt=0.04)\n    # Droop of the reactive power VAR/Volt Var.s/Volt\n    qdroop_param = InverseDroopParams(100, 0, nomVoltPeak, tau_filt=0.01)\n    ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, iLimit, droop_param, qdroop_param,\n                                              ts_sim=delta_t, ts_ctrl=5 * delta_t, name='slave'))\n\n    # validate that parameters can be changed later on\n    agent = StaticControlAgent(ctrl, {'master': [[f'lc1.inductor{i + 1}.i' for i in range(3)],\n                                                 [f'lc1.capacitor{i + 1}.v' for i in range(3)]],\n                                      'slave': [[f'lcl1.inductor{i + 1}.i' for i in range(3)],\n                                                [f'lcl1.capacitor{i + 1}.v' for i in range(3)],\n                                                np.zeros(3)]})\n    return agent\n\n\n@pytest.fixture()\ndef env():\n    net = Network.load('net/net_test.yaml')\n    env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n                   viz_mode=None,\n                   model_path='omg_grid/test.fmu',\n                   max_episode_steps=100,\n                   net=net)\n\n    return env, net.in_vars(), flatten(net.out_vars())\n\n\ndef test_main(agent, env):\n    env, _, out_params = env\n    runner = Runner(agent[1], env)\n    runner.run(1)\n    # env.history.df.to_hdf('tests/test_main.hd5', 'hist')\n    df = env.history.df.head(100)\n    df = df.reindex(sorted(df.columns), axis=1)\n    df2 = pd.read_hdf('tests/test_main.hd5', 'hist').head(100)  # noqa\n    df2 = df2.reindex(sorted(df2.columns), axis=1)\n    assert df[out_params].to_numpy() == approx(df2[out_params].to_numpy(), 5e-2)\n\n\ndef test_main_paramchange(agent, env):\n    params, agent = agent\n    env, _, out_params = env\n    runner = Runner(agent, env)\n    params['voltP'].val = 4\n    runner.run(1)\n\n    # env.history.df.to_hdf('tests/test_main2.hd5', 'hist')\n    df = env.history.df.head(20)\n    df = df.reindex(sorted(df.columns), axis=1)\n    df2 = pd.read_hdf('tests/test_main.hd5', 'hist').head(20)  # noqa\n    df2 = df2.reindex(sorted(df2.columns), axis=1)\n    assert df[out_params].to_numpy() != approx(df2[out_params].to_numpy(), 5e-3)\n\n    df2 = pd.read_hdf('tests/test_main2.hd5', 'hist').head(20)  # noqa\n    df2 = df2.reindex(sorted(df2.columns), axis=1)\n    assert df[out_params].to_numpy() == approx(df2[out_params].to_numpy(), 5e-3)\n\n\ndef test_main_undersample(agent_undersample, env):\n    env, _, out_params = env\n    runner = Runner(agent_undersample, env)\n    runner.run(1)\n\n    # env.history.df.to_hdf('tests/test_main4.hd5', 'hist')\n    df = env.history.df.head(11)\n    df = df.reindex(sorted(df.columns), axis=1)\n    df2 = pd.read_hdf('tests/test_main4.hd5', 'hist').head(11)  # noqa\n    df2 = df2.reindex(sorted(df2.columns), axis=1)\n    assert df[out_params].to_numpy() == approx(df2[out_params].to_numpy(), 5e-2)\n\n\ndef test_simpleagent(env):\n    np.random.seed(1)\n    env, inputs, out_params = env\n\n    class RndAgent(Agent):\n        def act(self, obs: pd.Series) -> np.ndarray:\n            return np.random.random(len(inputs))\n\n    agent = RndAgent()\n    runner = Runner(agent, env)\n    runner.run(1)\n\n    # env.history.df.to_hdf('tests/test_main3.hd5', 'hist')\n    df = env.history.df.head(23)\n    df = df.reindex(sorted(df.columns), axis=1)\n    df2 = pd.read_hdf('tests/test_main3.hd5', 'hist').head(23)  # noqa\n    df2 = df2.reindex(sorted(df2.columns), axis=1)\n    assert df[out_params].to_numpy() == approx(df2[out_params].to_numpy(), 5e-4)\n"
  },
  {
    "path": "tests/test_transforms.py",
    "content": "import numpy as np\nimport pytest\nfrom pytest import approx\n\nfrom openmodelica_microgrid_gym.util import *\n\n\n@pytest.fixture\ndef seed():\n    np.random.seed(1)\n\n\ndef test_inst_reactive(seed):\n    assert inst_reactive(np.random.random(3), np.random.random(3)) == approx(-0.07421999471678885)\n\n\ndef test_inst_power(seed):\n    assert inst_power(np.random.random(3), np.random.random(3)) == approx(0.23180175944821654)\n\n\ndef test_inst_rms(seed):\n    assert inst_rms(np.random.random(3)) == approx(0.4805464741106228)\n\n\ndef test_dq0_to_abc(seed):\n    assert dq0_to_abc(np.random.random(3), np.random.random(1)) == approx([0.18374714, 0.61133519, -0.79473921])\n\n\ndef test_dq0_to_abc_cos_sin(seed):\n    assert dq0_to_abc_cos_sin(np.random.random(3), *np.random.random(2)) == approx([0.02048185, 0.23152558, -0.2516643])\n\n\ndef test_dq0_to_abc_cos_sin_power_inv(seed):\n    assert dq0_to_abc_cos_sin_power_inv(np.random.random(3), *np.random.random(2)) == approx(\n        [0.025085037842289073, 0.28355976715193565, -0.3082245650813462])\n\n\ndef test_abc_to_dq0(seed):\n    assert abc_to_dq0(np.random.random(3), np.random.random(1)) == approx([0.15995477, 0.38566723, 0.37915362432069233])\n\n\ndef test_abc_to_dq0_cos_sin(seed):\n    assert abc_to_dq0_cos_sin(np.random.random(3), *np.random.random(2)) == approx([0.07247014, 0.12015287, 0.37915362])\n\n\ndef test_abc_to_alpha_beta(seed):\n    assert abc_to_alpha_beta(np.random.random(3)) == approx([0.03786838, 0.41580131])\n"
  },
  {
    "path": "tests/util/__init__.py",
    "content": ""
  },
  {
    "path": "tests/util/test_fastqueue.py",
    "content": "import numpy as np\nimport pytest\nfrom pytest import approx\n\nfrom openmodelica_microgrid_gym.util import Fastqueue\n\n\ndef test_fastqueue_1_element():\n    np.random.seed(1)\n    test_queue1d = Fastqueue(1)\n    test_queue1d.clear()\n    first_val = np.random.uniform()\n    test_queue1d.shift(first_val)\n    assert first_val == test_queue1d.shift(np.random.uniform())\n\n\ndef test_fastqueue_2_element():\n    np.random.seed(1)\n    test_queue1d = Fastqueue(2)\n    test_queue1d.clear()\n    first_val = np.random.uniform()\n    test_queue1d.shift(first_val)\n    test_queue1d.shift(np.random.uniform())\n    assert first_val == test_queue1d.shift(np.random.uniform())\n\n\ndef test_fastqueue_3_elem():\n    np.random.seed(1)\n    test_queue1d = Fastqueue(3)\n    test_queue1d.clear()\n    first_val = np.random.uniform()\n    test_queue1d.shift(first_val)\n    test_queue1d.shift(np.random.uniform())\n    test_queue1d.shift(np.random.uniform())\n    assert first_val == test_queue1d.shift(np.random.uniform())\n\n\ndef test_fastqueue2d():\n    np.random.seed(1)\n    test_queue2d = Fastqueue(3, 2)\n    test_queue2d.clear()\n    first_val = np.random.uniform(size=2)\n    test_queue2d.shift(first_val)\n    test_queue2d.shift(np.random.uniform(size=2))\n    test_queue2d.shift(np.random.uniform(size=2))\n    assert first_val == approx(test_queue2d.shift(np.random.uniform(size=2)))\n\n\ndef test_fastqueue_initialize():\n    q = Fastqueue(5)\n    with pytest.raises(RuntimeError):\n        q.shift(3)\n\n\ndef test_fastqueue2d_not_random():\n    np.random.seed(1)\n    test_queue2d = Fastqueue(3, 2)\n    test_queue2d.clear()\n    first_val = np.array([1, 2])\n    test_queue2d.shift(first_val)\n    test_queue2d.shift(np.random.uniform(size=2))\n    test_queue2d.shift(np.random.uniform(size=2))\n    assert np.array([1, 2]) == approx(test_queue2d.shift(np.random.uniform(size=2)))\n"
  },
  {
    "path": "tests/util/test_flattendict.py",
    "content": "import numpy as np\nimport pandas as pd\nimport pytest\n\nfrom openmodelica_microgrid_gym.util import fill_params, flatten, nested_map, nested_depth\n\nconf = {\n    'lc1': [\n        ['inductor1.i', 'inductor2.i', 'inductor3.i'],\n        ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']],\n    'lcl1':\n        [['inductor1.i', 'inductor2.i', 'inductor3.i'],\n         ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']]}\nconf2 = {\n    'lc1': [\n        ['inductor1.i', 'inductor2.i', 'inductor3.i'],\n        ['capacitor1.v', 'capacitor2.v'],\n        ['capacitor3.v']],\n    'lcl1': conf['lcl1']}\n\nconf3 = {\n    'lc1': [\n        ['inductor1.i', 'inductor2.i', 'inductor3.i'],\n        ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']],\n    'lcl1':\n        [['inductor1.i', 'inductor2.i', 'inductor3.i'],\n         ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']],\n    'pll':\n        ['add_freq_nom_delta_f.y']}\n\nresult_1 = [['lc1.inductor1.i', 'lc1.inductor2.i', 'lc1.inductor3.i'],\n            ['lc1.capacitor1.v', 'lc1.capacitor2.v', 'lc1.capacitor3.v'],\n            ['lcl1.inductor1.i', 'lcl1.inductor2.i', 'lcl1.inductor3.i'],\n            ['lcl1.capacitor1.v', 'lcl1.capacitor2.v', 'lcl1.capacitor3.v']]\nresult_1_2 = [['lc1.inductor1.i', 'lc1.inductor2.i', 'lc1.inductor3.i'],\n              ['lc1.capacitor1.v', 'lc1.capacitor2.v'],\n              ['lc1.capacitor3.v'],\n              ['lcl1.inductor1.i', 'lcl1.inductor2.i', 'lcl1.inductor3.i'],\n              ['lcl1.capacitor1.v', 'lcl1.capacitor2.v', 'lcl1.capacitor3.v']]\nresult_0 = ['lc1.inductor1.i', 'lc1.inductor2.i', 'lc1.inductor3.i',\n            'lc1.capacitor1.v', 'lc1.capacitor2.v', 'lc1.capacitor3.v',\n            'lcl1.inductor1.i', 'lcl1.inductor2.i', 'lcl1.inductor3.i',\n            'lcl1.capacitor1.v', 'lcl1.capacitor2.v', 'lcl1.capacitor3.v']\nresult_3_0 = result_0 + ['pll.add_freq_nom_delta_f.y']\n\n\n@pytest.mark.parametrize('i,o', [[[conf], result_0], [[result_1], result_0], [[conf2], result_0], [[conf3], result_3_0],\n                                 [[conf, 1], result_1], [[result_1, 1], result_1], [[conf2, 1], result_1_2],\n                                 [[result_1_2, None], result_1_2]])\ndef test_flatten(i, o):\n    assert flatten(*i) == o\n\n\ndef test_nested_map():\n    assert nested_map(lambda x: 'p' + x, ['a', 'b', 'c']) == ['pa', 'pb', 'pc']\n\n\ndef test_nested_map1():\n    assert np.array_equal(nested_map(len, np.array(['a', 'b', 'c'])), np.array([1, 1, 1]))\n\n\n@pytest.mark.parametrize('i,o', [[1, 0], [[1], 1], [[], 1], [[[], 1], 2], [result_1, 2], [result_1_2, 2]])\ndef test_nested_depth(i, o):\n    assert nested_depth(i) == o\n\n\n@pytest.mark.parametrize('tmpl,data,result',\n                         [[dict(a=['a', 'b', 'c']), pd.Series(dict(a=1, b=2, c=3)), dict(a=[1, 2, 3])],\n                          [dict(a=[np.array(['a', 'b', 'c']), np.array(['d', 'b', 'c']), 1]),\n                           pd.Series(dict(a=1, b=2, c=3, d=4)),\n                           dict(a=[np.array([1., 2., 3.]), np.array([4., 2., 3.]), 1])]])\ndef test_fill_params(tmpl, data, result):\n    # properly testing datastructures with nested numpy arrays is complicated because of \"ambiguous truthness\"\n    assert str(fill_params(tmpl, data)) == str(result)\n"
  },
  {
    "path": "tests/util/test_itertools_flatten_together.py",
    "content": "import pytest\n\nfrom openmodelica_microgrid_gym.util.itertools_ import flatten_together\n\n\n@pytest.mark.parametrize('i,o', [[([1, 2, 3], [4, 5, 6]), [4, 5, 6]],\n                                 [([1, 1], [0]), [0, 0]],\n                                 [([[1, 1]], [0]), [0, 0]],\n                                 [([[3, 2], [4]], [2, 3]), [2, 2, 3]],\n                                 [([[3, 2], 4], [2, 3]), [2, 2, 3]],\n                                 [([[3, 2], 4], 13), [13, 13, 13]],\n                                 ])\ndef test_flatten_together(i, o):\n    assert flatten_together(*i) == o\n\n\ndef test_flatten_together_negative():\n    with pytest.raises(ValueError):\n        # params don't match up\n        flatten_together([[3, 3], [3, 3], [3, 3]], [[1], [2]])\n\n\ndef test_flatten_together_negative2():\n    with pytest.raises(ValueError):\n        # to many nestings in values\n        flatten_together([4, 4], [[1], [3]])\n"
  },
  {
    "path": "tests/util/test_obs_template.py",
    "content": "import numpy as np\nimport pytest\n\nfrom openmodelica_microgrid_gym.util import ObsTempl\nfrom tests.helpers import nested_arrays_equal\n\n\n@pytest.mark.parametrize('i,o', [[list('ab'), [np.array([1]), np.array([2])]],\n                                 [list('a'), [np.array([1])]],\n                                 [[['a', 'b']], [np.array([1, 2])]],\n                                 [[['a', 'b'], ['c']], [np.array([1, 2]), np.array([3])]],\n                                 [None, [np.array([1, 2, 3])]]\n                                 ])\ndef test_obs_templ(i, o):\n    tmpl = ObsTempl(list('abc'), i)\n    assert nested_arrays_equal(o, tmpl.fill(np.array([1, 2, 3])))\n"
  }
]