Repository: upb-lea/openmodelica-microgrid-gym Branch: master Commit: a81e522594c4 Files: 242 Total size: 977.8 KB Directory structure: gitextract_vu3ilfpd/ ├── .github/ │ └── workflows/ │ └── build_and_test.yml ├── .gitignore ├── AUTHORS.rst ├── CONTRIBUTING.rst ├── HISTORY.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── docs/ │ ├── JOSS/ │ │ ├── paper.bib │ │ └── paper.md │ ├── Makefile │ ├── api/ │ │ ├── omg.agents.agent.rst │ │ ├── omg.agents.episodic.rst │ │ ├── omg.agents.rst │ │ ├── omg.agents.safeopt.rst │ │ ├── omg.agents.staticctrl.rst │ │ ├── omg.agents.util.rst │ │ ├── omg.aux_ctl.base.rst │ │ ├── omg.aux_ctl.droop_controllers.rst │ │ ├── omg.aux_ctl.filter.rst │ │ ├── omg.aux_ctl.inverter_controllers.rst │ │ ├── omg.aux_ctl.observers.rst │ │ ├── omg.aux_ctl.params.rst │ │ ├── omg.aux_ctl.pi_controllers.rst │ │ ├── omg.aux_ctl.rst │ │ ├── omg.env.modelica.rst │ │ ├── omg.env.plot.rst │ │ ├── omg.env.plotmanager.rst │ │ ├── omg.env.pyfmi.rst │ │ ├── omg.env.rst │ │ ├── omg.execution.callbacks.rst │ │ ├── omg.execution.rst │ │ ├── omg.execution.runner.rst │ │ ├── omg.net.base.rst │ │ ├── omg.net.components.rst │ │ ├── omg.net.rst │ │ ├── omg.util.fastqueue.rst │ │ ├── omg.util.itertools_.rst │ │ ├── omg.util.randproc.rst │ │ ├── omg.util.recorder.rst │ │ ├── omg.util.rst │ │ └── omg.util.transforms.rst │ ├── conf.py │ ├── index.rst │ ├── make.bat │ └── parts/ │ └── user_guide/ │ ├── OpenModelica.rst │ ├── Pythoncode.rst │ ├── controller_tuning.rst │ ├── examples/ │ │ ├── basic_agent.rst │ │ ├── creating_env.rst │ │ ├── plotting.rst │ │ ├── single_inverter_current_control_safe_opt.rst │ │ └── two_inverter_static_droop_control.rst │ ├── examples.rst │ ├── fmu.rst │ └── getting_started.rst ├── examples/ │ ├── basic_env.py │ ├── basic_env_norm.py │ ├── network_callbacks.py │ ├── plotting.py │ ├── simple_agent.py │ ├── single_inverter_current_control_safe_opt.py │ ├── single_inverter_voltage_current_control_safe_opt.py │ ├── stocastic_load.py │ ├── two_inverter_droop_safe_opt.py │ └── two_inverter_static_droop_control.py ├── experiments/ │ ├── model_validation/ │ │ ├── env/ │ │ │ ├── physical_testbench.py │ │ │ ├── rewards.py │ │ │ ├── stochastic_components.py │ │ │ └── testbench_voltage_ctrl.py │ │ ├── execution/ │ │ │ ├── monte_carlo_runner.py │ │ │ └── runner_hardware.py │ │ ├── lengthScaleSweepMC650.py │ │ ├── single_inverter_current_control_safe_opt_includingTB.py │ │ ├── single_inverter_voltage_current_control_safe_opt_includingTB.py │ │ └── single_inverter_voltage_current_control_safe_opt_includingTB_4D.py │ └── testing_framework_control/ │ ├── df_metrics_id_controller1.pkl │ ├── df_metrics_id_controller2.pkl │ ├── df_metrics_iq_controller1.pkl │ ├── df_metrics_iq_controller2.pkl │ ├── df_metrics_slave_f_controller1_droop.pkl │ ├── df_metrics_slave_f_controller2_droop.pkl │ ├── df_metrics_vd_controller1.pkl │ ├── df_metrics_vd_controller1_droop.pkl │ ├── df_metrics_vd_controller2.pkl │ ├── df_metrics_vd_controller2_droop.pkl │ ├── df_metrics_vq_controller1.pkl │ ├── df_metrics_vq_controller1_droop.pkl │ ├── df_metrics_vq_controller2.pkl │ ├── df_metrics_vq_controller2_droop.pkl │ ├── metrics.py │ ├── net.yaml │ ├── net_RL_load.yaml │ ├── net_single-inv-curr.yaml │ ├── scoringmodel_innerlevel.py │ ├── scoringmodel_primarylevel.py │ ├── tf_innerlevel_idq.py │ ├── tf_innerlevel_vdq.py │ └── tf_primarylevel_vdq_slavefreq.py ├── net/ │ ├── net.yaml │ ├── net_dupinputs.yaml │ ├── net_single-inv-Paper_Loadstep.yaml │ ├── net_single-inv-curr.yaml │ ├── net_single-inv-curr_Paper_SC.yaml │ ├── net_single-inv-volt.yaml │ ├── net_singleinverter.yaml │ ├── net_static_droop_controller.yaml │ ├── net_test.yaml │ └── net_valid.yaml ├── omg_grid/ │ ├── ActiveLoads/ │ │ ├── ActiveLoad.mo │ │ ├── package.mo │ │ └── package.order │ ├── Components/ │ │ ├── PhaseAngle.mo │ │ ├── StartValues.mo │ │ ├── package.mo │ │ └── package.order │ ├── Examples/ │ │ ├── ControlledNetworkSC.mo │ │ ├── ControlledNetworkSingleInverter.mo │ │ ├── NetworkSineTest.mo │ │ ├── PLL_Test.mo │ │ ├── package.mo │ │ └── package.order │ ├── Filter/ │ │ ├── IdealFilter/ │ │ │ ├── L.mo │ │ │ ├── LC.mo │ │ │ ├── LCL.mo │ │ │ ├── LCLC.mo │ │ │ ├── PI.mo │ │ │ ├── package.mo │ │ │ └── package.order │ │ ├── LossesFilter/ │ │ │ ├── L.mo │ │ │ ├── LC.mo │ │ │ ├── LCL.mo │ │ │ ├── LCLC.mo │ │ │ ├── PI.mo │ │ │ ├── package.mo │ │ │ └── package.order │ │ ├── package.mo │ │ └── package.order │ ├── Grids/ │ │ ├── Microgrid.mo │ │ ├── Network.mo │ │ ├── NetworkSineTest.bak-mo │ │ ├── NetworkSingleInverter.mo │ │ ├── PLL.bak-mo │ │ ├── PLL_Network.mo │ │ ├── RLC_Network.mo │ │ ├── SingleModel.mo │ │ ├── Testbench_SC2.mo │ │ ├── package.mo │ │ └── package.order │ ├── Inverters/ │ │ ├── Inverter.mo │ │ ├── package.mo │ │ └── package.order │ ├── Loads/ │ │ ├── C.mo │ │ ├── L.mo │ │ ├── LC.mo │ │ ├── R.mo │ │ ├── RC.mo │ │ ├── RL.mo │ │ ├── RLC.mo │ │ ├── package.mo │ │ └── package.order │ ├── PLLs/ │ │ ├── Inverter.bak-mo │ │ ├── PLL.mo │ │ ├── PLL_DQ.mo │ │ ├── package.mo │ │ └── package.order │ ├── Transformations/ │ │ ├── ABC2AlphaBeta.mo │ │ ├── ABC2DQ_Currents.mo │ │ ├── DQ2ABC.mo │ │ ├── package.mo │ │ └── package.order │ ├── UsersGuide/ │ │ ├── Contact.mo │ │ ├── ModelicaLicense2.mo │ │ ├── package.mo │ │ └── package.order │ ├── create_fmu.mos │ ├── grid.mo │ ├── grid.network.fmu │ ├── grid.network_singleInverter.fmu │ ├── merge_fmus.py │ ├── omg_grid.Grids.NetworkSingleInverter.fmu │ ├── package.mo │ ├── package.order │ └── test.fmu ├── openmodelica_microgrid_gym/ │ ├── __init__.py │ ├── agents/ │ │ ├── __init__.py │ │ ├── agent.py │ │ ├── episodic.py │ │ ├── safeopt.py │ │ ├── staticctrl.py │ │ └── util.py │ ├── aux_ctl/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── droop_controllers.py │ │ ├── filter.py │ │ ├── inverter_controllers.py │ │ ├── observers.py │ │ ├── params.py │ │ └── pi_controllers.py │ ├── env/ │ │ ├── __init__.py │ │ ├── modelica.py │ │ ├── plot.py │ │ ├── plotmanager.py │ │ └── pyfmi.py │ ├── execution/ │ │ ├── __init__.py │ │ ├── callbacks.py │ │ └── runner.py │ ├── net/ │ │ ├── __init__.py │ │ ├── base.py │ │ └── components.py │ └── util/ │ ├── __init__.py │ ├── fastqueue.py │ ├── itertools_.py │ ├── obs_template.py │ ├── randproc.py │ ├── recorder.py │ └── transforms.py ├── requirements.txt ├── requirements_dev.txt ├── setup.cfg ├── setup.py └── tests/ ├── __init__.py ├── aux_ctl/ │ ├── test_base.py │ └── test_inverter_control.py ├── helpers.py ├── net/ │ └── test_net.py ├── test__util_plot.py ├── test_main.hd5 ├── test_main2.hd5 ├── test_main3.hd5 ├── test_main4.hd5 ├── test_modelica.py ├── test_pd_convert.py ├── test_recorder.py ├── test_runner.py ├── test_transforms.py └── util/ ├── __init__.py ├── test_fastqueue.py ├── test_flattendict.py ├── test_itertools_flatten_together.py └── test_obs_template.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/build_and_test.yml ================================================ name: Build on: push: branches: [ master, develop ] pull_request: branches: [ master, develop ] workflow_dispatch: jobs: build-code: runs-on: ubuntu-latest strategy: max-parallel: 5 steps: - uses: actions/checkout@v2 - name: Set up Python 3.8 uses: actions/setup-python@v2 with: python-version: 3.8 - name: Add conda to system path run: | # $CONDA is an environment variable pointing to the root of the miniconda directory echo $CONDA/bin >> $GITHUB_PATH - name: Install dependencies run: | conda install -c conda-forge pyfmi pip install -r requirements.txt pip install .[all] pip install -r requirements_dev.txt conda install pytest - name: Test with pytest run: pytest build-doc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build Sphinx documentation uses: ammaraskar/sphinx-action@master with: pre-build-command: "python -m pip install -r requirements_dev.txt" docs-folder: "docs/" # Publish built docs to gh-pages branch. # =============================== - name: Commit documentation changes run: | git clone https://github.com/upb-lea/openmodelica-microgrid-gym.git --branch gh-pages --single-branch gh-pages cp -r docs/_build/html/* gh-pages/ cd gh-pages touch .nojekyll git config --local user.email "action@github.com" git config --local user.name "GitHub Action" git add . git commit -m "Update documentation" -a || true # The above command will fail if no changes were present, so we ignore # that. - name: Push changes uses: ad-m/github-push-action@master with: branch: gh-pages directory: gh-pages github_token: ${{ secrets.GITHUB_TOKEN }} # =============================== ================================================ FILE: .gitignore ================================================ .idea/ __pycache__/ .mypy_cache/ *.fmu.txt .coverage # release /openmodelica_microgrid_gym.egg-info/ /dist/ /build/ /.eggs/ /docs/_build/ /fmu/binaries/win64/ /fmu/sources/ /fmu/comb /fmu/*.bat /fmu/*.log /fmu/*_FMU.makefile /fmu/combi /fmu/combined /fmu/*.def /fmu/*.makefile /fmu/*_FMU.libs /fmu/*_FMU.log /fmu/*_init.xml /fmu/modelDescription.xml /fmu/OMCpp*.cpp /fmu/OMCpp*.h /fmu/OMCpp*AlgLoopMain.cpp /fmu/OMCpp*CalcHelperMain.cpp /fmu/OMCpp*CalcHelperMain.o /fmu/OMCpp*FactoryExport.cpp /fmu/OMCpp*FMU.cpp /fmu/OMCpp*FMU.h /fmu/OMCpp*Functions.cpp /fmu/OMCpp*Functions.h /fmu/OMCpp*Initialize.cpp /fmu/OMCpp*Initialize.h /fmu/OMCpp*InitializeAlgVars.cpp /fmu/OMCpp*InitializeParameter.cpp /fmu/OMCpp*Jacobian.cpp /fmu/OMCpp*Jacobian.h /fmu/OMCpp*Main.cpp /fmu/OMCpp*Mixed.cpp /fmu/OMCpp*Mixed.h /fmu/OMCpp*StateSelection.cpp /fmu/OMCpp*StateSelection.h /fmu/OMCpp*Types.h /fmu/OMCpp*WriteOutput.cpp /fmu/OMCpp*WriteOutput.h ================================================ FILE: AUTHORS.rst ================================================ ======= Credits ======= Development Lead ---------------- * LEA - Uni Paderborn Contributors ------------ None yet. Why not be the first? ================================================ FILE: CONTRIBUTING.rst ================================================ .. highlight:: shell ============ Contributing ============ Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. You can contribute in many ways: Types of Contributions ---------------------- Report Bugs ~~~~~~~~~~~ Report bugs at https://github.com/upb-lea/openmodelica_microgrid_gym/issues. If you are reporting a bug, please include: * Your operating system name and version. * Any details about your local setup that might be helpful in troubleshooting. * Detailed steps to reproduce the bug. Fix Bugs ~~~~~~~~ Look through the GitHub issues for bugs. Anything tagged with "bug" and "help wanted" is open to whoever wants to implement it. Implement Features ~~~~~~~~~~~~~~~~~~ Look through the GitHub issues for features. Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it. Write Documentation ~~~~~~~~~~~~~~~~~~~ OpenModelica Microgrid Gym could always use more documentation, whether as part of the official OpenModelica Microgrid Gym docs, in docstrings, or even on the web in blog posts, articles, and such. Submit Feedback ~~~~~~~~~~~~~~~ The best way to send feedback is to file an issue at https://github.com/upb-lea/openmodelica_microgrid_gym/issues. If you are proposing a feature: * Explain in detail how it would work. * Keep the scope as narrow as possible, to make it easier to implement. * Remember that this is a volunteer-driven project, and that contributions are welcome :) Get Started! ------------ Ready to contribute? Here's how to set up `openmodelica_microgrid_gym` for local development. 1. Fork the `openmodelica_microgrid_gym` repo on GitHub. 2. Clone your fork locally:: $ git clone git@github.com:your_name_here/openmodelica_microgrid_gym.git 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: $ mkvirtualenv openmodelica_microgrid_gym $ cd openmodelica_microgrid_gym/ $ python setup.py develop 4. Create a branch for local development:: $ git checkout -b name-of-your-bugfix-or-feature Now you can make your changes locally. 5. When you're done making changes, check that your changes pass pytest:: $ pytest 6. Commit your changes and push your branch to GitHub:: $ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature 7. Submit a pull request through the GitHub website. Pull Request Guidelines ----------------------- Before you submit a pull request, check that it meets these guidelines: 1. The pull request should include tests. 2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst. 3. The pull request should work for Python 3.5, 3.6, 3.7 and 3.8, and for PyPy. Check https://travis-ci.com/upb-lea/openmodelica_microgrid_gym/pull_requests and make sure that the tests pass for all supported Python versions. Tips ---- To run a subset of tests:: $ pytest tests.test_openmodelica_microgrid_gym Deploying --------- A reminder for the maintainers on how to deploy. Make sure all your changes are committed (including an entry in HISTORY.rst). Then run:: $ bump2version patch # possible: major / minor / patch $ git push $ git push --tags Travis will then deploy to PyPI if tests pass. ================================================ FILE: HISTORY.rst ================================================ ======= History ======= Next ------- 0.4.0 (2021-04-07) ------------------ Changes ^^^^^^^ * ModelicaEnv: - Introduced action clipping - model_params: None values are not passed to the OpenModelica env to allow initialization - model_params: negative time values are introduced for initialization (fix) - Introduced abort reward in env if episode is terminated - Introduced obs_output to define a subset of history given as observation to the agent Fix ^^^ * omg.net.MasterInverter: - default values used to overwrite passed values Add ^^^ * Random Process wrapper * ObsTempl test * reset test for initialized env 0.3.0 (2020-12-18) ------------------ API ^^^ * ModelicaEnv: - Uses Network - __init__: - removed: timestep, model_output, model_input - added: network - Delay buffer * Network and Components: - Specify class structure using config file corresponding to fmu (see net-folder) - added noise * SafeoptAgent: - __init__: Performance parameters and calculation * aux_ctl.Contoller: - __init__: timestep and undersampling changed - added output clipping * Plotmanager Examples ^^^^^^^^ * updated to changed API Experiments ^^^^^^^^^^^ * model validation: - experiment files - experiment environment managing testbench connection via SSH Dependencies ^^^^^^^^^^^^ * Decreased Language Level to Python 3.7 0.2.0 (2020-05-27) ------------------ API ^^^ * ModelicaEnv: - reward function parameter - vis_cols now also supports Plotting templates * EmptyHistory and descendant: update(), append() * Agent: added properties * StaticControlAgent and descendant: small changes in constructor params, specifically obs_template, added properties * SafeOptAgent: added properties * Runner: plotting can be disabled Examples ^^^^^^^^ * added example for plotting Performance ^^^^^^^^^^^ * 6.6× speedup Dependencies ^^^^^^^^^^^^ * Increased Language Level to Python 3.8 0.1.3 (2020-05-13) ------------------ * best parameter set output after termination of SafeOpt agent (`#7`_) * proper action and observation space (`#14`_) * resolved problem related to environment :code:`model_params` (`#21`_) | * documentation improvements (more examples, installation) .. _`#7`: https://github.com/upb-lea/openmodelica-microgrid-gym/issues/7 .. _`#14`: https://github.com/upb-lea/openmodelica-microgrid-gym/issues/14 .. _`#21`: https://github.com/upb-lea/openmodelica-microgrid-gym/issues/21 0.1.2 (2020-05-04) ------------------ * corrected pip install requirements 0.1.1 (2020-04-22) ------------------ * First release on PyPI. ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: MANIFEST.in ================================================ include AUTHORS.rst include CONTRIBUTING.rst include HISTORY.rst include LICENSE include README.rst recursive-include tests * recursive-exclude * __pycache__ recursive-exclude * *.py[co] recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif ================================================ FILE: Makefile ================================================ .PHONY: clean clean-test clean-pyc clean-build docs help .DEFAULT_GOAL := help define BROWSER_PYSCRIPT import os, webbrowser, sys from urllib.request import pathname2url webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) endef export BROWSER_PYSCRIPT define PRINT_HELP_PYSCRIPT import re, sys for line in sys.stdin: match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) if match: target, help = match.groups() print("%-20s %s" % (target, help)) endef export PRINT_HELP_PYSCRIPT BROWSER := python -c "$$BROWSER_PYSCRIPT" help: @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts clean-build: ## remove build artifacts rm -fr build/ rm -fr dist/ rm -fr .eggs/ find . -name '*.egg-info' -exec rm -fr {} + find . -name '*.egg' -exec rm -f {} + clean-pyc: ## remove Python file artifacts find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + find . -name '*~' -exec rm -f {} + find . -name '__pycache__' -exec rm -fr {} + clean-test: ## remove test and coverage artifacts rm -fr .tox/ rm -f .coverage rm -fr htmlcov/ rm -fr .pytest_cache lint: ## check style with flake8 flake8 openmodelica_microgrid_gym tests test: ## run tests quickly with the default Python pytest test-all: ## run tests on every Python version with tox tox coverage: ## check code coverage quickly with the default Python coverage run --source openmodelica_microgrid_gym -m pytest coverage report -m coverage html $(BROWSER) htmlcov/index.html docs: ## generate Sphinx HTML documentation, including API docs rm -f docs/openmodelica_microgrid_gym.rst rm -f docs/modules.rst sphinx-apidoc -o docs/ openmodelica_microgrid_gym $(MAKE) -C docs clean $(MAKE) -C docs html $(BROWSER) docs/_build/html/index.html servedocs: docs ## compile the docs watching for changes watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D . release: dist ## package and upload a release twine upload dist/* dist: clean ## builds source and wheel package python setup.py sdist python setup.py bdist_wheel ls -l dist install: clean ## install the package to the active Python's site-packages python setup.py install ================================================ FILE: README.rst ================================================ ========================== OpenModelica Microgrid Gym ========================== | |build| |cov| |nbsp| |nbsp| |python| |pypi| |download| |nbsp| |nbsp| |license| | |doc| |whitepaper| |joss| .. |nbsp| unicode:: U+00A0 .. NO-BREAK SPACE .. |build| image:: https://github.com/upb-lea/openmodelica-microgrid-gym/actions/workflows/build_and_test.yml/badge.svg :target: https://github.com/upb-lea/openmodelica-microgrid-gym/actions/workflows/build_and_test.yml .. |cov| image:: https://codecov.io/gh/upb-lea/openmodelica-microgrid-gym/branch/master/graph/badge.svg :target: https://codecov.io/gh/upb-lea/openmodelica-microgrid-gym .. |license| image:: https://img.shields.io/github/license/upb-lea/openmodelica-microgrid-gym :target: LICENSE .. |python| image:: https://img.shields.io/pypi/pyversions/openmodelica-microgrid-gym :target: https://pypi.python.org/pypi/openmodelica_microgrid_gym .. |pypi| image:: https://img.shields.io/pypi/v/openmodelica_microgrid_gym :target: https://pypi.python.org/pypi/openmodelica_microgrid_gym .. |download| image:: https://img.shields.io/pypi/dw/openmodelica-microgrid-gym :target: https://pypistats.org/packages/openmodelica-microgrid-gym .. |doc| image:: https://img.shields.io/badge/doc-success-success :target: https://upb-lea.github.io/openmodelica-microgrid-gym .. |whitepaper| image:: https://img.shields.io/badge/arXiv-whitepaper-informational :target: https://arxiv.org/pdf/2005.04869.pdf .. |joss| image:: https://joss.theoj.org/papers/10.21105/joss.02435/status.svg :target: https://doi.org/10.21105/joss.02435 .. figure:: https://github.com/upb-lea/openmodelica-microgrid-gym/raw/develop/docs/pictures/omg_flow.png **The OpenModelica Microgrid Gym (OMG) package is a software toolbox for the simulation and control optimization of microgrids based on energy conversion by power electronic converters.** The main characteristics of the toolbox are the plug-and-play grid design and simulation in OpenModelica as well as the ready-to-go approach of intuitive reinfrocement learning (RL) approaches through a Python interface. The OMG toolbox is built upon the `OpenAI Gym`_ environment definition framework. Therefore, the toolbox is specifically designed for running reinforcement learning 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. .. _OpenAI Gym: https://gym.openai.com/ * Free software: GNU General Public License v3 * Documentation: https://upb-lea.github.io/openmodelica-microgrid-gym Video Tutorial -------------- Following is a short YouTube video introduction, to get a fist impression how to use OMG. - https://www.youtube.com/watch?v=rwBNFvCi_dY Installation ------------ Install Python Environment ^^^^^^^^^^^^^^^^^^^^^^^^^^ This 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! Since 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. As of now, only Windows and Linux are supported officially. - If conda is NOT installed on your PC, install miniconda_ for python 3.8 - Create a new conda environment (e.g. in PyCharm) - Install PyFMI from the conda-forge channel in the terminal:: $ conda install -c conda-forge pyfmi - Install OpenModelica MicrogridGym from PyPI (recommended):: $ pip install openmodelica_microgrid_gym .. _OpenModelica: https://openmodelica.org/download/download-mac .. _miniconda: https://conda.io/en/latest/miniconda.html .. _PyFMI: https://github.com/modelon-community/PyFMI Installation of OpenModelica ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OMG was create by using OMEdit_ v1.16 In case of installation issues you can resort to their pre-built `virtual machine`_. .. _OMEdit: https://openmodelica.org/download/download-windows .. _virtual machine: https://openmodelica.org/download/virtual-machine Getting started --------------- The environment is initialized and run like any other OpenAI Gym .. code-block:: python import gym if __name__ == '__main__': env = gym.make('openmodelica_microgrid_gym:ModelicaEnv-v1', max_episode_steps=None, net='../net/net.yaml', model_path='../omg_grid/grid.network.fmu') env.reset() for _ in range(1000): env.render() env.step(env.action_space.sample()) # take a random action env.close() OMG uses the `FMI standard`_ for the exchange of the model between OpenModelica and Python. .. _FMI standard: https://fmi-standard.org/ An example network consisting out of two inverters, three filters and an inductive load. .. figure:: https://github.com/upb-lea/openmodelica-microgrid-gym/raw/master/docs/pictures/omedit.jpg You 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:: openmodelica_microgrid_gym\fmu> omc create_fmu.mos Windows 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. Running the ``staticctrl.py`` starts a simulation with a manually tuned cascaded PIPI controller .. figure:: https://github.com/upb-lea/openmodelica-microgrid-gym/raw/master/docs/pictures/control.jpg :scale: 70% :align: center A save Bayesian approach of a reinforcement learning agent is provided under examples/berkamkamp.py. .. figure:: https://github.com/upb-lea/openmodelica-microgrid-gym/raw/master/docs/pictures/kp_kp_J.png :figwidth: 60% :align: center Using pytest ^^^^^^^^^^^^ OMG provides a big range of tests to ensure correct working toolbox after changes are done. On some windows machines, the tests can only be started from the terminal via 'pytest'. The 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. Since on Linux everything works fine, it seems to be a numerical issue connected with the FMUs. Citation & white paper ---------------------- Please find a white paper on the OMG toolbox including an exemplary usage scenario here: - https://arxiv.org/abs/2005.04869 Please use the following BibTeX entry for citing us:: @article{OMG-code2020, title = {OMG: A Scalable and Flexible Simulation and Testing Environment Toolbox for Intelligent Microgrid Control}, author = {Stefan Heid and Daniel Weber and Henrik Bode and Eyke Hüllermeier and Oliver Wallscheid}, year = {2020}, doi = {10.21105/joss.02435}, url = {https://doi.org/10.21105/joss.02435}, publisher = {The Open Journal}, volume = {5}, number = {54}, pages = {2435}, journal = {Journal of Open Source Software} } @article{OMG-whitepaper2020, title={Towards a Scalable and Flexible Simulation and Testing Environment Toolbox for Intelligent Microgrid Control}, author={Henrik Bode and Stefan Heid and Daniel Weber and Eyke Hüllermeier and Oliver Wallscheid}, year={2020}, eprint={http://arxiv.org/abs/2005.04869}, archivePrefix={arXiv}, primaryClass={eess.SY} } Contributing ------------ Please refer to the `contribution guide`_. .. _`contribution guide`: https://github.com/upb-lea/openmodelica-microgrid-gym/blob/master/CONTRIBUTING.rst Credits ------- This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template. .. _Cookiecutter: https://github.com/audreyr/cookiecutter .. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage ================================================ FILE: docs/JOSS/paper.bib ================================================ @Misc{Berkenkamp2020, author = {Felix Berkenkamp}, title = {{SafeOpt: Safe Bayesian Optimization}}, year = {2020}, url = {https://github.com/befelix/SafeOpt}, } @misc{OpenAI2016, Author = {Greg Brockman and Vicki Cheung and Ludwig Pettersson and Jonas Schneider and John Schulman and Jie Tang and Wojciech Zaremba}, Title = {OpenAI Gym}, Year = {2016}, url = {https://arxiv.org/abs/1606.01540}, Eprint = {arXiv:1606.01540}, } @Misc{OSMC2020, author = {{Open Source Modelica Consortium (OSMC)}}, title = {{OpenModelica}}, year = {2020}, url = {https://github.com/OpenModelica/OpenModelica}, } @inproceedings{Fritzson2018, 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}, title = {{The OpenModelica Integrated Modeling, Simulation and Optimization Environment}}, year = {2018}, crossref = {modelicaUS2018}, doi = {10.3384/ecp18154206} } @Misc{FMI2020, author = {{Modelica Association}}, title = {{Functional Mock-up Interface}}, year = {2020}, url = {https://fmi-standard.org/}, } @Misc{PyFMI2020, author = {{Modelon AB}}, title = {{PyFMI}}, year = {2020}, url = {https://github.com/modelon-community/PyFMI}, } @Article{Kroposki2008, author = {B. {Kroposki} and R. {Lasseter} and T. {Ise} and S. {Morozumi} and S. {Papathanassiou} and N. {Hatziargyriou}}, title = {{Making Microgrids Work}}, doi = {10.1109/MPE.2008.918718}, journal = {IEEE Power and Energy Magazine}, year = {2008}, volume = {6}, number = {3}, pages = {40-53}, } @Article{Lund2017, author = {Henrik Lund and Poul Alberg {\O}stergaard and David Connolly and Brian Vad Mathiesen}, title = {{Smart Energy and Smart Energy Systems}}, doi = {10.1016/j.energy.2017.05.123}, journal = {Energy}, year = {2017}, volume = {137}, pages = {556 - 565}, 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.}, } @article{Garcia2015, title={A Comprehensive Survey on Safe Reinforcement Learning}, author={Garc{\i}a, Javier and Fern{\'a}ndez, Fernando}, journal={Journal of Machine Learning Research}, volume={16}, number={1}, pages={1437--1480}, year={2015} } @Article{Brown2018, author = {T. Brown and J. H\"orsch and D. Schlachtberger}, title = {{PyPSA: Python for Power System Analysis}}, doi = {10.5334/jors.188}, journal = {Journal of Open Research Software}, year = {2018}, volume = {6}, eprint = {1707.09913}, issue = {1}, } @Article{Thurner2018, 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}}, title = {{Pandapower: An Open-Source Python Tool for Convenient Modeling, Analysis, and Optimization of Electric Power Systems}}, doi = {10.1109/TPWRS.2018.2829021}, journal = {IEEE Transactions on Power Systems}, year = {2018}, volume = {33}, number = {6}, pages = {6510-6521}, } @Article{Guerrero2013, author = {J. M. {Guerrero} and M. {Chandorkar} and T. {Lee} and P. C. {Loh}}, title = {{Advanced Control Architectures for Intelligent Microgrids, Part I: Decentralized and Hierarchical Control}}, journal = {IEEE Transactions on Industrial Electronics}, doi = {10.1109/TIE.2012.2194969}, year = {2013}, volume = {60}, number = {4}, pages = {1254-1262}, } @Article{Guerrero2013a, author = {J. M. {Guerrero} and P. C. {Loh} and T. {Lee} and M. {Chandorkar}}, title = {{Advanced Control Architectures for Intelligent Microgrids, Part II: Power Quality, Energy Storage, and AC/DC Microgrids}}, journal = {IEEE Transactions on Industrial Electronics}, doi = {10.1109/TIE.2012.2196889}, year = {2013}, volume = {60}, number = {4}, pages = {1263-1270}, } @Misc{stable-baselines3, author = {Raffin, Antonin and Hill, Ashley and Ernestus, Maximilian and Gleave, Adam and Kanervisto, Anssi and Dormann, Noah}, howpublished = {\url{https://github.com/DLR-RM/stable-baselines3}}, title = {Stable Baselines3}, year = {2019}, journal = {GitHub repository}, publisher = {GitHub}, } @Misc{TFAgents, 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}, howpublished = {\url{https://github.com/tensorflow/agents}}, note = {[Online; accessed 25-June-2019]}, title = {{TF-Agents}: A library for Reinforcement Learning in TensorFlow}, year = {2018}, journal = {GitHub repository}, url = {https://github.com/tensorflow/agents}, } @Misc{plappert2016kerasrl, author = {Matthias Plappert}, howpublished = {\url{https://github.com/keras-rl/keras-rl}}, title = {keras-rl}, year = {2016}, journal = {GitHub repository}, publisher = {GitHub}, url = {https://github.com/keras-rl/keras-rl}, } ================================================ FILE: docs/JOSS/paper.md ================================================ --- title: 'OMG: A Scalable and Flexible Simulation and Testing Environment Toolbox for Intelligent Microgrid Control' tags: - Python - OpenModelica - Microgrids - Reinforcement Learning - Energy Systems - Simulation - Testing - Control authors: - name: Stefan Heid affiliation: 1 - name: Daniel Weber affiliation: 2 - name: Henrik Bode affiliation: 2 - name: Eyke Hüllermeier affiliation: 1 - name: Oliver Wallscheid orcid: 0000-0001-9362-8777 affiliation: 2 affiliations: - name: Chair of Intelligent Systems and Machine Learning, Paderborn University, Paderborn, Germany index: 1 - name: Chair of Power Electronics and Electrical Drives, Paderborn University, Paderborn, Germany index: 2 date: 25 May 2020 bibliography: paper.bib --- # Summary The 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]. ![\label{fig:omg}](omg.png) _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._ # Background on microgrids and their control Micro- 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]. Due to their high efficiency and flexibility, power electronic converters are largely used to drive modern MSG. Power 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. This 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. Controlling MSGs is a challenging task due to the high requirements on energy availability, safety, and voltage quality. This 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]. This 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. This 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]. # State of field ``OMG`` is a Python-based package for the modeling and simulation of microgrids based on power electronic energy conversion. The 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. Due 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. This 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``. # Interfaces for control and reinforcement learning The 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. This 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]. Many 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. Following this structure, nearly every control approach, including data-driven RL, can be implemented and tested with ``OMG`` in a relatively short amount of time. To 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. # Intended use and targeted audience ``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). # Features The ``OMG`` toolbox provides the following key features: * A library for the scalable and flexible design of local electricity grids in OpenModelica. Users can select between a wide range of different grid components and connect them in a plug-and-play approach. * Dynamic simulation of local electricity grids on component level including single and multi-phase systems as well as AC and DC operation. * 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. * 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. * Large variety of predefined and parameterizable controllers (droop, voltage, current in multi- and singlephase) available, easy implementation of user-defined control structures possible. * Monitoring tools to follow the live performance of the RL agent and to map the overall grid behaviour depending of each selected parameter set * Interesting use cases applying safe data-driven learning to highlight the requirement of safety in a delicate control environment are available. # Examples Detailed examples are shown in the OMG whitepaper (https://arxiv.org/pdf/2005.04869.pdf) including the implementation and evaluation of a safe Bayesian controller [@Berkenkamp2020]. The 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). Furthermore, the provided evaluation tools enable users to compare the performance of different RL algorithms against each other and against manually tuned inverters. # Availability and installation ``OMG`` is supported and tested on Linux and Windows. Mac users are asked to run this toolbox on a Linux VM. The 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 `pip install openmodelica_microgrid_gym` command. The source code, guide and datasets are available on the GitHub repository (https://github.com/upb-lea/openmodelica-microgrid-gym). # Individual contributions of the authors Following are shown the main fields of each individual contributor of OMG: * S. Heid: Main software architecture, software module integration, unit tests * D. Weber: Application examples, control-related auxiliary features (e.g. basic expert controllers), unit tests * H. Bode: Design of the specific OpenModelica library created for OMG, grid modelling and simulation, data transfer between OpenModelica and Python, unit tests * O. Wallscheid: Concept design and idea generation, testing and technical feedback, administrative project management * E. Hüllermeier: Administrative project management, concept-oriented feedback # Acknowledgements The 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. # References ================================================ FILE: docs/Makefile ================================================ # Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) ================================================ FILE: docs/api/omg.agents.agent.rst ================================================ omg.agents.agent ================================== .. automodule:: openmodelica_microgrid_gym.agents.agent :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.agents.episodic.rst ================================================ omg.agents.episodic =================================================== .. automodule:: openmodelica_microgrid_gym.agents.episodic :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.agents.rst ================================================ omg.agents ============================= Submodules ---------- .. toctree:: omg.agents.agent omg.agents.episodic omg.agents.safeopt omg.agents.staticctrl omg.agents.util Module contents --------------- .. automodule:: openmodelica_microgrid_gym.agents :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.agents.safeopt.rst ================================================ omg.agents.safeopt ==================================== .. automodule:: openmodelica_microgrid_gym.agents.safeopt :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.agents.staticctrl.rst ================================================ omg.agents.staticctrl ======================================= .. automodule:: openmodelica_microgrid_gym.agents.staticctrl :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.agents.util.rst ================================================ omg.agents.util ================================= .. automodule:: openmodelica_microgrid_gym.agents.util :members: :special-members: :exclude-members: __dict__,__module__,__repr__,__weakref__ :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.aux_ctl.base.rst ================================================ omg.aux_ctl.base ====================================== .. automodule:: openmodelica_microgrid_gym.aux_ctl.base :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.aux_ctl.droop_controllers.rst ================================================ omg.aux\_ctl.droop\_controllers =============================================================== .. automodule:: openmodelica_microgrid_gym.aux_ctl.droop_controllers :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.aux_ctl.filter.rst ================================================ omg.aux_ctl.filter ======================================== .. automodule:: openmodelica_microgrid_gym.aux_ctl.filter :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.aux_ctl.inverter_controllers.rst ================================================ omg.aux_ctl.inverter\_controllers ================================================ .. automodule:: openmodelica_microgrid_gym.aux_ctl.inverter_controllers :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.aux_ctl.observers.rst ================================================ omg.aux\_ctl.observers ====================================================== .. automodule:: openmodelica_microgrid_gym.aux_ctl.observers :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.aux_ctl.params.rst ================================================ omg.aux_ctl.params ======================================== .. automodule:: openmodelica_microgrid_gym.aux_ctl.params :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.aux_ctl.pi_controllers.rst ================================================ omg.aux_ctl.pi_controllers ==================================== .. automodule:: openmodelica_microgrid_gym.aux_ctl.pi_controllers :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.aux_ctl.rst ================================================ omg.aux_ctl ================================== Submodules ---------- .. toctree:: omg.aux_ctl.base omg.aux_ctl.filter omg.aux_ctl.params omg.aux_ctl.observers omg.aux_ctl.droop_controllers omg.aux_ctl.pi_controllers omg.aux_ctl.inverter_controllers Module contents --------------- .. automodule:: openmodelica_microgrid_gym.aux_ctl :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.env.modelica.rst ================================================ omg.env.modelica ================================== .. automodule:: openmodelica_microgrid_gym.env.modelica :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.env.plot.rst ================================================ omg.env.plot ================================== .. automodule:: openmodelica_microgrid_gym.env.plot :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.env.plotmanager.rst ================================================ omg.env.plotmanager =================================================== .. automodule:: openmodelica_microgrid_gym.env.plotmanager :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.env.pyfmi.rst ================================================ omg.env.pyfmi ============================================= .. automodule:: openmodelica_microgrid_gym.env.pyfmi :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.env.rst ================================================ omg.env ========================== Submodules ---------- .. toctree:: omg.env.modelica omg.env.pyfmi omg.env.plot omg.env.plotmanager Module contents --------------- .. automodule:: openmodelica_microgrid_gym.env :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.execution.callbacks.rst ================================================ omg.execution.callbacks ======================================================= .. automodule:: openmodelica_microgrid_gym.execution.callbacks :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.execution.rst ================================================ omg.execution ================================ Submodules ---------- .. toctree:: omg.execution.runner omg.execution.callbacks Module contents --------------- .. automodule:: openmodelica_microgrid_gym.execution :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.execution.runner.rst ================================================ omg.execution.runner ====================================== .. automodule:: openmodelica_microgrid_gym.execution.runner :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.net.base.rst ================================================ omg.net.base ============================================ .. automodule:: openmodelica_microgrid_gym.net.base :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.net.components.rst ================================================ omg.net.components ================================================== .. automodule:: openmodelica_microgrid_gym.net.components :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.net.rst ================================================ omg.net ======================================== Submodules ---------- .. toctree:: :maxdepth: 4 omg.net.base omg.net.components Module contents --------------- .. automodule:: openmodelica_microgrid_gym.net :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.util.fastqueue.rst ================================================ omg.util.fastqueue ================================================== .. automodule:: openmodelica_microgrid_gym.util.fastqueue :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.util.itertools_.rst ================================================ omg.util.itertools\_ ======================================== .. automodule:: openmodelica_microgrid_gym.util.itertools_ :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.util.randproc.rst ================================================ omg.util.randproc ================================== .. automodule:: openmodelica_microgrid_gym.util.randproc :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.util.recorder.rst ================================================ omg.util.recorder ================================== .. automodule:: openmodelica_microgrid_gym.util.recorder :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.util.rst ================================================ omg.util ============================= Submodules ---------- .. toctree:: omg.util.fastqueue omg.util.itertools_ omg.util.transforms omg.util.randproc omg.util.recorder Module contents --------------- .. automodule:: openmodelica_microgrid_gym.util :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/api/omg.util.transforms.rst ================================================ omg.util.transforms ======================================= .. automodule:: openmodelica_microgrid_gym.util.transforms :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/conf.py ================================================ # -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('..')) sys.setrecursionlimit(1500) # -- Project information ----------------------------------------------------- project = 'OpenModelica Microgrid' copyright = '2020, Bode, Heid, Wallscheid, Weber' author = 'Bode, Heid, Wallscheid, Weber' # The short X.Y version version = '' # The full version, including alpha/beta/rc tags release = '2020' # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx_autodoc_typehints', 'sphinx.ext.coverage', 'sphinx.ext.mathjax', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst'] # source_suffix = '.rst' # The master toctree document. master_doc = 'index' # Include Constructor documentation autoclass_content = 'both' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path . exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # The default sidebars (for documents that don't match any pattern) are # defined by theme itself. Builtin themes are using these templates by # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # # html_sidebars = {} # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = 'LEA-RLdoc' # -- Options for LaTeX output ------------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'LEA-RL.tex', 'LEA-RL Documentation', 'Wallscheid, Weber, Heid, Bode', 'manual'), ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'lea-rl', 'LEA-RL Documentation', [author], 1) ] # -- Options for Texinfo output ---------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'LEA-RL', 'LEA-RL Documentation', author, 'LEA-RL', 'One line description of project.', 'Miscellaneous'), ] # -- Extension configuration ------------------------------------------------- ================================================ FILE: docs/index.rst ================================================ Welcome to OpenModelica Microgrid Gym Toolbox Documentation! ===================================================================== The OpenModelica Microgrid Gym (OMG) package is a software toolbox for the simulation of power electronics-driven microgrids and to train and test reinforcement learning agents and to compare them with classical control approaches. Content ******* In the examples section all available use cases are presented with their default configuration. For quick start, one of these can be selected and used out of the box. The documentation of the base classes is important for the development of own modules like further reward functions or reference generators. In this part, the basic interfaces of each module are specified. For the creation of additional grid constellations, Openmodelica (nightly build recommended) can be used. .. toctree:: :maxdepth: 4 :titlesonly: :caption: User Guide: parts/user_guide/getting_started parts/user_guide/OpenModelica parts/user_guide/fmu parts/user_guide/Pythoncode parts/user_guide/examples parts/user_guide/controller_tuning .. toctree:: :maxdepth: 4 :titlesonly: :caption: API: api/omg.agents api/omg.aux_ctl api/omg.env api/omg.execution api/omg.net api/omg.util .. GENERATE APIDOC .. - sphinx-apidoc -o docs/api openmodelica_microgrid_gym/ -e .. - delete module.rst .. - remove package and module names: .. - execute regex '.* package$' '' .. - execute regex '.* module$' '' Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ================================================ FILE: docs/make.bat ================================================ @ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=. set BUILDDIR=_build if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd ================================================ FILE: docs/parts/user_guide/OpenModelica.rst ================================================ OpenModelica ============ `OpenModelica `__ is an open-source Modelica-based modeling and simulation environment intended for industrial and academic usage. Installation of OpenModelica ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The models shown below were created by using `OMEdit `__ v1.16. Using a Linux OS, sometimes may lead to problems while trying to install OpenModelica. In this case, try to download the pre-built `virtual machine `__. Mac users are strongly encouraged to run OpenModelica as well as the OMG toolbox in a Linux VM. Creating Microgrids with OpenModelica ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The microgrids are created with a user defined library. To start it, run the file *grid.mo* directly in the folder *omg_grid*. This package contains all components and required for creating the power electronics driven microgrids, as well as some example networks. .. figure:: ../../pictures/library1.jpg :alt: It contains several folders with predefined components, filters, loads etc. as well as microgrids for the FMU export to Python and some stand-alone examples which can be run directly in OpenModelica. .. figure:: ../../pictures/omedit.jpg :alt: Main components of any microgrid are the three-phase inverters. They consist of three input voltages controlled by a cascaded PI-PI controller in Python. Default nomenclature for those inputs is i1p1 for "**i**\ nverter 1 **p**\ hase 1" etc, but it can be changed in the python code (model\_input=['one', 'two',...] in the env=gym.make() call). **Important**: By using filters/loads with inductors or capacitors, leave the checkbox for the initialization in the provided settings. Changes are likely to result in errors while creating the FMU or running the Python files. The provided examples are designed for up to two inverters, but the underlying models can be easily extended in order to investigate on more complex microgrid topologies. Extended example showcases are also planed for future releases. Power Losses ^^^^^^^^^^^^ In the default example "network", no power losses in the inverters or filters are included. For the latter they can be added by using parts out of the "Filter.LossesFilter" package instead of the "Filter.IdealFilter" package. Due to a big increase of components and equations in the ODE-system, the simulation time will increase. For modeling losses inside the power electronic converters, adding a model in the Python interface scripts is recommending. Integrating, e.g switching losses, directly in the OpenModelia model will require to reduce to simulation step size significantly. For larger simulations with a demand of power loss modeling, it is recommended to create user defined filters with only the resistors which are needed for the calculation. To modify them, create a new package (ctrl+N, specialization: package), duplicate the part which is to modify in the new package (right-click on it, duplicate, select the previously created package in path) and modify it there. Switches / Transistors ^^^^^^^^^^^^^^^^^^^^^^ Modeling switching-like events inside the model, e.g. triggering loadsteps by adding or removing loads, is desirable, but difficult to implement with the possibilities of OpenModelica. Switches in OpenModelica - like in many other free modelling languages - are designed as resistors. A closed switch has a low resistance, an open switch a high one. "Removed" loads are still connected to the grid. Connections with resistors in such dimension cause numerical issues while simulating as the ODE system becomes stiff. There are solvers available for stiff equation systems like BDF and Radau or ones with automatic stiffness detection, but using the switches often runs into non-converging systems and execution errors. The working alternative is a parameter-variation of the load. It is possible to change the parameters of any load during a simulation and apply loadsteps in this way (see the topic `Python code `__). Setting of v\_DC ^^^^^^^^^^^^^^^^ The DC supply voltage *v*\_DC can be set either directly in the OpenModelica model or via `Python `__. In the OM model, doubleclick in your network on the inverter, and change the parameter *v*\_ DC to the demanded value. All three phases of the inverter will be supplied with the same DC voltage. The default value is 1000 V. The default value can be changed with a right-click on an inverter, *open class*, select *text view* on the top left corner of the model canvas, and change the number in the following code line to the demanded default value: :: parameter Real v_DC = 1000; Phase-Locked Loop (PLL) ^^^^^^^^^^^^^^^^^^^^^^^ The PLL blocks are working for simulations in OpenModelica, but out of structural reasons, the PLL is calculated in Python. ================================================ FILE: docs/parts/user_guide/Pythoncode.rst ================================================ Toolbox Installation and General Remarks ================================ In the following, some installation hints and an introduction to the Python code written for the OMG toolbox is presented. Installation and Requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This 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! It is recommended to install OMG via pip: :: pip install openmodelica_microgrid_gym Alternatively, you can clone the GitHub repository. A list of requirements is provided in the home-directory. .. literalinclude:: ../../../requirements.txt **Hint:** If you are running a windows, PyFMI might throw some errors while installing via pip. It can be installed via *conda* by running: :: conda install -c conda-forge pyfmi Simulation Settings ~~~~~~~~~~~~~~~~~~~ Heart of the program structure is the creation of the environment via **gym.make()** in the main programm (in the folder example). Nearly every simulation setting can be done directly in here. Some of the most important ones are described in the following. For further information, see the `API-documentation <../../api/omg.env.modelica.html>`__. - **time\_step:** step size of the simulation in seconds. Too large timesteps may result in numerical issues, small timesteps result in a high simulation time. 1e-4 seems to be a good compromise as many real controllers operate in timesteps like this. - **reward\_fun:** Callable - Reward function for the RL-algorithm. Minimal value of rewards is for example used as lower bound for the safe Bayseian algorithm (see single_inverter_current_control_safe_opt.py). Has to be adjusted problem-specific. - **solver\_method:** Solver used for the ODE system. Every solver from `scipy.integrate.solve\_ivp `__ can be selected. Though it is highly recommended to use the implicit ones. Default solver is the "LSODA" with its integrated stiffness detection. Non-stiff systems become solved with the faster "Adams" method, stiff systems with "BDF". - **model\_params:** Parameters for the simulation, which should be changed compared to the default values from the OpenModelica model. Also usable for loadsteps as replacement for `switches `__. Example which increases the resistors in the load after 0.2 seconds from 20 Ohm to 40 Ohm: :: def f(t): return 20 if t < .2 else 40 model_params={'rl.resistor1.R': f, 'rl.resistor.R': f, 'rl.resistor.R': f}, The function :code:`f` is passed as a callable (function reference). The environment will evaluate this function in every timestep passing this timestep as parameter to the function automatically. Setting of v\_DC ~~~~~~~~~~~~~~~~ The DC supply voltage v\_DC can be set either directly in the `OpenModelica model `__ or via Python. The default value is 1000 V. It can be changed in the environment creation with the line: :: model_params={'inverter1.v_DC': 700, 'inverter2.v_DC': 500}, It will be set for every of the three phases of the inverter. Take care to set the param for every inverter which should no have the default supply voltage of 1000 V. Data Logging ~~~~~~~~~~~~ To enable logging, the root logger needs to be initialized in the main function. To do so, call: :: import numpy as np logging.basicConfig() if __name__ == '__main__': ctrl = dict() For further information about logging and the level see the `logging standard library `__. ================================================ FILE: docs/parts/user_guide/controller_tuning.rst ================================================ Controller Tuning Hints ======================= 1. Current controller of primary inverter - With no droop, in other words constant mains frequency, apply a short circuit to inverter and tune Kp, Ki of the current controller. Can tune Kp then Ki, finally Kp again if both can’t be tuned at the same time. - Try tune for 90-95% of max current. Not peak current limit, but maximum allowed during nominal operation. - Ensure that the tuning does not allow the current to exceed the peak current limit. In real world scenarios this is when an inverter will shutoff or explode. 2. Voltage controller of primary inverter - With no droop and an open circuit load of just the inverter tune Kp, Ki of the voltage controller. Can tune Kp then Ki, finally Kp again if both can’t be tuned at the same time. 3. PLL of secondary inverter - With primary inverter providing a constant frequency start tuning Kp, Ki of the PLL. Noise on the frequency output of the PLL is acceptable, as long as the output phasors of the PLL accurately match the incoming voltage signal. - The secondary inverter power electronics should be disconnected/open-circuit for this. - If possible inject step changes to the frequency setpoint of the primary inverter, watching how accurately the PLL tracks the external voltage reference. Continue tuning if necessary. 4. Current controller of secondary inverter - With the PLL of the secondary inverter locked to the primary inverter tune the Kp, Ki of the current controller. No droops at this stage. - Might need to create a step change for the setpoint for this to test accurate tracking. - Try tune for 90-95% of max current. Not peak current limit, but maximum allowed during nominal operation. 5. Droop of the primary inverter - This isn’t really a tuning step as the parameters won’t affect any other system. 6. Droops of the secondary inverter - Firstly, the filter for the frequency and the voltage feedback to the droop controllers should be set before tuning the droop. - Tune the droop controllers of the secondary inverter. - The frequency of the filter (for the frequency feedback from the PLL) affects the droop parameters and should thus be considered in the tuning. ================================================ FILE: docs/parts/user_guide/examples/basic_agent.rst ================================================ Creating an agent and a runner ============================== Additionally 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 of agents and environments, as well as the execution of multiple episodes. The class will handle all information exchange between agent and environment like presented in the high level code architecture shown below: .. figure:: ../../../pictures/highlevel.png :width: 400 :alt: Since 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. The environment is the same as above. Afterwards, the agent and the runner get defined, and the runner runs for one episode. .. literalinclude:: ../../../../examples/simple_agent.py :linenos: ================================================ FILE: docs/parts/user_guide/examples/creating_env.rst ================================================ Creating an environment ======================= Following is a minimal example how to set-up and run an environment. Necessary is the definition of model inputs, in this case the three phases of the first inverter. The model outputs will be shown as simulation results, and the model path is the relative location of the FMU file, which contains the network. For any other simulation parameters, for example the step-size, default values will be used. For the initialisation, the environment needs to be reseted, and env.render will define the output plots. The simulation will perform 1000 steps. A different random number will be provided to every of the three previously defined model_inputs. Afterwards, 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. .. literalinclude:: ../../../../examples/basic_env.py :linenos: ================================================ FILE: docs/parts/user_guide/examples/plotting.rst ================================================ Creating plots of environment variables ======================================= In this example is shown how the environment variables can be plotted and how the plots can be adjusted. The environment gets an object of the class PlotTmpl, where can be chosen which environment variables are visualized. For style, linestyle and color the grouping is detected in comparison to the environment variables to be plotted. Ajdusting labels and saving the figure can be done via callable, like shown in the example below. .. literalinclude:: ../../../../examples/plotting.py :linenos: ================================================ FILE: docs/parts/user_guide/examples/single_inverter_current_control_safe_opt.rst ================================================ Single Inverter Current Control ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In this example a three phase inverter is supplying a load (rl1) via a filter (lc1) like shown in the figure below. From that model a FMU is built to create the environment. .. figure:: ../../../pictures/Model.png An optimization method developed by `Berkenkamp et al.`_ called Safe Controller Optimization (safeopt) is used which takes a Gaussian process and Bayesian optimization to safely determine "optimal" controller parameters. The goal of the standard PI current controller is to supply an exemplary 15 A d-current to the load. .. _`Berkenkamp et al.`: https://arxiv.org/abs/1509.01066 The `generated FMU `__ is used in the environment to build up a gym env like the examples from OpenAI Gym (https://gym.openai.com/). The gym enviroment is defined in (examples/single\_inverter\_current\_control\_safe\_opt.py). It generates a gym environment using - a reward function, - plotting the inductor values (current) from the lc1-filter (which should be controlled) like shown in the figure below, - simulating 300 timesteps of delta\_t of the FMU grid.network\_singleInverter.fmu (generated from the model in the plot above), - using the setpoints for the inverters (modulation indices) i1p{1,2,3} as inputs, - and the inductor currents and capacitor voltages of lc1-filter as outputs. .. figure:: ../../../pictures/i_abc_bk_kp15_Ki121.png The agent used in this simple RL-example is taken from the class :code:`SafeOptAgent`. It contains the controller a :code:`MultiPhaseDQCurrentSourcingController`, which consists of multiphase (3) PI controllers to control the current across the inductor of the lc1-filter. There are also droop controllers implemented to calculate e.g. the frequency drop due to load changes. The agent's task is to find better parameters for the current controllers (Kp & Ki). Therefore, they are defined as mutable\_params (e.g. examples/single\_inverter\_current\_control\_safe\_opt.py) to adopt them between the episodes. The SafeOpt algorithm uses a Gaussian process to estimate the performance of the controller. Thus, the bounds and the lengthscale (c.f. examples/single\_inverter\_current\_control\_safe\_opt.py) for the gain parameters (Kp and Ki) have to be defined. One can adjust one of the parameters (Kp or Ki) (1D case) or both of them (2D case) using the algorithm. Therefore, the following flag parameters have to be adjusted accoridngly: - To adjust only Kp set :code:`adjust = 'Kp'` - To adjust only Ki set :code:`adjust = 'Kp'` - To adjust only Kp and Ki set :code:`adjust = 'Kpi'` Due to SafeOpt the agent need a safe starting point (Kp and Ki). Then it tries to calculate safely parameters with better performance. The performance is calculated using the reward function from the environment. There the mean-root-error (RME) from the measured currents and the setpoints are calculated. Additionally a barrier function is used to penalize over-currents. The barrier function can be adjusted using the parameter mu. The safe threshold for the agent is set as safe\_threshold-times of the initial performance (c.f. agents/safeopt.py). For example, safe\_threshold = 1.2 and the initial reward is -10 the safe threshold would be -12. In the end of the script a :code:`Runner` is used to execute 10 episodes using the agent to control the environment. For every episode the controlled currents and the performance function as a function of Kp and/or Ki are plotted. Some exemplary results are shown below: - If :code:`adjust == 'Kp'`, the agent tries to find an optimal value for the proportional gain (Kp) of the controller in the range of [0, 0.03] with a lengthscale of 0.01. In the figure below on the x-axis is the value for Kp and on the y-axis the performance value calculated using the reward function mentioned above. .. figure:: ../../../pictures/kp_J.png - If :code:`adjust == 'Ki'`, the agent tries to find an optimal value for the integral gain (Ki) of the controller in the range of [0, 300] with a lengthscale of 50. In the figure below on the x-axis is the value for Ki and on the y-axis the performance value calculated using the reward function mentioned above. .. figure:: ../../../pictures/ki_J.png The - due to the algorithm - "unsafe" point on the right (for Kp as well as for Ki) is not due to overcurrent but due to bad performance due to permanent control error. The resulting currents for Kp = 0.01 and Ki = 0 ("unsafe" point on the right in the figure above) is shown in the picture below. Due to the high error compared to the reference value (15 A d-current), the performance is as bad as the algorithm defines it as unsafe - in comparison to the performance reached using the initial controller parameters. .. figure:: ../../../pictures/i_abc_ki_J_bad.png - If :code:`adjust == 'Kpi'`, the agent tries to find an optimal value for the proportional gain (Kp) as well as for the integral gain (Ki) of the controller in the ranges of [0, 0.03] and a lengthscale of 0.01 for Kp and a range of [0, 300] and a lengthscale of 50 for Ki. In the figure below on the x-axis is the value for Kp, the y-axis the value for Ki and the z-axis the performance value calculated using the reward function. .. figure:: ../../../pictures/kp_ki_J.png The results of the algorithm are printed into the console in the form like below: Iteration, performance J, Params [Kp, Ki] :: J Params 0 -0.527522 [0.01, 10.0] 1 -0.442648 [0.01517286546392185, 14.85163114970222] 2 -0.318154 [0.01426989823624961, 44.96747682456248] 3 -0.296940 [0.007935547159879385, 63.12800825929393] 4 -0.286636 [0.01482713453607815, 88.70170996759624] 5 -0.286815 [0.006770598304777539, 108.12303673537075] 6 -0.280167 [0.013261084415467694, 135.24448051372738] 7 -0.313204 [0.02201710533671064, 56.446583269542394] 8 -1.387003 [0.022868977920736434, 108.40140778199653] 9 -0.304403 [0.002145673177669012, 55.14569829606201] 10 -0.480421 [0.026197353734745858, 22.29566509028389] 11 -1.097157 [0.0055262530542335535, 157.4879776902759] 12 -0.391706 [0.0, 17.86728037560901] 13 -1.307038 [0.0, 106.0724160092763] 14 -1.561142 [0.03, 42.1020413015999] The best performance in this short example of -0.280167 produces the parameter set of Kp = 0.0132... and Ki = 135.244... ================================================ FILE: docs/parts/user_guide/examples/two_inverter_static_droop_control.rst ================================================ Two Inverter Static Droop Control ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In this example, a FMU generated by OpenModelica as gym environment containing two inverters, each connected via a filter to supply in parallel a RC load is used which is shown in the figure below. This example uses the controllers as defined in the auxiliaries. One inverter is set up as voltage forming inverter with a direct droop controller which e.g. frequency drops due to the applied power. The other controller is used as current sourcing inverter with an inverse droop controller which reacts on the frequency and voltage change due to its droop control parameters by a power/reactive power change. In the default settings, plots of the abc signal as well as the dq0 signals of the master and slave are provided. By default, the following small network will be simulated: .. figure:: ../../../pictures/network.png A short introduction to experimental controller tuning with some hints can be found `here `__. If the controller works fine, a three phase voltage similar to the following one should be one of the plots. .. figure:: ../../../pictures/abc.png Any other demanded signal which is provided by the FMU or saved during the simulating can be plotted by adding it to :: viz_cols=['*.m[dq0]', 'slave.freq', 'lcl1.*'], in the gym.make() command. Make sure that demanded signal from the fmu are listed as a model\_output: :: model_output={ 'lc1': [ ['inductor1.i', 'inductor2.i', 'inductor3.i'], ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']], 'rl1': [f'inductor{i}.i' for i in range(1, 4)], 'lcl1': [['inductor1.i', 'inductor2.i', 'inductor3.i'], ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']]}, ) Hint: Every possible variable which is provided by the FMU can be seen the easiest in OpenModelica. Run the simulation without input signals, so every result for voltages and currents should be 0. On the bottom right side, you can select each component of the model in the tree structure. Clicking through the components until reaching the variable will show the whole variable name (for example lcl2.inductor2.i) on top of the plotting window. The parameters of the controller like the control frequency delta\_t, the voltage, frequency or droop characteristics can be set directly in the main function. ================================================ FILE: docs/parts/user_guide/examples.rst ================================================ Examples ======== .. toctree:: :maxdepth: 4 :titlesonly: :hidden: examples/creating_env examples/basic_agent examples/plotting examples/single_inverter_current_control_safe_opt examples/two_inverter_static_droop_control Basic Examples -------------- To 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. - `Creating the Environment`_ - `Writing an simple Agent`_ - `Modify Plotting`_ .. _`Creating the Environment`: examples/creating_env .. _`Writing an simple Agent`: examples/basic_agent .. _`Modify Plotting`: examples/plotting Advanced Examples ----------------- - `Single Inverter Current Control`_ - `Two Inverter Static Droop Control`_ .. _`Single Inverter Current Control`: examples/single_inverter_current_control_safe_opt .. _`Two Inverter Static Droop Control`: examples/two_inverter_static_droop_control ================================================ FILE: docs/parts/user_guide/fmu.rst ================================================ Functional Mock-up Unit (FMU) ============================= What is FMI and FMU? ^^^^^^^^^^^^^^^^^^^^ The Functional Mock-up Interface (`FMI `__) is a free standard that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The generated files for the exchange are called Functional Mock-up Units (FMU). The binaries are platform-specific, so a linux user can´t run a FMU which is created on a windows machine. There are two versions of the FMI standard. The OMG toolbox uses **FMI 2.0**. Furthermore, there are two simulation types, co-simulation (CS) and model exchange (ME). Co-simulation has an included solver in the FMU, model exchange provides the possibility to use solvers in Python. Due to a lack of implicit solvers in CS, the OMG toolbox uses **ME**. Create FMU´s ^^^^^^^^^^^^ The best way to create a FMU for the OMG toolbox is to run the create\_fmu.mos file in the folder "omg_grid" with the terminal. Make sure that `OpenModelica `__ is installed and your latest version of your OpenModelica package is in the same folder as this script. By running the create\_fmu.mos with the command *omc*, you can create a FMU of the model *network* in the *Grids* folder. :: path\omg_grid> omc create_fmu.mos Windows 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. Creating FMUs of other models can be generated by changing *omg_grid.Grids.Network* in the last line of the *create\_fmu.mos* to *omg_grid.Grids.YourNetworksName*. :: OpenModelica.Scripting.loadFile("package.mo"); getErrorString(); setCommandLineOptions("-d=newInst"); getErrorString(); setCommandLineOptions("-d=initialization"); getErrorString(); setCommandLineOptions("--simCodeTarget=Cpp"); getErrorString(); setCommandLineOptions("-d=-disableDirectionalDerivatives"); getErrorString(); OpenModelica.Scripting.translateModelFMU(omg_grid.Grids.Network, version="2.0", fmuType = "me"); getErrorString(); The lines in between are setting flags for the FMU-creation. The target-language is C++ instead of the default C because of a missing implementation of directional derivatives in C, which are necessary for the solvers in python. It is possible to create FMUs directly in OpenModelica (File -> Export -> FMU). Settings for this can be done in Tools -> Options -> Simulation (Target language and Translation Flags) and Tools -> Options -> FMU for the Version, type, name and path. This way is not recommended because of the possibility to miss flags like the initialization. Furthermore, problems with the providing of directional derivatives occurred during testing. Merge FMUs ^^^^^^^^^^^ As mentioned above, FMUs only work on the operating system they are created on. Though, there is a possibility to combine previously generated FMUs to allow a usage on different platforms. First, generate FMUs on different platforms from the **same** model. Then run the Python file *merge*\_fmus.py\. Select the FMUs which should be combined and choose a target file (does not need to exist before). The script checks at several points if the FMUs were generated from the same version of the library (careful: small changes like parameter variation might not be detected) and if all FMUs contain binaries for different platforms. Annotation for Windows: Sometimes, the script throws an error because in the temp-folder. The script still works and merges the fmu. The resulting file just needs to be renamed with an ".fmu" at the end and can directly be used. ================================================ FILE: docs/parts/user_guide/getting_started.rst ================================================ Getting Started =============== .. figure:: ../../pictures/omg_flow.png :alt: This user guide covers all of OpenModelica Microgrids Gym (OMG) toolbox features by topic area. Each of the following steps is introduced in a more detailed way in the different chapters of this users guide. First step is to `create the microgrid `__, which is the environment for training reinforcement learning agents in power electronic-driven microgrids. In addition, the OMG toolbox can be used for pure simulation and classical control purpose using OpenModelica models with a Python interface. Each microgrid model is built in the open source software `OpenModelica `__ and can be easily adapted. .. figure:: ../../pictures/network1.png :alt: For the transfer to Python, it needs to get exported as a `Functional Mock-up Unit (FMU) `__. The creation process of the FMU is shown `here `__. It is used to build a gym environment like in the examples from `OpenAI Gym `__. In OMG, the gym environment is defined for example in (examples/two_inverter_static_droop_control.py). After creating the environment, the network can be simulated in Python. On the one hand, it is possible to test predefined, static controller designs like described `here `__. .. figure:: ../../pictures/abc.png :alt: However, the main function of this toolbox is to apply reinforcement learning approaches by utilizing the OMG interface for optimal microgrid control as shown in this `example `__. .. figure:: ../../pictures/kp_kp_J.png :alt: Basic Examples ~~~~~~~~~~~~~~ To 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. Creating an environment ----------------------- Following is a minimal example how to set-up an run an environment. Necessary is the definition of model inputs, in this case the three phases of the first inverter. The model outputs will be shown as simulation results, and the model path is the relative location of the FMU file, which contains the network. For any other simulation parameters, for example the step-size, default values will be used. For the initialisation, the environment needs to be reseted, and env.render will define the output plots. The simulation will perform 1000 steps. A different random number will be provided to every of the three previously defined model_inputs. Afterwards, 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. .. literalinclude:: ../../../examples/basic_env.py :linenos: Creating an agent and a runner ------------------------------ Additionally 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 of agents and environments, as well as the execution of multiple episodes. The class will handle all information exchange between agent and environment like presented in the high level code architecture shown below: .. figure:: ../../pictures/highlevel.png :width: 400 :alt: Since 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. The environment is the same as above. Afterwards, the agent and the runner get defined, and the runner runs for one episode. .. literalinclude:: ../../../examples/simple_agent.py :linenos: ================================================ FILE: examples/basic_env.py ================================================ import gym if __name__ == '__main__': env = gym.make('openmodelica_microgrid_gym:ModelicaEnv-v1', max_episode_steps=None, net='../net/net.yaml', model_path='../omg_grid/grid.network.fmu') env.reset() for _ in range(1000): env.render() env.step(env.action_space.sample()) # take a random action env.close() ================================================ FILE: examples/basic_env_norm.py ================================================ import gym if __name__ == '__main__': env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', is_normalized=True, net='../net/net.yaml', model_path='../omg_grid/grid.network.fmu') env.reset() for _ in range(1000): env.render() obs, rew, done, info = env.step(env.action_space.sample()) # take a random action if done: break env.close() ================================================ FILE: examples/network_callbacks.py ================================================ ##################################### # Example using a FMU by OpenModelica as gym environment containing two inverters, each connected via an LC-filter to # supply in parallel a RL load. # This example uses the available standard controllers as defined in the 'auxiliaries' folder. # One inverter is set up as voltage forming inverter with a direct droop controller. # The other controller is used as current sourcing inverter with an inverse droop controller which reacts on the # frequency and voltage change due to its droop control parameters by a power/reactive power change. import logging from functools import partial import gym import numpy as np from matplotlib import pyplot as plt from openmodelica_microgrid_gym import Runner from openmodelica_microgrid_gym.agents import StaticControlAgent from openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, MultiPhaseDQ0PIPIController, \ MultiPhaseDQCurrentController, InverseDroopParams, PLLParams from openmodelica_microgrid_gym.env import PlotTmpl from openmodelica_microgrid_gym.net import Network # Simulation definitions max_episode_steps = 3000 # number of simulation steps per episode num_episodes = 1 # number of simulation episodes # (here, only 1 episode makes sense since simulation conditions don't change in this example) DroopGain = 40000.0 # virtual droop gain for active power / W/Hz QDroopGain = 1000.0 # virtual droop gain for reactive power / VAR/V net = Network.load('../net/net.yaml') delta_t = net.ts # simulation time step size / s freq_nom = net.freq_nom # nominal grid frequency / Hz v_nom = net.v_nom # nominal grid voltage / V v_DC = net['inverter1'].v_DC # DC-link voltage / V; will be set as model parameter in the FMU i_lim = net['inverter1'].i_lim # inverter current limit / A i_nom = net['inverter1'].i_nom # nominal inverter current / A logging.basicConfig() def load_step(t, gain): """ Doubles the load parameters :param t: :param gain: device parameter :return: Dictionary with load parameters """ # Defines a load step after 0.2 s return 1 * gain if t < .2 else 2 * gain if __name__ == '__main__': ctrl = [] # Empty dict which shall include all controllers ##################################### # Define the voltage forming inverter as master # Voltage control PI gain parameters for the voltage sourcing inverter voltage_dqp_iparams = PI_params(kP=0.025, kI=60, limits=(-i_lim, i_lim)) # Current control PI gain parameters for the voltage sourcing inverter current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1)) # Droop characteristic for the active power Watt/Hz, delta_t droop_param = DroopParams(DroopGain, 0.005, freq_nom) # Droop characteristic for the reactive power VAR/Volt Var.s/Volt qdroop_param = DroopParams(QDroopGain, 0.002, v_nom) # Add to dict ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param, ts_sim=delta_t, name='master')) ##################################### # Define the current sourcing inverter as slave # Current control PI gain parameters for the current sourcing inverter current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1)) # PI gain parameters for the PLL in the current forming inverter pll_params = PLLParams(kP=10, kI=200, limits=None, f_nom=freq_nom) # Droop characteristic for the active power Watts/Hz, W.s/Hz droop_param = InverseDroopParams(DroopGain, delta_t, freq_nom, tau_filt=0.04) # Droop characteristic for the reactive power VAR/Volt Var.s/Volt qdroop_param = InverseDroopParams(50, delta_t, v_nom, tau_filt=0.01) # Add to dict ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, i_lim, droop_param, qdroop_param, ts_sim=delta_t, name='slave')) # Define the agent as StaticControlAgent which performs the basic controller steps for every environment set agent = StaticControlAgent(ctrl, {'master': [[f'lc1.inductor{k}.i' for k in '123'], [f'lc1.capacitor{k}.v' for k in '123']], 'slave': [[f'lcl1.inductor{k}.i' for k in '123'], [f'lcl1.capacitor{k}.v' for k in '123'], [f'inverter2.i_ref.{k}' for k in '012']]}) # np.zeros(3)]}) def update_legend(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$i_{\mathrm{abc}}\,/\,\mathrm{A}$') ax.grid(which='both') plt.legend(handles=ax.lines[::3], labels=('Measurement abc', 'Setpoint dq0')) fig.show() # Define the environment env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', viz_mode='episode', viz_cols=[ PlotTmpl([[f'lcl1.inductor{i}.i' for i in '123'], [f'slave.SPI{i}' for i in 'dq0']], callback=update_legend, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']], title='Example of using an timevariant external current reference' ), ], log_level=logging.INFO, max_episode_steps=max_episode_steps, model_params={'rl1.resistor1.R': partial(load_step, gain=20), 'rl1.resistor2.R': partial(load_step, gain=20), 'rl1.resistor3.R': partial(load_step, gain=20), 'rl1.inductor1.L': 0.001, 'rl1.inductor2.L': 0.001, 'rl1.inductor3.L': 0.001 }, model_path='../omg_grid/grid.network.fmu', net=net ) # User runner to execute num_episodes-times episodes of the env controlled by the agent runner = Runner(agent, env) def timeshift(component, t): if t > .1: return dict(i_ref=np.array([30, 0, 0])) return dict(i_ref=np.array([5, 0, 0])) net['inverter2'].post_calculate_hook = timeshift runner.run(num_episodes, visualise=True) ================================================ FILE: examples/plotting.py ================================================ import gym from openmodelica_microgrid_gym.env import PlotTmpl if __name__ == '__main__': def second_plot(fig): ax = fig.gca() ax.set_ylabel('y label!') ax.set_xlabel('$t\,/\,\mathrm{ms}$') fig.savefig('plot2.pdf') env = gym.make('openmodelica_microgrid_gym:ModelicaEnv-v1', viz_mode='episode', viz_cols=[ PlotTmpl([f'lc1.inductor{i}.i' for i in '123'], callback=lambda fig: fig.savefig('plot.pdf'), linewidth=4, style=[None, '--', '*'], linestyle=['None', None, None], marker=[r'$\heartsuit$', None, None], c=['pink', None, None], title='test'), PlotTmpl(['lc1.inductor1.i', 'lc1.inductor2.i'], callback=second_plot, legend=[False, True], label=[None, 'something']) ], max_episode_steps=None, net='../net/net.yaml', model_path='../omg_grid/grid.network.fmu') env.reset() for _ in range(100): env.render() env.step(env.action_space.sample()) # take a random action env.close() ================================================ FILE: examples/simple_agent.py ================================================ import gym import numpy as np import pandas as pd from openmodelica_microgrid_gym import Agent, Runner class RndAgent(Agent): def act(self, obs: pd.Series) -> np.ndarray: return self.env.action_space.sample() if __name__ == '__main__': env = gym.make('openmodelica_microgrid_gym:ModelicaEnv-v1', net='../net/net.yaml', model_path='../omg_grid/grid.network.fmu') agent = RndAgent() runner = Runner(agent, env) runner.run(1) ================================================ FILE: examples/single_inverter_current_control_safe_opt.py ================================================ ##################################### # Example using a FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters # Simulation setup: Single inverter supplying 15 A d-current to an RL-load via a LC filter # Controller: PI current controller gain parameters are optimized by SafeOpt import logging from typing import List import GPy import gym import matplotlib.pyplot as plt import numpy as np from openmodelica_microgrid_gym import Runner from openmodelica_microgrid_gym.agents import SafeOptAgent from openmodelica_microgrid_gym.agents.util import MutableFloat from openmodelica_microgrid_gym.aux_ctl import PI_params, MultiPhaseDQCurrentSourcingController from openmodelica_microgrid_gym.env import PlotTmpl from openmodelica_microgrid_gym.net import Network from openmodelica_microgrid_gym.util import dq0_to_abc, nested_map, FullHistory # Choose which controller parameters should be adjusted by SafeOpt. # - Kp: 1D example: Only the proportional gain Kp of the PI controller is adjusted # - Ki: 1D example: Only the integral gain Ki of the PI controller is adjusted # - Kpi: 2D example: Kp and Ki are adjusted simultaneously adjust = 'Kpi' # Check if really only one simulation scenario was selected if adjust not in {'Kp', 'Ki', 'Kpi'}: raise ValueError("Please set 'adjust' to one of the following values: 'Kp', 'Ki', 'Kpi'") # Simulation definitions max_episode_steps = 600 # number of simulation steps per episode num_episodes = 10 # number of simulation episodes (i.e. SafeOpt iterations) mu = 2 # factor for barrier function (see below) net = Network.load('../net/net_single-inv-curr.yaml') i_lim = net['inverter1'].i_lim # inverter current limit / A i_nom = net['inverter1'].i_nom # nominal inverter current / A i_ref = np.array(net['inverter1'].i_ref) # exemplary set point i.e. id = 15, iq = 0, i0 = 0 / A class Reward: def __init__(self): self._idx = None def set_idx(self, obs): if self._idx is None: self._idx = nested_map( lambda n: obs.index(n), [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0']]) def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and setpoints to evaluate the quality of the used parameters. Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx Iabc_master = data[idx[0]] # 3 phase currents at LC inductors phase = data[idx[1]] # phase from the master controller needed for transformation # setpoints ISPdq0_master = data[idx[2]] # setting dq reference ISPabc_master = dq0_to_abc(ISPdq0_master, phase) # convert dq set-points into three-phase abc coordinates # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) # plus barrier penalty for violating the current constraint error = (np.sum((np.abs((ISPabc_master - Iabc_master)) / i_lim) ** 0.5, axis=0) \ + -np.sum(mu * np.log(1 - np.maximum(np.abs(Iabc_master) - i_nom, 0) / (i_lim - i_nom)), axis=0)) \ / max_episode_steps return -error.squeeze() if __name__ == '__main__': ##################################### # Definitions for the GP prior_mean = 0 # mean factor of the GP prior mean which is multiplied with the first performance of the initial set noise_var = 0.001 # measurement noise sigma_omega prior_var = 2 # prior variance of the GP bounds = None lengthscale = None if adjust == 'Kp': bounds = [(0.00, 0.03)] # bounds on the input variable Kp lengthscale = [.01] # length scale for the parameter variation [Kp] for the GP # For 1D example, if Ki should be adjusted if adjust == 'Ki': bounds = [(0, 300)] # bounds on the input variable Ki lengthscale = [50.] # length scale for the parameter variation [Ki] for the GP # For 2D example, choose Kp and Ki as mutable parameters (below) and define bounds and lengthscale for both of them if adjust == 'Kpi': bounds = [(0.0, 0.07), (0, 300)] lengthscale = [.02, 50.] # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times # the initial performance: safe_threshold = 1.2 means. Performance measurement for optimization are seen as # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!) # parameter set safe_threshold = 0 # minimal allowed performance (depending on episode return) j_min = -2 # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop # expanding points eventually. # The following variable is multiplied with the first performance of the initial set by the factor below: explore_threshold = 0 # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of # limit exceeded abort_reward = 10 * j_min # Definition of the kernel kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True) ##################################### # Definition of the controllers mutable_params = None current_dqp_iparams = None if adjust == 'Kp': # mutable_params = parameter (Kp gain of the current controller of the inverter) to be optimized using # the SafeOpt algorithm mutable_params = dict(currentP=MutableFloat(5e-3)) # Define the PI parameters for the current controller of the inverter current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=115, limits=(-1, 1)) # For 1D example, if Ki should be adjusted elif adjust == 'Ki': mutable_params = dict(currentI=MutableFloat(10)) current_dqp_iparams = PI_params(kP=10e-3, kI=mutable_params['currentI'], limits=(-1, 1)) # For 2D example, choose Kp and Ki as mutable parameters elif adjust == 'Kpi': mutable_params = dict(currentP=MutableFloat(40e-3), currentI=MutableFloat(10)) current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'], limits=(-1, 1)) # Define a current sourcing inverter as master inverter using the pi and droop parameters from above ctrl = MultiPhaseDQCurrentSourcingController(current_dqp_iparams, ts_sim=net.ts, f_nom=net.freq_nom, ts_ctrl=1e-4, name='master') ##################################### # Definition of the optimization agent # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example # Arguments described above # History is used to store results agent = SafeOptAgent(mutable_params, abort_reward, j_min, kernel, dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold, explore_threshold=explore_threshold), [ctrl], dict(master=[[f'lc1.inductor{k}.i' for k in '123'], i_ref]), history=FullHistory() ) ##################################### # Definition of the environment using a FMU created by OpenModelica # (https://www.openmodelica.org/) # Using an inverter supplying a load # - using the reward function described above as callable in the env # - viz_cols used to choose which measurement values should be displayed (here, only the 3 currents across the # inductors of the inverters are plotted. Labels and grid is adjusted using the PlotTmpl (For more information, # see UserGuide) # - inputs to the models are the connection points to the inverters (see user guide for more details) # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors def xylables(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$i_{\mathrm{abc}}\,/\,\mathrm{A}$') ax.grid(which='both') fig.show() # fig.savefig('Inductor_currents.pdf') env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', reward_fun=Reward().rew_fun, viz_cols=[ PlotTmpl([f'lc1.inductor{i}.i' for i in '123'], callback=xylables ) ], log_level=logging.INFO, viz_mode='episode', max_episode_steps=max_episode_steps, net=net, model_path='../omg_grid/grid.network_singleInverter.fmu', history=FullHistory() ) ##################################### # Execution of the experiment # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations) runner = Runner(agent, env) runner.run(num_episodes, visualise=True) print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df[:])) print('\n Experiment finished with best set: \n') print('\n {} = {}'.format(adjust, agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') # Show best episode measurment (current) plot best_env_plt = runner.run_data['best_env_plt'] if best_env_plt: ax = best_env_plt[0].axes[0] ax.set_title('Best Episode') best_env_plt[0].show() best_env_plt[0].savefig('best_env_plt.png') # Show last performance plot best_agent_plt = runner.run_data['last_agent_plt'] if best_agent_plt: ax = best_agent_plt.axes[0] ax.grid(which='both') ax.set_axisbelow(True) if adjust == 'Ki': ax.set_xlabel(r'$K_\mathrm{i}\,/\,\mathrm{(VA^{-1}s^{-1})}$') ax.set_ylabel(r'$J$') elif adjust == 'Kp': ax.set_xlabel(r'$K_\mathrm{p}\,/\,\mathrm{(VA^{-1})}$') ax.set_ylabel(r'$J$') elif adjust == 'Kpi': agent.params.reset() ax.set_xlabel(r'$K_\mathrm{i}\,/\,\mathrm{(VA^{-1}s^{-1})}$') ax.set_ylabel(r'$K_\mathrm{p}\,/\,\mathrm{(VA^{-1})}$') ax.get_figure().axes[1].set_ylabel(r'$J$') plt.plot(bounds[0], [mutable_params['currentP'].val, mutable_params['currentP'].val], 'k-', zorder=1, lw=4, alpha=.5) best_agent_plt.show() best_agent_plt.savefig('agent_plt.png') ================================================ FILE: examples/single_inverter_voltage_current_control_safe_opt.py ================================================ ##################################### # Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters # Simulation setup: Single voltage forming inverter supplying an RL-load via an LC-filter # Controller: Cascaded PI-PI voltage and current controller gain parameters are optimized by SafeOpt import logging import os from time import strftime, gmtime from typing import List import GPy import gym import numpy as np from openmodelica_microgrid_gym import Runner from openmodelica_microgrid_gym.agents import SafeOptAgent from openmodelica_microgrid_gym.agents.util import MutableFloat from openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, MultiPhaseDQ0PIPIController from openmodelica_microgrid_gym.env import PlotTmpl from openmodelica_microgrid_gym.net import Network from openmodelica_microgrid_gym.util import dq0_to_abc, nested_map, FullHistory # Simulation definitions max_episode_steps = 300 # number of simulation steps per episode num_episodes = 2 # number of simulation episodes (i.e. SafeOpt iterations) mu = 2 # factor for barrier function (see below) DroopGain = 40000.0 # virtual droop gain for active power / W/Hz QDroopGain = 1000.0 # virtual droop gain for reactive power / VAR/V net = Network.load('../net/net_single-inv-volt.yaml') i_lim = net['inverter1'].i_lim # inverter current limit / A i_nom = net['inverter1'].i_nom # nominal inverter current / A # Files saves results and resulting plots to the folder saves_VI_control_safeopt in the current directory current_directory = os.getcwd() save_folder = os.path.join(current_directory, r'saves_VI_control_safeopt') os.makedirs(save_folder, exist_ok=True) class Reward: def __init__(self): self._idx = None def set_idx(self, obs): if self._idx is None: self._idx = nested_map( lambda n: obs.index(n), [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'], [f'lc1.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0']]) def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of the used parameters. Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx iabc_master = data[idx[0]] # 3 phase currents at LC inductors phase = data[idx[1]] # phase from the master controller needed for transformation vabc_master = data[idx[3]] # 3 phase currents at LC inductors # set points (sp) isp_dq0_master = data[idx[2]] # setting dq current reference isp_abc_master = dq0_to_abc(isp_dq0_master, phase) # convert dq set-points into three-phase abc coordinates vsp_dq0_master = data[idx[4]] # setting dq voltage reference vsp_abc_master = dq0_to_abc(vsp_dq0_master, phase) # convert dq set-points into three-phase abc coordinates # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) # plus barrier penalty for violating the current constraint error = np.sum((np.abs((isp_abc_master - iabc_master)) / i_lim) ** 0.5, axis=0) \ + -np.sum(mu * np.log(1 - np.maximum(np.abs(iabc_master) - i_nom, 0) / (i_lim - i_nom)), axis=0) \ + np.sum((np.abs((vsp_abc_master - vabc_master)) / net.v_nom) ** 0.5, axis=0) return -error.squeeze() if __name__ == '__main__': ##################################### # Definitions for the GP prior_mean = 0 # 2 # mean factor of the GP prior mean which is multiplied with the first performance of the initial set noise_var = 0.001 ** 2 # measurement noise sigma_omega prior_var = 2 # prior variance of the GP # Choose Kp and Ki (current and voltage controller) as mutable parameters (below) and define bounds and lengthscale # for both of them bounds = [(0.0, 0.03), (0, 300), (0.0, 0.03), (0, 300)] # bounds on the input variable current-Ki&Kp and voltage-Ki&Kp lengthscale = [.005, 50., .005, 50.] # length scale for the parameter variation [current-Ki&Kp and voltage-Ki&Kp] for the GP # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times # the initial performance: safe_threshold = 1.2 means: performance measurement for optimization are seen as # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!) # parameter set safe_threshold = 0.5 # minimal allowed performance (depending on episode return) j_min = -2 # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop # expanding points eventually. # The following variable is multiplied with the first performance of the initial set by the factor below: explore_threshold = 0 # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of # limit exceeded abort_reward = -10 * j_min # Definition of the kernel kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True) ##################################### # Definition of the controllers # Choose Kp and Ki for the current and voltage controller as mutable parameters mutable_params = dict(currentP=MutableFloat(10e-3), currentI=MutableFloat(10), voltageP=MutableFloat(25e-3), voltageI=MutableFloat(60)) voltage_dqp_iparams = PI_params(kP=mutable_params['voltageP'], kI=mutable_params['voltageI'], limits=(-i_lim, i_lim)) current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'], limits=(-1, 1)) # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for the # filter and the nominal frequency # Droop controller used to calculate the virtual frequency drop due to load changes droop_param = DroopParams(DroopGain, 0.005, net.freq_nom) # Define the Q-droop parameters for the inverter of the reactive power VAR/Volt, delta_t (0.002) used for the # filter and the nominal voltage qdroop_param = DroopParams(QDroopGain, 0.002, net.v_nom) # Define a voltage forming inverter using the PIPI and droop parameters from above ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param, ts_sim=net.ts, ts_ctrl=2 * net.ts, name='master') ##################################### # Definition of the optimization agent # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example # Arguments described above # History is used to store results agent = SafeOptAgent(mutable_params, abort_reward, j_min, kernel, dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold, explore_threshold=explore_threshold), ctrls=[ctrl], obs_template=dict(master=[[f'lc1.inductor{k}.i' for k in '123'], [f'lc1.capacitor{k}.v' for k in '123'], ]), history=FullHistory() ) ##################################### # Definition of the environment using a FMU created by OpenModelica # (https://www.openmodelica.org/) # Using an inverter supplying a load # - using the reward function described above as callable in the env # - viz_cols used to choose which measurement values should be displayed. # Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide) # generated figures are stored to file # - inputs to the models are the connection points to the inverters (see user guide for more details) # - model outputs are the 3 currents through the inductors and the 3 voltages across the capacitors def xylables_i(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$i_{\mathrm{abc}}\,/\,\mathrm{A}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/Inductor_currents' + time + '.pdf') def xylables_v_abc(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$v_{\mathrm{abc}}\,/\,\mathrm{V}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/abc_voltage' + time + '.pdf') def xylables_v_dq0(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$v_{\mathrm{dq0}}\,/\,\mathrm{V}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/dq0_voltage' + time + '.pdf') env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', reward_fun=Reward().rew_fun, viz_cols=[ PlotTmpl([f'lc1.inductor{i}.i' for i in '123'], callback=xylables_i ), PlotTmpl([f'lc1.capacitor{i}.v' for i in '123'], callback=xylables_v_abc ), PlotTmpl([f'master.CVV{i}' for i in 'dq0'], callback=xylables_v_dq0 ) ], log_level=logging.INFO, viz_mode='episode', max_episode_steps=max_episode_steps, net=net, model_path='../omg_grid/grid.network_singleInverter.fmu', history=FullHistory() ) ##################################### # Execution of the experiment # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations) runner = Runner(agent, env) runner.run(num_episodes, visualise=True) ##################################### # Performance results and parameters as well as plots are stored in folder pipi_signleInvALT agent.history.df.to_csv(save_folder + '/result.csv') print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4}))) print('\n Experiment finished with best set: \n') print('\n Current-Ki&Kp and voltage-Ki&Kp = {}'.format( agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') # Show best episode measurment (current) plot best_env_plt = runner.run_data['best_env_plt'] for ii in range(len(best_env_plt)): ax = best_env_plt[ii].axes[0] ax.set_title('Best Episode') best_env_plt[ii].show() # best_env_plt[0].savefig('best_env_plt.png') ================================================ FILE: examples/stocastic_load.py ================================================ from functools import partial import gym import matplotlib.pyplot as plt from stochastic.processes import VasicekProcess from openmodelica_microgrid_gym.env import PlotTmpl from openmodelica_microgrid_gym.net import Network from openmodelica_microgrid_gym.util import RandProcess load = 28 upper_bound_load = 45 lower_bound_load = 11 net = Network.load('../net/net.yaml') def load_step(t): """ Doubles the load parameters :param t: :param gain: device parameter :return: Dictionary with load parameters """ # Defines a load step after 0.01 s if .01 < t <= .01 + net.ts: gen.proc.mean = load * 0.55 gen.reserve = load * 0.55 return gen.sample(t) if __name__ == '__main__': gen = RandProcess(VasicekProcess, proc_kwargs=dict(speed=100, vol=70, mean=load), initial=load, bounds=(lower_bound_load, upper_bound_load)) def xylables(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$R_{\mathrm{load}}\,/\,\mathrm{\Omega}$') ax.grid(which='both') ax.set_ylim([lower_bound_load - 2, upper_bound_load + 2]) plt.title('Load example drawn from Ornstein-Uhlenbeck process \n- Clipping outside the shown y-range') plt.legend() fig.show() env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', net=net, model_params={'rl1.resistor1.R': load_step, 'rl1.resistor2.R': load_step, 'rl1.resistor3.R': load_step}, viz_cols=[ PlotTmpl([f'rl1.resistor{i}.R' for i in '123'], callback=xylables )], model_path='../omg_grid/grid.network.fmu') env.reset() for _ in range(1000): env.render() obs, rew, done, info = env.step(env.action_space.sample()) # take a random action if done: break env.close() ================================================ FILE: examples/two_inverter_droop_safe_opt.py ================================================ ##################################### # Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters # Simulation setup: Two inverters (one voltage forming, one current sourcing) supplying an RL-load via an LC-filter # Controller: droop controller gains are optimized by SafeOpt import logging import os from functools import partial from time import strftime, gmtime from typing import List import GPy import gym import numpy as np from openmodelica_microgrid_gym import Runner from openmodelica_microgrid_gym.agents import SafeOptAgent from openmodelica_microgrid_gym.agents.util import MutableFloat from openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, \ MultiPhaseDQ0PIPIController, PLLParams, InverseDroopParams, MultiPhaseDQCurrentController from openmodelica_microgrid_gym.env import PlotTmpl from openmodelica_microgrid_gym.net import Network from openmodelica_microgrid_gym.util import nested_map, FullHistory # Simulation definitions max_episode_steps = 6000 # number of simulation steps per episode num_episodes = 1 # number of simulation episodes (i.e. SafeOpt iterations) mu = 2 # factor for barrier function (see below) DroopGain = 40000.0 # virtual droop gain for active power / W/Hz QDroopGain = 1000.0 # virtual droop gain for reactive power / VA/V net = Network.load('../net/net.yaml') delta_t = net.ts # simulation time step size / s freq_nom = net.freq_nom # nominal grid frequency / Hz v_nom = net.v_nom # nominal grid voltage / V v_DC = net['inverter1'].v_DC # DC-link voltage / V; will be set as model parameter in the FMU i_lim = net['inverter1'].i_lim # inverter current limit / A i_nom = net['inverter1'].i_nom # nominal inverter current / A # Files saves results and resulting plots to the folder saves_VI_control_safeopt in the current directory current_directory = os.getcwd() save_folder = os.path.join(current_directory, r'saves_droop_control_safeopt') os.makedirs(save_folder, exist_ok=True) def load_step(t, gain): """ Increases the load parameters by a factor of 5 :param t: time :param gain: device parameter :return: Dictionary with load parameters """ # Defines a load step after 0.15 s return 1 * gain if t < .15 else 5 * gain class Reward: def __init__(self): self._idx = None def set_idx(self, obs): if self._idx is None: self._idx = nested_map( lambda n: obs.index(n), [[f'slave.freq'], [f'master.CVV{s}' for s in 'dq0']]) def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of the used parameters. Takes current measurement and set-points so calculate the mean-root control error :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx freq = data[idx[0]] vdq0_master = data[idx[1]] # 3 phase voltages at LC capacitor # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) error = np.sum((np.abs((freq_nom - freq)) / freq_nom) ** 0.5, axis=0) + \ np.sum((np.abs(([v_nom, 0, 0] - vdq0_master)) / v_nom) ** 0.5, axis=0) return -error.squeeze() if __name__ == '__main__': ctrl = [] # Empty dict which shall include all controllers ##################################### # Definitions for the GP prior_mean = 2 # mean factor of the GP prior mean which is multiplied with the first performance of the initial set noise_var = 0.001 ** 2 # measurement noise sigma_omega prior_var = 0.2 # prior variance of the GP # Choose all droop params as mutable parameters (below) and define bounds and lengthscale for both of them bounds = [(0, 100000), (0, 100000), (0, 3000), (0, 100)] # bounds on the input variable lengthscale = [10000, 10000, 300., 10.] # length scale for the parameter variation for the GP # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times # the initial performance: safe_threshold = 1.2 means. Performance measurement for optimization are seen as # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!) # parameter set safe_threshold = 0.5 # minimal allowed performance (depending on episode return) j_min = -2 # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop # expanding points eventually. # The following variable is multiplied with the first performance of the initial set by the factor below: explore_threshold = 0 # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of # limit exceeded abort_reward = -10 * j_min # Definition of the kernel kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True) ##################################### # Definition of the controllers # Choose all droop parameter as mutable parameters mutable_params = dict(pDroop_master=MutableFloat(30000.0), pDroop_slave=MutableFloat(30000.0), qDroop_master=MutableFloat(QDroopGain), qDroop_slave=MutableFloat(10)) # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for # the filter and the nominal frequency # Droop controller used to calculate the virtual frequency drop due to load changes droop_param_master = DroopParams(mutable_params['pDroop_master'], 0.005, freq_nom) # droop parameter used to react on frequency drop droop_param_slave = InverseDroopParams(mutable_params['pDroop_slave'], delta_t, freq_nom, tau_filt=0.04) # Droop characteristic for the reactive power VAR/Volt Var.s/Volt # qDroop parameter used for virtual voltage drop qdroop_param_master = DroopParams(mutable_params['qDroop_master'], 0.002, v_nom) # Droop characteristic for the reactive power VAR/Volt Var.s/Volt qdroop_param_slave = InverseDroopParams(mutable_params['qDroop_slave'], delta_t, v_nom, tau_filt=0.01) ############### # define Master voltage_dqp_iparams = PI_params(kP=0.025, kI=60, limits=(-i_lim, i_lim)) # Current control PI gain parameters for the voltage sourcing inverter current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1)) # Define a current sourcing inverter as master inverter using the pi and droop parameters from above ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param_master, qdroop_param_master, ts_sim=delta_t, ts_ctrl=2 * delta_t, name='master')) ############### # define slave current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1)) # PI gain parameters for the PLL in the current forming inverter pll_params = PLLParams(kP=10, kI=200, limits=(-10000, 10000), f_nom=freq_nom) # Droop characteristic for the active power Watts/Hz, W.s/Hz # Add to dict ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, i_lim, droop_param_slave, qdroop_param_slave, ts_sim=delta_t, name='slave')) ##################################### # Definition of the optimization agent # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example # Arguments described above # History is used to store results agent = SafeOptAgent(mutable_params, abort_reward, j_min, kernel, dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold, explore_threshold=explore_threshold), ctrl, {'master': [[f'lc1.inductor{k}.i' for k in '123'], [f'lc1.capacitor{k}.v' for k in '123'], ], 'slave': [[f'lcl1.inductor{k}.i' for k in '123'], [f'lcl1.capacitor{k}.v' for k in '123'], np.zeros(3)]}, history=FullHistory() ) ##################################### # Definition of the environment using a FMU created by OpenModelica # (https://www.openmodelica.org/) # Using two inverters supplying a load # - using the reward function described above as callable in the env # - viz_cols used to choose which measurement values should be displayed # Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide) # figures are stored to folder # - inputs to the models are the connection points to the inverters (see user guide for more details) # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors for each # inverter and the 3 currents through the load def xylables_i(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$i_{\mathrm{abc}}\,/\,\mathrm{A}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/Inductor_currents' + time + '.pdf') def xylables_v_abc(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$v_{\mathrm{abc}}\,/\,\mathrm{V}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/abc_voltage' + time + '.pdf') def xylables_v_dq0(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$v_{\mathrm{dq0}}\,/\,\mathrm{V}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/dq0_voltage' + time + '.pdf') def xylables_P_master(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$P_{\mathrm{master}}\,/\,\mathrm{W}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/P_master' + time + '.pdf') def xylables_P_slave(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$P_{\mathrm{slave}}\,/\,\mathrm{W}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/P_slave' + time + '.pdf') def xylables_freq(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$f_{\mathrm{slave}}\,/\,\mathrm{Hz}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/f_slave' + time + '.pdf') env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', reward_fun=Reward().rew_fun, viz_cols=[ PlotTmpl([f'lc1.inductor{i}.i' for i in '123'], callback=xylables_i ), PlotTmpl([f'lc1.capacitor{i}.v' for i in '123'], callback=xylables_v_abc ), PlotTmpl([f'master.instPow'], callback=xylables_P_master ), PlotTmpl([f'slave.instPow'], callback=xylables_P_slave ), PlotTmpl([f'slave.freq'], callback=xylables_freq ), PlotTmpl([f'master.CVV{i}' for i in 'dq0'], callback=xylables_v_dq0 ) ], log_level=logging.INFO, viz_mode='episode', max_episode_steps=max_episode_steps, model_params={'rl1.resistor1.R': partial(load_step, gain=20), 'rl1.resistor2.R': partial(load_step, gain=20), 'rl1.resistor3.R': partial(load_step, gain=20), 'rl1.inductor1.L': partial(load_step, gain=0.001), # 0.001, 'rl1.inductor2.L': partial(load_step, gain=0.001), # 0.001, 'rl1.inductor3.L': partial(load_step, gain=0.001) # 0.001 }, model_path='../omg_grid/grid.network.fmu', net=net, ) ##################################### # Execution of the experiment # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations) runner = Runner(agent, env) runner.run(num_episodes, visualise=True) ##################################### # Performance results and Parameters as well as plots are stored in folder saves_droop agent.history.df.to_csv(save_folder + '/result.csv') print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4}))) print('\n Experiment finished with best set: \n') print('\n [pDroop_master, pDroop_slave, qDroop_master, qDroop_slave]= {}'.format( agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') ================================================ FILE: examples/two_inverter_static_droop_control.py ================================================ ##################################### # Example using a FMU by OpenModelica as gym environment containing two inverters, each connected via an LC-filter to # supply in parallel a RL load. # This example uses the available standard controllers as defined in the 'auxiliaries' folder. # One inverter is set up as voltage forming inverter with a direct droop controller. # The other controller is used as current sourcing inverter with an inverse droop controller which reacts on the # frequency and voltage change due to its droop control parameters by a power/reactive power change. import logging from functools import partial import gym import numpy as np from openmodelica_microgrid_gym import Runner from openmodelica_microgrid_gym.agents import StaticControlAgent from openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, MultiPhaseDQ0PIPIController, \ MultiPhaseDQCurrentController, InverseDroopParams, PLLParams from openmodelica_microgrid_gym.net import Network # Simulation definitions max_episode_steps = 6000 # number of simulation steps per episode num_episodes = 1 # number of simulation episodes # (here, only 1 episode makes sense since simulation conditions don't change in this example) DroopGain = 40000.0 # virtual droop gain for active power / W/Hz QDroopGain = 1000.0 # virtual droop gain for reactive power / VAR/V net = Network.load('../net/net.yaml') delta_t = net.ts # simulation time step size / s freq_nom = net.freq_nom # nominal grid frequency / Hz v_nom = net.v_nom # nominal grid voltage / V v_DC = net['inverter1'].v_DC # DC-link voltage / V; will be set as model parameter in the FMU i_lim = net['inverter1'].i_lim # inverter current limit / A i_nom = net['inverter1'].i_nom # nominal inverter current / A logging.basicConfig() def load_step(t, gain): """ Doubles the load parameters :param t: :param gain: device parameter :return: Dictionary with load parameters """ # Defines a load step after 0.2 s return 1 * gain if t < .2 else 2 * gain if __name__ == '__main__': ctrl = [] # Empty dict which shall include all controllers ##################################### # Define the voltage forming inverter as master # Voltage control PI gain parameters for the voltage sourcing inverter voltage_dqp_iparams = PI_params(kP=0.025, kI=60, limits=(-i_lim, i_lim)) # Current control PI gain parameters for the voltage sourcing inverter current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1)) # Droop characteristic for the active power Watt/Hz, delta_t droop_param = DroopParams(DroopGain, 0.005, freq_nom) # Droop characteristic for the reactive power VAR/Volt Var.s/Volt qdroop_param = DroopParams(QDroopGain, 0.002, v_nom) # Add to dict ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param, ts_sim=delta_t, name='master')) ##################################### # Define the current sourcing inverter as slave # Current control PI gain parameters for the current sourcing inverter current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1)) # PI gain parameters for the PLL in the current forming inverter pll_params = PLLParams(kP=10, kI=200, limits=None, f_nom=freq_nom) # Droop characteristic for the active power Watts/Hz, W.s/Hz droop_param = InverseDroopParams(DroopGain, delta_t, freq_nom, tau_filt=0.04) # Droop characteristic for the reactive power VAR/Volt Var.s/Volt qdroop_param = InverseDroopParams(50, delta_t, v_nom, tau_filt=0.01) # Add to dict ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, i_lim, droop_param, qdroop_param, ts_sim=delta_t, name='slave')) # Define the agent as StaticControlAgent which performs the basic controller steps for every environment set agent = StaticControlAgent(ctrl, {'master': [[f'lc1.inductor{k}.i' for k in '123'], [f'lc1.capacitor{k}.v' for k in '123']], 'slave': [[f'lcl1.inductor{k}.i' for k in '123'], [f'lcl1.capacitor{k}.v' for k in '123'], np.zeros(3)]}) # Define the environment env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', viz_mode='episode', # viz_cols=['*.m[dq0]', 'slave.freq', 'lcl1.*'], viz_cols=['master.inst*', 'slave.inst*', 'lcl1.*', 'lc1.*', 'slave.freq'], log_level=logging.INFO, max_episode_steps=max_episode_steps, model_params={'rl1.resistor1.R': partial(load_step, gain=20), 'rl1.resistor2.R': partial(load_step, gain=20), 'rl1.resistor3.R': partial(load_step, gain=20), 'rl1.inductor1.L': 0.001, 'rl1.inductor2.L': 0.001, 'rl1.inductor3.L': 0.001 }, model_path='../omg_grid/grid.network.fmu', net=net ) # User runner to execute num_episodes-times episodes of the env controlled by the agent runner = Runner(agent, env) runner.run(num_episodes, visualise=True) ================================================ FILE: experiments/model_validation/env/physical_testbench.py ================================================ import gym import paramiko import numpy as np import pandas as pd import matplotlib.pyplot as plt from time import strftime, gmtime, sleep class TestbenchEnv(gym.Env): viz_modes = {'episode', 'step', None} """Set of all valid visualisation modes""" def __init__(self, host: str = '131.234.172.139', username: str = 'root', password: str = 'omg', DT: float = 1 / 20000, executable_script_name: str = 'my_first_hps', num_steps: int = 1000, kP: float = 0.01, kI: float = 5.0, kPV: float = 0.01, kIV: float = 5.0, ref: float = 10.0, ref2: float = 12, f_nom: float = 50.0, i_limit: float = 25, i_nominal: float = 15, v_nominal: float = 20, mu=2): """ Environment to connect the code to an FPGA-Controlled test-bench via SSH. Just for demonstration purpose """ self.ssh = paramiko.SSHClient() self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) self.host = host self.username = username self.password = password self.DT = DT self.executable_script_name = executable_script_name self.max_episode_steps = num_steps self.kP = kP self.kI = kI self.kPV = kPV self.kIV = kIV self.ref = ref self.ref2 = ref2 self.f_nom = f_nom self.data = np.array(list()) self.current_step = 0 self.done = False self.i_limit = i_limit self.i_nominal = i_nominal self.v_nominal = v_nominal self.mu = mu @staticmethod def __decode_result(ssh_result): """ Methode to decode the data sent from FPGA """ result = list() for line in ssh_result.read().splitlines(): temp = line.decode("utf-8").split(",") if len(temp) == 31: # temp.pop(-1) # Drop the last item floats = [float(i) for i in temp] # print(floats) result.append(floats) elif len(temp) != 1: print(temp) decoded_result = np.array(result) return decoded_result def rew_fun(self, Iabc_meas, Iabc_SP) -> float: """ Defines the reward function for the environment. Uses the observations and setpoints to evaluate the quality of the used parameters. Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param Iabc_meas: :param Iabc_SP: :return: Error as negative reward """ # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) # plus barrier penalty for violating the current constraint error = (np.sum((np.abs((Iabc_SP - Iabc_meas)) / self.i_limit) ** 0.5, axis=0) + -np.sum(self.mu * np.log(1 - np.maximum(np.abs(Iabc_meas) - self.i_nominal, 0) / (self.i_limit - self.i_nominal)), axis=0) ) / self.max_episode_steps return -error.squeeze() def reset(self, kP, kI): # toDo: ssh connection not open every episode! self.kP = kP self.kI = kI count_retries = 0 connected = False # for x in range(10): while not connected and count_retries < 10: count_retries += 1 try: print('I Try!') self.ssh.connect(self.host, username=self.username, password=self.password) connected = True # return True except: # (BadHostKeyException, AuthenticationException, # SSHException, socket.gaierror) as e: # print(e) print('Argh') sleep(1) if count_retries == 10: print('SSH connection not possible!') str_command = './{} -u 100 -n {} -i {} -f {} -1 {} -2 {} -E'.format(self.executable_script_name, self.max_episode_steps, self.ref, self.f_nom, self.kP, self.kI ) ssh_stdin, ssh_stdout, ssh_stderr = self.ssh.exec_command(str_command) self.data = self.__decode_result(ssh_stdout) self.current_step = 0 self.done = False self.ssh.close() def step(self): """ Takes measured data and returns stepwise Measured data recorded in reset -> controller part of env """ temp_data = self.data[self.current_step] self.current_step += 1 i_abc_meas = temp_data[[3, 4, 5]] i_abc_sp = temp_data[[9, 10, 11]] reward = self.rew_fun(i_abc_meas, i_abc_sp) if self.current_step == self.max_episode_steps: self.done = True info = [] return temp_data, reward, self.done, info def render(self, J, save_folder): N = (len(self.data)) t = np.linspace(0, N * self.DT, N) V_A = self.data[:, 0] V_B = self.data[:, 1] V_C = self.data[:, 2] I_A = self.data[:, 3] I_B = self.data[:, 4] I_C = self.data[:, 5] I_D = self.data[:, 6] I_Q = self.data[:, 7] I_0 = self.data[:, 8] SP_A = self.data[:, 9] SP_B = self.data[:, 10] SP_C = self.data[:, 11] m_A = self.data[:, 12] m_B = self.data[:, 13] m_C = self.data[:, 14] m_D = self.data[:, 15] m_Q = self.data[:, 16] m_0 = self.data[:, 17] ICont_Out_D = self.data[:, 18] ICont_Out_Q = self.data[:, 19] ICont_Out_0 = self.data[:, 20] UCont_Out_D = self.data[:, 21] UCont_Out_Q = self.data[:, 22] UCont_Out_0 = self.data[:, 23] ISP_A = self.data[:, 24] ISP_B = self.data[:, 25] ISP_C = self.data[:, 26] V_D = self.data[:, 27] V_Q = self.data[:, 28] V_0 = self.data[:, 29] Ph = self.data[:, 30] # store measurement to dataframe df = pd.DataFrame({ 'V_A': self.data[:, 0], 'V_B': self.data[:, 1], 'V_C': self.data[:, 2], 'I_A': self.data[:, 3], 'I_B': self.data[:, 4], 'I_C': self.data[:, 5], 'I_D': self.data[:, 6], 'I_Q': self.data[:, 7], 'I_0': self.data[:, 8], 'Ph': self.data[:, 30] }) # df.to_pickle('Measurement') # df.to_pickle('Noise_measurement') """ fig = plt.figure() plt.plot(t, V_A, 'b', label=r'$v_{\mathrm{a}}$') plt.plot(t, V_B, 'g') plt.plot(t, V_C, 'r') plt.plot(t, SP_A, 'b--', label=r'$v_{\mathrm{SP,a}}$') plt.plot(t, SP_B, 'g--') plt.plot(t, SP_C, 'r--') plt.xlabel(r'$t\,/\,\mathrm{s}$') plt.ylabel('$v_{\mathrm{abc}}\,/\,\mathrm{V}$') plt.title('{}'.format(J)) plt.grid() plt.legend() plt.show() time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) # fig.savefig('Paper_CC_meas3/{}_abcInductor_currents' + time + '.pdf'.format(J)) fig.savefig('Paper_CC_meas3/J_{}_abcvoltage.pdf'.format(J)) """ fig = plt.figure() plt.plot(t, I_A, 'b', label=r'Measurement') plt.plot(t, I_B, 'g') plt.plot(t, I_C, 'r') plt.plot(t, SP_A, 'b--', label=r'Setpoint') plt.plot(t, SP_B, 'g--') plt.plot(t, SP_C, 'r--') plt.xlabel(r'$t\,/\,\mathrm{s}$') plt.ylabel('$i_{\mathrm{abc}}\,/\,\mathrm{A}$') # plt.title('{}'.format(J)) plt.grid() plt.legend() plt.show() fig.savefig(save_folder + '/J_{}_abcInductor_currents.pdf'.format(J)) fig.savefig(save_folder + '/J_{}_abcInductor_currents.pgf'.format(J)) fig = plt.figure() plt.plot(t, I_D, t, I_Q, t, I_0) plt.xlabel(r'$t\,/\,\mathrm{s}$') plt.ylabel('$i_{\mathrm{dq0}}\,/\,\mathrm{A}$') # plt.title('{}'.format(J)) # plt.ylim([-3, 16]) plt.grid() plt.show() fig.savefig(save_folder + '/J_{}_dq0Inductor_currents.pdf'.format(J)) fig.savefig(save_folder + '/J_{}_dq0Inductor_currents.pgf'.format(J)) fig = plt.figure() plt.plot(t, m_D, t, m_Q, t, m_0) plt.xlabel(r'$t\,/\,\mathrm{s}$') plt.ylabel('$i_{\mathrm{dq0}}\,/\,\mathrm{A}$') # plt.title('{}'.format(J)) # plt.ylim([-3, 16]) plt.grid() plt.show() time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) # fig.savefig('hardwareTest_plt/dq0Inductor_currents' + time + '.pdf') fig.savefig(save_folder + '/J_{}_mdq0.pdf'.format(J)) fig.savefig(save_folder + '/J_{}_mdq0.pgf'.format(J)) ================================================ FILE: experiments/model_validation/env/rewards.py ================================================ from typing import List import numpy as np from openmodelica_microgrid_gym.util import nested_map, dq0_to_abc class Reward: def __init__(self, i_limit: float = 15, i_nominal: float = 10, v_limit: float = 500, v_nominal: float = 230 * np.sqrt(2), mu_c: float = 1, mu_v: float = 1, max_episode_steps: float = 1000, obs_dict: List = None, funnel: np.ndarray = None): """ Reward class including different reward functions useable for current and voltage controller evaluation :param i_limit: Current limit of inverter :param i_nominal: Nominal current of inverter :param v_limit: Voltage limit of inverter :param v_nominal: Nominal voltage of inverter :param mu_c: Weighting factor for barrier to punish over-current :param mu_v: Weighting factor for barrier to punish over-voltage :param max_episode_steps: number of episode steps :param funnel: Defines area around setpoint in which a different reward function is valid to punish leaving funnel different """ self._idx = None self.i_limit = i_limit self.i_nominal = i_nominal self.v_limit = v_limit self.v_nominal = v_nominal self.mu_c = mu_c self.mu_v = mu_v self.max_episode_steps = max_episode_steps self.funnel = funnel self.obs_dict = obs_dict def set_idx(self, obs): if self._idx is None: self._idx = nested_map( lambda n: obs.index(n), self.obs_dict) # [[f'lc.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'], # [f'lc.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0'], # [f'master.CVV{k}' for k in 'dq0']]) def rew_fun_c(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and setpoints to evaluate the quality of the used parameters. Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx i_abc_master = data[idx[0]] # 3 phase currents at LC inductors phase = data[idx[1]] # phase from the master controller needed for transformation # setpoints is_pdq0_master = data[idx[2]] # setting dq reference i_sp_abc_master = dq0_to_abc(is_pdq0_master, phase) # +0.417e-4*50) # convert dq set-points into three-phase abc coordinates # Idq0_master = data[idx[0]] # ISPdq0_master = data[idx[1]] # convert dq set-points into three-phase abc coordinates # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) # plus barrier penalty for violating the current constraint error = (np.sum((np.abs((i_sp_abc_master - i_abc_master)) / self.i_limit) ** 0.5, axis=0) + -np.sum(self.mu_c * np.log(1 - np.maximum(np.abs(i_abc_master) - self.i_nominal, 0) / (self.i_limit - self.i_nominal)), axis=0)) / self.max_episode_steps return -error.squeeze() def rew_fun_v(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of the used parameters. Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx phase = data[idx[1]] # phase from the master controller needed for transformation v_abc_master = data[idx[3]] # 3 phase currents at LC inductors # set points (sp) vsp_dq0_master = data[idx[4]] # setting dq voltage reference vsp_abc_master = dq0_to_abc(vsp_dq0_master, phase) # convert dq set-points into three-phase abc coordinates # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) # plus barrier penalty for violating the current constraint error = (np.sum((np.abs((vsp_abc_master - v_abc_master)) / self.v_limit) ** 0.5, axis=0) + -np.sum( self.mu_v * np.log( 1 - np.maximum(np.abs(v_abc_master) - self.v_nominal, 0) / (self.v_limit - self.v_nominal)), axis=0)) \ / self.max_episode_steps return -error.squeeze() def rew_fun_vc(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of the used parameters. Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx iabc_master = data[idx[0]] # 3 phase currents at LC inductors phase = data[idx[1]] # phase from the master controller needed for transformation vabc_master = data[idx[3]] # 3 phase currents at LC inductors # set points (sp) isp_dq0_master = data[idx[2]] # setting dq current reference isp_abc_master = dq0_to_abc(isp_dq0_master, phase) # convert dq set-points into three-phase abc coordinates vsp_dq0_master = data[idx[4]] # setting dq voltage reference vsp_abc_master = dq0_to_abc(vsp_dq0_master, phase) # convert dq set-points into three-phase abc coordinates # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) # plus barrier penalty for violating the current constraint error = (np.sum((np.abs((isp_abc_master - iabc_master)) / self.i_limit) ** 0.5, axis=0) + -np.sum(self.mu_c * np.log(1 - np.maximum(np.abs(iabc_master) - self.i_nominal, 0) / (self.i_limit - self.i_nominal)), axis=0) + np.sum((np.abs((vsp_abc_master - vabc_master)) / self.v_limit) ** 0.5, axis=0) \ + -np.sum(self.mu_v * np.log(1 - np.maximum(np.abs(vabc_master) - self.v_nominal, 0) / (self.v_limit - self.v_nominal)), axis=0)) \ / self.max_episode_steps return -error.squeeze() def rew_fun_funnel(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of the used parameters. Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx phase = data[idx[1]] # phase from the master controller needed for transformation vabc_master = data[idx[3]] # 3 phase currents at LC inductors vdq0_master = data[idx[5]] # set points (sp) vsp_dq0_master = data[idx[4]] # setting dq voltage reference vsp_abc_master = dq0_to_abc(vsp_dq0_master, phase) # convert dq set-points into three-phase abc coordinates # calculate err = vdq0_master - vsp_dq0_master if (abs(err) > self.funnel).any(): # E2 + Offset error = (np.sum((np.abs((vsp_abc_master - vabc_master)) / self.v_limit) ** 0.5, axis=0) + 20) / self.max_episode_steps else: # E1 2: raise ValueError('Choose between single or three phase!') if len(self.gains) == 1: return 1 * self.gains[0] if (t < .05 + 0.023 or t > 0.1 + 0.023) else 0.55 * self.gains[0] else: return 1 * self.gains[n] if (t < .05 + 0.023 or t > 0.1 + 0.023) else 0.55 * self.gains[n] def give_value(self, t, n: int): """ Defines a load step after 0.2 s Doubles the load parameters :param t: t :D :param n: Index referring to the current phase :return: Dictionary with load parameters """ if n > 2: raise ValueError('Choose between single or three phase!') if len(self.gains) == 1: return self.gains[0] else: return self.gains[n] def reset(self): self.gains = np.clip( [np.random.normal(self.load_mean, self.load_std) for _ in range(1 if self.balanced else 3)], (self.load_mean - self.load_mean * self.tolerance), (self.load_mean + self.load_mean * self.tolerance)).tolist() ================================================ FILE: experiments/model_validation/env/testbench_voltage_ctrl.py ================================================ from socket import socket import gym import paramiko import numpy as np import pandas as pd import matplotlib.pyplot as plt from time import strftime, gmtime, sleep from paramiko import BadHostKeyException, AuthenticationException, SSHException from openmodelica_microgrid_gym.util import dq0_to_abc class TestbenchEnvVoltage(gym.Env): viz_modes = {'episode', 'step', None} """Set of all valid visualisation modes""" def __init__(self, host: str = '131.234.172.139', username: str = 'root', password: str = 'omg', DT: float = 1/20000, executable_script_name: str = 'my_first_hps' ,num_steps: int = 1000, kP: float = 0.052346, kI: float = 15.4072 , kPV: float = 0.018619 , kIV: float = 10.0, ref: float = 10.0, ref2: float =12, f_nom: float = 50.0, i_limit: float = 16, i_nominal: float = 12, v_nominal: float = 20, v_limit = 30, mu=2): """ Environment to connect the code to an FPGA-Controlled test-bench via SSH for votage controll. Just for demonstration purpose - different """ self.ssh = paramiko.SSHClient() self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) self.host = host self.username = username self.password = password self.DT = DT self.executable_script_name = executable_script_name self.max_episode_steps = num_steps self.kP = kP self.kI = kI self.kPV = kPV self.kIV = kIV self.ref = ref self.ref2 = ref2 self.f_nom = f_nom self.data = np.array(list()) self.data_obs = np.array(list()) self.current_step = 0 self.done = False self.i_limit = i_limit self.i_nominal = i_nominal self.v_nominal = v_nominal self.v_limit = v_limit self.mu = mu @staticmethod def __decode_result(ssh_result): result = list() resultObs = list() for line in ssh_result.read().splitlines(): temp = line.decode("utf-8").split(",") if len(temp) == 31: #temp.pop(-1) # Drop the last item floats = [float(i) for i in temp] # print(floats) result.append(floats) elif len(temp) == 9: # print(temp) floatsObs = [float(i) for i in temp] # print(floatsObs) resultObs.append(floatsObs) # count=count+1 elif len(temp) != 1: print(temp) N = (len(result)) decoded_result = np.array(result) return [decoded_result, np.array(resultObs)] @staticmethod def __decode_result_obs(ssh_result): resultObs = list() for line in ssh_result.read().splitlines(): temp = line.decode("utf-8").split(",") if len(temp) == 9: # print(temp) floatsObs = [float(i) for i in temp] # print(floatsObs) resultObs.append(floatsObs) # count=count+1 elif len(temp) != 1: print(temp) N = (len(resultObs)) decoded_result = np.array(resultObs) return decoded_result def rew_fun(self, vabc_meas, vsp_abc) -> float: """ Defines the reward function for the environment. Uses the observations and setpoints to evaluate the quality of the used parameters. Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ mu = self.mu # setpoints #Iabc_SP = dq0_to_abc(Idq0_SP, phase) # convert dq set-points into three-phase abc coordinates # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) # plus barrier penalty for violating the current constraint #error = np.sum((np.abs((Vabc_SP - vabc_meas)) / self.v_nominal) ** 0.5, axis=0) #+\ error = (np.sum((np.abs((vsp_abc - vabc_meas)) / self.v_limit) ** 0.5, axis=0) +\ -np.sum(mu * np.log(1 - np.maximum(np.abs(vabc_meas) - self.v_nominal, 0) / (self.v_limit - self.v_nominal)), axis=0) \ )/ self.max_episode_steps return -error.squeeze() #def reset(self, kP, kI, kPv, kIv): def reset(self, kPv, kIv): # toDo: ssh connection not open every episode! #self.kP = kP #self.kI = kI self.kPV = kPv self.kIV = kIv #self.ssh.connect(self.host, username=self.username, password=self.password) count_retries = 0 connected = False #for x in range(10): while not connected and count_retries < 10: count_retries+=1 try: print('I Try!') self.ssh.connect(self.host, username=self.username, password=self.password) connected = True # return True except: # (BadHostKeyException, AuthenticationException, # SSHException, socket.gaierror) as e: # print(e) print('Argh') sleep(1) if count_retries == 10: print('SSH FUCKED UP!') # toDo: get SP and kP/I from agent? # str_command = './{} {} {} {} {} {}'.format(self.executable_script_name, self.max_episode_steps, self.kP, self.kI, # self.i_ref, self.f_nom) str_command = './{} -u 100 -n {} -v {} -f {} -1 {} -2 {} -4 {} -5 {} -L -E'.format(self.executable_script_name, self.max_episode_steps, self.v_nominal, self.f_nom, self.kP, self.kI, self.kPV, self.kIV ) ssh_stdin, ssh_stdout, ssh_stderr = self.ssh.exec_command(str_command) self.data, self.data_obs = self.__decode_result(ssh_stdout) # self.data_obs = self.__decode_result_obs(ssh_stdout) self.current_step = 0 self.done = False self.ssh.close() def rew_fun4D(self, vabc_meas, vsp_abc, iabc_meas, iabc_SP) -> float: """ Defines the reward function for the environment. Uses the observations and setpoints to evaluate the quality of the used parameters. Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ mu = self.mu # setpoints # Iabc_SP = dq0_to_abc(Idq0_SP, phase) # convert dq set-points into three-phase abc coordinates # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) # plus barrier penalty for violating the current constraint # error = np.sum((np.abs((Vabc_SP - vabc_meas)) / self.v_nominal) ** 0.5, axis=0) #+\ error = (np.sum((np.abs((iabc_SP - iabc_meas)) / self.i_limit) ** 0.5, axis=0) \ + -np.sum(80 * np.log(1 - np.maximum(np.abs(iabc_meas) - self.i_nominal, 0) / \ (self.i_limit - self.i_nominal)), axis=0)+ np.sum((np.abs((vsp_abc - vabc_meas)) / self.v_limit) ** 0.5, axis=0) + \ -np.sum(mu * np.log( 1 - np.maximum(np.abs(vabc_meas) - self.v_nominal, 0) / (self.v_limit - self.v_nominal)), axis=0) \ ) / self.max_episode_steps return -error.squeeze() def reset4D(self, kP, kI, kPv, kIv): # toDo: ssh connection not open every episode! self.kP = kP self.kI = kI self.kPV = kPv self.kIV = kIv # self.ssh.connect(self.host, username=self.username, password=self.password) count_retries = 0 connected = False # for x in range(10): while not connected and count_retries < 10: count_retries += 1 try: print('I Try!') self.ssh.connect(self.host, username=self.username, password=self.password) connected = True # return True except: # (BadHostKeyException, AuthenticationException, # SSHException, socket.gaierror) as e: # print(e) print('Argh') sleep(1) if count_retries == 10: print('SSH FUCKED UP!') # toDo: get SP and kP/I from agent? # str_command = './{} {} {} {} {} {}'.format(self.executable_script_name, self.max_episode_steps, self.kP, self.kI, # self.i_ref, self.f_nom) str_command = './{} {} {} {} {} {} {} {} {} {}'.format(self.executable_script_name, self.max_episode_steps, np.int(self.max_episode_steps / 3 - 220), np.int(self.max_episode_steps * 2 / 3 - 520), self.kP, self.kI, self.kPV, self.kIV, self.v_nominal, self.f_nom) ssh_stdin, ssh_stdout, ssh_stderr = self.ssh.exec_command(str_command) self.data, self.data_obs = self.__decode_result(ssh_stdout) # self.data_obs = self.__decode_result_obs(ssh_stdout) self.current_step = 0 self.done = False self.ssh.close() def step(self): """ Takes measured data and returns stepwise Measured data recorded in reset -> controller part of env """ temp_data = self.data[self.current_step] self.current_step += 1 V_abc_meas = temp_data[[0,1,2]] V_abc_SP = temp_data[[10,11,12]] I_abc_meas = temp_data[[3,4,5]] I_abc_SP = temp_data[[24,25,26]] #phase = temp_data[9] #reward = self.rew_fun(V_abc_meas, V_abc_SP) reward = self.rew_fun4D(V_abc_meas, V_abc_SP, I_abc_meas, I_abc_SP) #V_abc_meas = temp_data[[0, 1, 2]] # Idq0_SP = np.array([self.i_ref,0,0]) #V_abc_SP = temp_data[[10, 11, 12]] #reward = self.rew_fun(V_abc_meas, V_abc_SP) if self.current_step == self.max_episode_steps: self.done = True info = [] return temp_data, reward, self.done, info def render(self, J, save_folder): N = (len(self.data)) t = np.linspace(0, N * self.DT, N) V_A = self.data[:, 0] V_B = self.data[:, 1] V_C = self.data[:, 2] I_A = self.data[:, 3] I_B = self.data[:, 4] I_C = self.data[:, 5] I_D = self.data[:, 6] I_Q = self.data[:, 7] I_0 = self.data[:, 8] SP_A = self.data[:, 9] SP_B = self.data[:, 10] SP_C = self.data[:, 11] m_A = self.data[:, 12] m_B = self.data[:, 13] m_C = self.data[:, 14] m_D = self.data[:, 15] m_Q = self.data[:, 16] m_0 = self.data[:, 17] ICont_Out_D = self.data[:, 18] ICont_Out_Q = self.data[:, 19] ICont_Out_0 = self.data[:, 20] UCont_Out_D = self.data[:, 21] UCont_Out_Q = self.data[:, 22] UCont_Out_0 = self.data[:, 23] ISP_A = self.data[:, 24] ISP_B = self.data[:, 25] ISP_C = self.data[:, 26] V_D = self.data[:, 27] V_Q = self.data[:, 28] V_0 = self.data[:, 29] Ph = self.data[:, 30] """ If_A = self.data[:, 31]; If_B = self.data[:, 32]; If_C = self.data[:, 33]; Vc_A = self.data[:, 34]; Vc_B = self.data[:, 35]; Vc_C = self.data[:, 36]; """ # Io_A = self.data_obs[:, 6] # Io_B = self.data_obs[:, 7] # Io_C = self.data_obs[:, 8] # store measurment to dataframe df = pd.DataFrame({'V_A': self.data[:, 0], 'V_B': self.data[:, 1], 'V_C': self.data[:, 2], 'I_A': self.data[:, 3], 'I_B': self.data[:, 4], 'I_C': self.data[:, 5], 'I_D': self.data[:, 6], 'I_Q': self.data[:, 7], 'I_0': self.data[:, 8], 'Ph': self.data[:, 9], 'SP_A': self.data[:, 10], 'SP_B': self.data[:, 11], 'SP_C': self.data[:, 12], 'm_A': self.data[:, 13], 'm_B': self.data[:, 14], 'm_C': self.data[:, 15], 'm_D': self.data[:, 16], 'm_Q': self.data[:, 17], 'm_0': self.data[:, 18]}) #df.to_pickle('Measurement') #df.to_pickle('Noise_measurement') #plt.plot(t, V_A, t, V_B, t, V_C) #plt.ylabel('Voltages (V)') #plt.show() fig = plt.figure() plt.plot(t, V_A, 'b', label=r'$v_{\mathrm{a}}$') plt.plot(t, V_B, 'g') plt.plot(t, V_C, 'r') plt.plot(t, SP_A, 'b--', label=r'$v_{\mathrm{SP,a}}$') plt.plot(t, SP_B, 'g--') plt.plot(t, SP_C, 'r--') plt.xlabel(r'$t\,/\,\mathrm{s}$') plt.ylabel('$v_{\mathrm{abc}}\,/\,\mathrm{V}$') #plt.title('{}'.format(J)) plt.grid() plt.legend() plt.show() #time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) #fig.savefig('hardwareTest_plt/{}_abcInductor_currents' + time + '.pdf'.format(J)) fig.savefig(save_folder +'/J_{}_abcvoltage.pdf'.format(J)) fig.savefig(save_folder +'/J_{}_abcvoltage.pgf'.format(J)) fig = plt.figure() plt.plot(t, I_A, 'b' , label = r'$i_{\mathrm{a}}$') plt.plot(t, I_B, 'g') plt.plot(t, I_C, 'r') #plt.plot(t, SP_A, 'b--', label = r'$i_{\mathrm{a}}$') #plt.plot(t, SP_B, 'g--') #plt.plot(t, SP_C, 'r--') plt.xlabel(r'$t\,/\,\mathrm{s}$') plt.ylabel('$i_{\mathrm{abc}}\,/\,\mathrm{A}$') #plt.title('{}'.format(J)) plt.grid() plt.legend() plt.show() #time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) #fig.savefig('hardwareTest_plt/{}_abcInductor_currents' + time + '.pdf'.format(J)) fig.savefig(save_folder +'/J_{}_abcInductor_currents.pdf'.format(J)) fig.savefig(save_folder +'/J_{}_abcInductor_currents.pgf'.format(J)) fig = plt.figure() plt.plot(t, I_D, t, I_Q, t, I_0) plt.xlabel(r'$t\,/\,\mathrm{s}$') plt.ylabel('$v_{\mathrm{dq0}}\,/\,\mathrm{V}$') plt.title('{}'.format(J)) #plt.ylim([-3, 16]) plt.grid() plt.show() time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) #fig.savefig('hardwareTest_plt/dq0Inductor_currents' + time + '.pdf') #fig.savefig('Paper_meas/J_{}_dq0Inductor_currents.pdf'.format(J)) fig = plt.figure() plt.plot(t, I_D, t, I_Q, t, I_0) plt.xlabel(r'$t\,/\,\mathrm{s}$') plt.ylabel('$i_{\mathrm{dq0}}\,/\,\mathrm{A}$') plt.title('{}'.format(J)) # plt.ylim([-3, 16]) plt.grid() plt.show() time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) fig = plt.figure() plt.plot(t, V_D, t, V_Q, t, V_0) plt.xlabel(r'$t\,/\,\mathrm{s}$') plt.ylabel('$v_{\mathrm{dq0}}\,/\,\mathrm{V}$') plt.title('{}'.format(J)) # plt.ylim([-3, 16]) #plt.xlim([0, 0.025]) plt.grid() plt.show() time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) fig.savefig(save_folder + '/J_{}_dq0voltage.pdf'.format(J)) fig.savefig(save_folder + '/J_{}_dq0voltage.pgf'.format(J)) """ fig = plt.figure() plt.plot(t, V_D, t, V_Q, t, V_0) plt.xlabel(r'$t\,/\,\mathrm{s}$') plt.ylabel('$v_{\mathrm{dq0}}\,/\,\mathrm{V}$') plt.title('{}'.format(J)) # plt.ylim([-3, 16]) plt.xlim([0, 0.025]) plt.grid() plt.show() time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) """ # fig = plt.figure() # plt.plot(t, Io_A, 'b', label=r'$i_{\mathrm{a}}$') # plt.plot(t, Io_B, 'g') # plt.plot(t, Io_C, 'r') # # plt.plot(t, SP_A, 'b--', label = r'$i_{\mathrm{a}}$') # # plt.plot(t, SP_B, 'g--') # # plt.plot(t, SP_C, 'r--') # plt.xlabel(r'$t\,/\,\mathrm{s}$') # plt.ylabel('$i_{\mathrm{abc,est}}\,/\,\mathrm{A}$') # # plt.title('{}'.format(J)) # plt.grid() # plt.legend() # plt.show() ================================================ FILE: experiments/model_validation/execution/monte_carlo_runner.py ================================================ import numpy as np from typing import Dict, Any from tqdm import tqdm from openmodelica_microgrid_gym.agents.episodic import EpisodicLearnerAgent from openmodelica_microgrid_gym.env import ModelicaEnv class MonteCarloRunner: """ This class will execute an agent on the environment. It handles communication between agent and environment and handles the execution of multiple epochs Additionally to runner, the Monte-Carlo runner has an additional loop to perform n_MC experiments using one (controller) parameter set before update the (controller) parameters. Therefore, the agent.observe function is used. Inside the MC-loop the observe function is called with terminated = False to only update the return. The return is stored in an array at the end of the MC-loop. After finishing the MC-loop, the average of the return-array is used to update the (controller) parameters. Therefore, the agent-observe function is called with terminated = True """ def __init__(self, agent: EpisodicLearnerAgent, env: ModelicaEnv): """ :param agent: Agent that acts on the environment :param env: Environment tha Agent acts on """ self.env = env self.agent = agent self.agent.env = env self.run_data = dict() # type: Dict[str,Any] """ Dictionary storing information about the experiment. - "best_env_plt": environment best plots - "best_episode_idx": index of best episode - "agent_plt": last agent plot """ def run(self, n_episodes: int = 10, n_mc: int = 5, visualise: bool = False, prepare_mc_experiment=lambda: True, return_gradient_extend: bool = False): """ Trains/executes the agent on the environment for a number of epochs :param n_episodes: number of epochs to play :param n_mc: number of Monte-Carlo experiments using the same parameter set before updating the latter :param visualise: turns on visualization of the environment :param prepare_mc_experiment: prepares experiment by resetting stochastic components :param return_gradient_extend: calculates gradient extension for return if return_gradient_extend """ t = np.linspace(0, self.env.max_episode_steps * self.env.net.ts, self.env.max_episode_steps + 1) self.agent.reset() self.env.history.cols = self.env.history.structured_cols(None) + self.agent.measurement_cols self.agent.obs_varnames = self.env.history.cols self.env.measure = self.agent.measure initial_performance_mc = np.zeros(n_mc) performance_mc = np.zeros(n_mc) if not visualise: self.env.viz_mode = None agent_fig = None for i in tqdm(range(n_episodes), desc='episodes', unit='epoch'): done, r = False, None np.random.seed(0) for m in tqdm(range(n_mc), desc='monte_carlo_run', unit='epoch', leave=False): prepare_mc_experiment() # reset stoch components r_vec = np.zeros(self.env.max_episode_steps) obs = self.env.reset() for p in tqdm(range(self.env.max_episode_steps), desc='steps', unit='step', leave=False): self.agent.observe(r, False) act = self.agent.act(obs) obs, r, done, info = self.env.step(act) r_vec[p] = r self.env.render() if done: self.agent.observe(r, False) if return_gradient_extend: w = self.env.history['master.CVVd'].values w1 = self.env.history['master.CVVq'].values w2 = self.env.history['master.CVV0'].values v = self.env.history['master.SPVd'].values SP_sattle = (abs(w - v) < v * 0.12).astype(int) # 0.12 -> +-20V setpoint dw = np.gradient(w) dw1 = np.gradient(w1) dw2 = np.gradient(w2) dev_return = (np.mean(abs(SP_sattle * dw)) + np.mean(abs(SP_sattle * dw1)) + np.mean( abs(SP_sattle * dw2))) else: dev_return = 0 print('NO DEV RETURN!!!!') dev_fac = 5 # 3 print(self.agent.episode_return) print(dev_return) self.agent.performance = (( self.agent.episode_return - dev_return * dev_fac) - self.agent.min_performance) \ / (self.agent.initial_performance - self.agent.min_performance) if m == 0 and i == 0: self.agent.initial_performance = self.agent.episode_return - dev_return * dev_fac self.agent.performance = (( self.agent.episode_return - dev_return * dev_fac) - self.agent.min_performance) \ / ( self.agent.initial_performance - self.agent.min_performance) # instead of perf/initial_perf self.agent.last_best_performance = self.agent.performance self.agent.last_worst_performance = self.agent.performance self.agent.best_episode = self.agent.history.df.shape[0] self.agent.last_best_performance = self.agent.performance self.agent.worst_episode = self.agent.history.df.shape[0] self.agent.last_worst_performance = self.agent.performance self.agent.performance = (( self.agent.episode_return - dev_return * dev_fac) - self.agent.min_performance) \ / (self.agent.initial_performance - self.agent.min_performance) performance_mc[m] = self.agent.performance initial_performance_mc[m] = self.agent.episode_return # set iterations and episode return = 0 self.agent.prepare_episode() break _, env_fig = self.env.close() # vor break? if (m == 0 and i == 0): # and self.agent.has_improved: self.run_data['best_env_plt'] = env_fig self.run_data['best_episode_idx'] = i self.agent.last_best_performance = self.agent.performance if (m == 0 and i == 0): # and self.agent.has_worsened: self.run_data['worst_env_plt'] = env_fig self.run_data['worst_episode_idx'] = i self.agent.last_worst_performance = self.agent.performance if i == 0: # performance was normalized to first run -> use average of first episode so that J_initial for first # is 1 eps_ret = performance_mc * ( self.agent.initial_performance - self.agent.min_performance) + self.agent.min_performance self.agent.initial_performance = np.mean(eps_ret) performance_mc = (eps_ret - self.agent.min_performance) \ / (self.agent.initial_performance - self.agent.min_performance) self.agent.performance = np.mean(performance_mc) self.agent.update_params() if visualise: agent_fig = self.agent.render() self.run_data['last_agent_plt'] = agent_fig ================================================ FILE: experiments/model_validation/execution/runner_hardware.py ================================================ from typing import Dict, Any from tqdm import tqdm import numpy as np from openmodelica_microgrid_gym.agents import Agent from experiments.model_validation.env.physical_testbench import TestbenchEnv from experiments.model_validation.env.testbench_voltage_ctrl import TestbenchEnvVoltage class RunnerHardware: """ This class will execute an agent on the environment. It handles communication between agent and environment and handles the execution of multiple epochs """ def __init__(self, agent: Agent, env: TestbenchEnv): """ :param agent: Agent that acts on the environment :param env: Environment that Agent acts on """ self.env = env self.agent = agent self.agent.env = env self.run_data = dict() # type: Dict[str,Any] """ :type dict: Stores information about the experiment. best_env_plt - environment best plots best_episode_idx - index of best episode agent_plt - last agent plot """ def run(self, n_episodes: int = 10, visualise: bool = False, save_folder: str = 'Mess'): """ Trains/executes the agent on the environment for a number of epochs :param n_episodes: number of epochs to play :param visualise: turns on visualization of the environment :param save_folder: string with save folder name """ self.agent.reset() agent_fig = None for i in tqdm(range(n_episodes), desc='episodes', unit='epoch'): self.env.reset(self.agent.params[0], self.agent.params[1]) #self.env.render(0) done, r = False, None for _ in tqdm(range(self.env.max_episode_steps), desc='steps', unit='step', leave=False): self.agent.observe(r, done) #act = self.agent.act(obs) #self.env.measurement = self.agent.measurement obs, r, done, info = self.env.step() #self.env.render() if done: break self.env.render(0, save_folder) self.agent.observe(r, done) self.env.render(self.agent.history.df.J.iloc[-1], save_folder) print(self.agent.unsafe) if visualise: agent_fig = self.agent.render() self.run_data['last_agent_plt'] = agent_fig if i == 0 or self.agent.has_improved: #self.run_data['best_env_plt'] = env_fig self.run_data['best_episode_idx'] = i class RunnerHardwareGradient: """ This class will execute an agent on the environment. It handles communication between agent and environment and handles the execution of multiple epochs adds gradient depending return """ def __init__(self, agent: Agent, env: TestbenchEnvVoltage): """ :param agent: Agent that acts on the environment :param env: Environment that Agent acts on """ self.env = env self.agent = agent self.agent.env = env self.run_data = dict() # type: Dict[str,Any] """ :type dict: Stores information about the experiment. best_env_plt - environment best plots best_episode_idx - index of best episode agent_plt - last agent plot """ def run(self, n_episodes: int = 10, visualise: bool = False, save_folder: str = 'Mess'): """ Trains/executes the agent on the environment for a number of epochs :param n_episodes: number of epochs to play :param visualise: turns on visualization of the environment :param save_folder: string with save folder name """ self.agent.reset() # self.env.history.cols = self.env.history.structured_cols(None) + self.agent.measurement_cols # self.agent.obs_varnames = self.env.history.cols # if not visualise: # self.env.viz_mode = None agent_fig = None for i in tqdm(range(n_episodes), desc='episodes', unit='epoch'): self.env.reset4D(self.agent.params[0], self.agent.params[1], self.agent.params[2], self.agent.params[3]) #self.env.reset(self.agent.params[0], self.agent.params[1]) # self.env.reset(0.01, self.agent.params[0]) # self.env.render(0) print(self.agent.params[0]) print(self.agent.params[1]) done, r = False, None for _ in tqdm(range(self.env.max_episode_steps), desc='steps', unit='step', leave=False): self.agent.observe(r, False) # act = self.agent.act(obs) # self.env.measurement = self.agent.measurement obs, r, done, info = self.env.step() # self.env.render() if done: self.agent.observe(r, False) w = self.env.data[:, 27] w1 = self.env.data[:, 28] w2 = self.env.data[:, 29] v = np.ones(len(w)) * 169.7 SP_sattle = (abs(w - v) < v * 0.12).astype(int) # 0.12 -> +-20V setpoint dw = np.gradient(w) dw1 = np.gradient(w1) dw2 = np.gradient(w2) dev_return = (np.mean(abs(SP_sattle * dw)) + np.mean(abs(SP_sattle * dw1)) + np.mean( abs(SP_sattle * dw2))) dev_fac = 2.5 if i == 0: self.agent.initial_performance = self.agent.episode_return - dev_return *dev_fac self.agent.performance = (( self.agent.episode_return - dev_return *dev_fac) - self.agent.min_performance) \ / (self.agent.initial_performance - self.agent.min_performance) self.agent.last_best_performance = self.agent.performance self.agent.last_worst_performance = self.agent.performance self.agent.performance = ((self.agent.episode_return - dev_return *dev_fac) - self.agent.min_performance) \ / (self.agent.initial_performance - self.agent.min_performance) self.agent.prepare_episode() #self.env.render(0, save_folder) self.agent.update_params() # self.agent.observe(r, done) self.env.render(self.agent.history.df.J.iloc[-1], save_folder) print(self.agent.unsafe) if visualise: agent_fig = self.agent.render() self.run_data['last_agent_plt'] = agent_fig if i == 0 or self.agent.has_improved: # self.run_data['best_env_plt'] = env_fig self.run_data['best_episode_idx'] = i ================================================ FILE: experiments/model_validation/lengthScaleSweepMC650.py ================================================ ##################################### # Experiment Multicore to search for propper lengthscale: Single inverter supplying current to an short circuit via a LR filter # Controller: PI current controller gain parameters are optimized by SafeOpt # a) FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters # b) connecting via ssh to a testbench to perform real-world measurement import logging import os from distutils.util import strtobool from functools import partial from itertools import product from itertools import tee from multiprocessing import Pool from os.path import isfile import GPy import gym import matplotlib import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns from openmodelica_microgrid_gym.env.plotmanager import PlotManager from experiments.model_validation.env.rewards import Reward from openmodelica_microgrid_gym.net import Network params = {'backend': 'ps', 'axes.labelsize': 8, # fontsize for x and y labels (was 10) 'axes.titlesize': 8, 'font.size': 8, # was 10 'legend.fontsize': 8, # was 10 'xtick.labelsize': 8, 'ytick.labelsize': 8, # 'text.usetex': True, # 'figure.figsize': [3.39, 2.5], 'figure.figsize': [3.9, 3.1], 'font.family': 'serif', 'lines.linewidth': 1 } matplotlib.rcParams.update(params) from openmodelica_microgrid_gym.agents import SafeOptAgent from openmodelica_microgrid_gym.agents.util import MutableFloat, MutableParams from openmodelica_microgrid_gym.aux_ctl import PI_params, MultiPhaseDQCurrentSourcingController from openmodelica_microgrid_gym.env import PlotTmpl from experiments.model_validation.env.stochastic_components import Load from experiments.model_validation.execution.monte_carlo_runner import MonteCarloRunner from openmodelica_microgrid_gym.util import FullHistory lengthscale_vec_kP = 0.0005 * np.logspace(.5, 1.5, 5) lengthscale_vec_kI = np.logspace(.5, 1.5, 5) # Choose which controller parameters should be adjusted by SafeOpt. # - Kp: 1D example: Only the proportional gain Kp of the PI controller is adjusted # - Ki: 1D example: Only the integral gain Ki of the PI controller is adjusted # - Kpi: 2D example: Kp and Ki are adjusted simultaneously adjust = 'Ki' # Check if really only one simulation scenario was selected if adjust not in {'Kp', 'Ki', 'Kpi'}: raise ValueError("Please set 'adjust' to one of the following values: 'Kp', 'Ki', 'Kpi'") include_simulate = True show_plots = False balanced_load = True do_measurement = False save_results = True # Files saves results and resulting plots to the folder saves_VI_control_safeopt in the current directory current_directory = os.getcwd() save_folder = os.path.join(current_directory, r'../1Test') os.makedirs(save_folder, exist_ok=True) np.random.seed(1) # Simulation definitions net = Network.load('../../net/net_single-inv-curr_Paper_SC.yaml') delta_t = 1e-4 # simulation time step size / s undersample = 1 # undersampling of controller max_episode_steps = 1000 # number of simulation steps per episode num_episodes = 1 # number of simulation episodes (i.e. SafeOpt iterations) n_MC = 1 # number of Monte-Carlo samples for simulation - samples device parameters (e.g. L,R, noise) from iLimit = 16 # inverter current limit / A iNominal = 12 # nominal inverter current / A mu = 80 # factor for barrier function (see below) i_ref1 = np.array([10, 0, 0]) # exemplary set point i.e. id = 10, iq = 0, i0 = 0 / A i_ref2 = np.array([5, 0, 0]) # exemplary set point i.e. id = 15, iq = 0, i0 = 0 / A # plant L = 2.3e-3 # / H R = 400e-3 # / Ohm phase_shift = 5 amp_dev = 1.1 def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = tee(iterable) next(b, None) return zip(a, b) def cal_j_min(phase_shift, amp_dev): """ Calulated the miminum performance for safeopt Best case error of all safe boundary scenarios is used (max) to indicate which typ of error tears the safe boarder first (the weakest link in the chain) """ ph_list = [phase_shift, 0] amp_list = [1, amp_dev] return_j_min = np.empty(len(ph_list)) error_j_min = np.empty(3) ph_shift = [0, 120, 240] t = np.linspace(0, max_episode_steps * delta_t, max_episode_steps) # risetime = 0.0015 -> 15 steps um auf 10A zu kommen: grade = 10/15 grad = 0.66 # 1e-1 irefs = [0, i_ref1[0], i_ref2[0]] ts = [0, max_episode_steps // 2, max_episode_steps] for q in range(len(ph_list)): for p in range(3): amplitude_sp = np.concatenate([np.full(t1 - t0, r1) for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))]) amplitude = np.concatenate( [np.minimum( r0 + grad * np.arange(0, t1 - t0), # ramp up phase np.full(t1 - t0, r1) # max amplitude ) for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))]) mess = amp_list[q] * amplitude * np.cos( 2 * np.pi * 50 * t + (ph_list[q] * np.pi / 180) + (ph_shift[p] * np.pi / 180)) sp = amplitude_sp * np.cos(2 * np.pi * 50 * t + (ph_shift[p] * np.pi / 180)) error_j_min[p] = -np.sum((np.abs((sp - mess)) / iLimit) ** 0.5, axis=0) / max_episode_steps return_j_min[q] = np.sum(error_j_min) # Sum all 3 phases return max(return_j_min) # @memoize def run_experiment(len_kp, len_ki): if isfile(f'{save_folder}/{len_kp:.4f},{len_ki:.4f}.txt'): with open(f'{save_folder}/{len_kp:.4f},{len_ki:.4f}.txt', 'r')as f: return strtobool(f.read().strip()) rew = Reward(i_limit=iLimit, i_nominal=iNominal, mu_c=mu, max_episode_steps=max_episode_steps, obs_dict=[[f'lc.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0']]) ##################################### # Definitions for the GP prior_mean = 0 # 2 # mean factor of the GP prior mean which is multiplied with the first performance of the # initial set noise_var = 0.001 # 0.001 ** 2 # measurement noise sigma_omega prior_var = 2 # prior variance of the GP bounds = None lengthscale = None if adjust == 'Kp': bounds = [(0.0001, 0.1)] # bounds on the input variable Kp lengthscale = [.025] # length scale for the parameter variation [Kp] for the GP # For 1D example, if Ki should be adjusted if adjust == 'Ki': bounds = [(0, 20)] # bounds on the input variable Ki lengthscale = [10] # length scale for the parameter variation [Ki] for the GP # For 2D example, choose Kp and Ki as mutable parameters (below) and define bounds and lengthscale for both of them if adjust == 'Kpi': bounds = [(0.001, 0.07), (2, 150)] lengthscale = [0.012, 30.] df_len = pd.DataFrame({'lengthscale': lengthscale, 'bounds': bounds, 'balanced_load': balanced_load, 'barrier_param_mu': mu}) # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times # the initial performance: safe_threshold = 0.8 means. Performance measurement for optimization are seen as # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!) # parameter set safe_threshold = 0 j_min = cal_j_min(phase_shift, amp_dev) # Used for normalization # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop # expanding points eventually. # The following variable is multiplied with the first performance of the initial set by the factor below: explore_threshold = 0 # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of # limit exceeded # has to be negative due to normalized performance (regarding J_init = 1) abort_reward = 100 * j_min # Definition of the kernel kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True) ##################################### # Definition of the controllers mutable_params = None current_dqp_iparams = None if adjust == 'Kp': # mutable_params = parameter (Kp gain of the current controller of the inverter) to be optimized using # the SafeOpt algorithm mutable_params = dict(currentP=MutableFloat(0.04)) # Define the PI parameters for the current controller of the inverter current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=12, limits=(-1, 1)) # For 1D example, if Ki should be adjusted elif adjust == 'Ki': mutable_params = dict(currentI=MutableFloat(5)) current_dqp_iparams = PI_params(kP=0.005, kI=mutable_params['currentI'], limits=(-1, 1)) # For 2D example, choose Kp and Ki as mutable parameters elif adjust == 'Kpi': mutable_params = dict(currentP=MutableFloat(0.04), currentI=MutableFloat(11.8)) current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'], limits=(-1, 1)) # Define a current sourcing inverter as master inverter using the pi and droop parameters from above ctrl = MultiPhaseDQCurrentSourcingController(current_dqp_iparams, delta_t, undersampling=undersample, name='master', f_nom=net.freq_nom) i_ref = MutableParams([MutableFloat(f) for f in i_ref1]) ##################################### # Definition of the optimization agent # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example # Arguments described above # History is used to store results agent = SafeOptAgent(mutable_params, abort_reward, j_min, kernel, dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold, explore_threshold=explore_threshold), [ctrl], dict(master=[[f'lc.inductor{k}.i' for k in '123'], i_ref]), history=FullHistory(), ) ##################################### # Definition of the environment using a FMU created by OpenModelica # (https://www.openmodelica.org/) # Using an inverter supplying a load # - using the reward function described above as callable in the env # - viz_cols used to choose which measurement values should be displayed (here, only the 3 currents across the # inductors of the inverters are plotted. Labels and grid is adjusted using the PlotTmpl (For more information, # see UserGuide) # - inputs to the models are the connection points to the inverters (see user guide for more details) # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors if include_simulate: # Defining unbalanced loads sampling from Gaussian distribution with sdt = 0.2*mean # r_load = Load(R, 0.1 * R, balanced=balanced_load, tolerance=0.1) # l_load = Load(L, 0.1 * L, balanced=balanced_load, tolerance=0.1) # i_noise = Noise([0, 0, 0], [0.0023, 0.0015, 0.0018], 0.0005, 0.32) # if no noise should be included: r_load = Load(R, 0 * R, balanced=balanced_load) l_load = Load(L, 0 * L, balanced=balanced_load) def reset_loads(): r_load.reset() l_load.reset() plotter = PlotManager(agent, save_results=save_results, save_folder=save_folder, show_plots=show_plots) def ugly_foo(t): if t >= .05: i_ref[:] = i_ref2 else: i_ref[:] = i_ref1 return partial(l_load.give_value, n=2)(t) env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', # reward_fun=Reward().rew_fun, reward_fun=rew.rew_fun_c, # time_step=delta_t, viz_cols=[ PlotTmpl([[f'lc.inductor{i}.i' for i in '123'], [f'master.SPI{i}' for i in 'abc']], callback=plotter.xylables_i_abc, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ), PlotTmpl([[f'master.m{i}' for i in 'abc']], callback=lambda fig: plotter.update_axes(fig, title='Simulation', ylabel='$m_{\mathrm{abc}}\,/\,\mathrm{}$') ), PlotTmpl([[f'master.CVI{i}' for i in 'dq0'], [f'master.SPI{i}' for i in 'dq0']], callback=plotter.xylables_i_dq0, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ) ], log_level=logging.INFO, viz_mode='episode', max_episode_steps=max_episode_steps, model_params={'lc.resistor1.R': partial(r_load.give_value, n=0), 'lc.resistor2.R': partial(r_load.give_value, n=1), 'lc.resistor3.R': partial(r_load.give_value, n=2), 'lc.inductor1.L': partial(l_load.give_value, n=0), 'lc.inductor2.L': partial(l_load.give_value, n=1), 'lc.inductor3.L': ugly_foo}, model_path='../../omg_grid/grid.paper.fmu', # model_path='../omg_grid/omg_grid.Grids.Paper_SC.fmu', net=net, history=FullHistory(), action_time_delay=1 * undersample ) runner = MonteCarloRunner(agent, env) runner.run(num_episodes, n_mc=n_MC, visualise=True, prepare_mc_experiment=reset_loads) with open(f'{save_folder}/{len_kp:.4f},{len_ki:.4f}.txt', 'w')as f: print(f'{agent.unsafe}', file=f) return agent.unsafe if __name__ == '__main__': print(lengthscale_vec_kP, lengthscale_vec_kI) with Pool(5) as p: is_unsafe = p.starmap(run_experiment, product(lengthscale_vec_kP, lengthscale_vec_kI)) safe_vec = np.empty([len(lengthscale_vec_kP), len(lengthscale_vec_kI)]) for ((kk, ls_kP), (ii, ls_IP)), unsafe in zip(product(enumerate(lengthscale_vec_kP), enumerate(lengthscale_vec_kI)), is_unsafe): safe_vec[kk, ii] = int(not unsafe) df = pd.DataFrame(safe_vec, index=[f'{i:.3f}' for i in lengthscale_vec_kP], columns=[f'{i:.2f}' for i in lengthscale_vec_kI]) df.to_pickle(save_folder + '/Unsafe_matrix') print(df) sns.heatmap(df) plt.show() # agent.unsafe = False ##################################### # Performance results and parameters as well as plots are stored in folder pipi_signleInv # agent.history.df.to_csv('len_search/result.csv') # if safe_results: # env.history.df.to_pickle('Simulation') print(safe_vec) ================================================ FILE: experiments/model_validation/single_inverter_current_control_safe_opt_includingTB.py ================================================ ##################################### # Experiment : Single inverter supplying current to an short circuit via a LR filter # Controller: PI current controller gain parameters are optimized by SafeOpt # a) FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters # b) connecting via ssh to a testbench to perform real-world measurement import logging import os from functools import partial from itertools import tee import GPy import gym import matplotlib import matplotlib.pyplot as plt import numpy as np import pandas as pd from experiments.model_validation.env.physical_testbench import TestbenchEnv from experiments.model_validation.env.rewards import Reward from experiments.model_validation.env.stochastic_components import Load from experiments.model_validation.execution.monte_carlo_runner import MonteCarloRunner from experiments.model_validation.execution.runner_hardware import RunnerHardware from openmodelica_microgrid_gym.agents import SafeOptAgent from openmodelica_microgrid_gym.agents.util import MutableFloat, MutableParams from openmodelica_microgrid_gym.aux_ctl import PI_params, MultiPhaseDQCurrentSourcingController from openmodelica_microgrid_gym.env import PlotTmpl from openmodelica_microgrid_gym.env.plotmanager import PlotManager from openmodelica_microgrid_gym.net import Network from openmodelica_microgrid_gym.util import FullHistory # Plotting params params = {'backend': 'ps', 'text.latex.preamble': [r'\usepackage{gensymb}' r'\usepackage{amsmath,amssymb,mathtools}' r'\newcommand{\mlutil}{\ensuremath{\operatorname{ml-util}}}' r'\newcommand{\mlacc}{\ensuremath{\operatorname{ml-acc}}}'], 'axes.labelsize': 8, # fontsize for x and y labels (was 10) 'axes.titlesize': 8, 'font.size': 8, # was 10 'legend.fontsize': 8, # was 10 'xtick.labelsize': 8, 'ytick.labelsize': 8, 'text.usetex': True, 'figure.figsize': [3.9, 3.1], 'font.family': 'serif', 'lines.linewidth': 1 } matplotlib.rcParams.update(params) # Choose which controller parameters should be adjusted by SafeOpt. # - Kp: 1D example: Only the proportional gain Kp of the PI controller is adjusted # - Ki: 1D example: Only the integral gain Ki of the PI controller is adjusted # - Kpi: 2D example: Kp and Ki are adjusted simultaneously adjust = 'Kpi' # Check if really only one simulation scenario was selected if adjust not in {'Kp', 'Ki', 'Kpi'}: raise ValueError("Please set 'adjust' to one of the following values: 'Kp', 'Ki', 'Kpi'") include_simulate = True show_plots = True balanced_load = True do_measurement = False save_results = False # Files saves results and resulting plots to the folder saves_VI_control_safeopt in the current directory current_directory = os.getcwd() save_folder = os.path.join(current_directory, r'../1Test') os.makedirs(save_folder, exist_ok=True) np.random.seed(1) # Simulation definitions net = Network.load('../../net/net_single-inv-curr_Paper_SC.yaml') delta_t = 1e-4 # simulation time step size / s undersample = 1 # undersampling of controller max_episode_steps = 1000 # number of simulation steps per episode num_episodes = 1 # number of simulation episodes (i.e. SafeOpt iterations) n_MC = 1 # number of Monte-Carlo samples for simulation - samples device parameters (e.g. L,R, noise) from iLimit = 16 # inverter current limit / A iNominal = 12 # nominal inverter current / A mu = 80 # factor for barrier function (see below) i_ref1 = np.array([10, 0, 0]) # exemplary set point i.e. id = 10, iq = 0, i0 = 0 / A i_ref2 = np.array([5, 0, 0]) # exemplary set point i.e. id = 15, iq = 0, i0 = 0 / A # plant L = 2.3e-3 # / H R = 400e-3 # / Ohm phase_shift = 5 amp_dev = 1.1 def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = tee(iterable) next(b, None) return zip(a, b) def cal_j_min(phase_shift, amp_dev): """ Calulated the miminum performance for safeopt Best case error of all safe boundary scenarios is used (max) to indicate which typ of error tears the safe boarder first (the weakest link in the chain) """ ph_list = [phase_shift, 0] amp_list = [1, amp_dev] return_j_min = np.empty(len(ph_list)) error_j_min = np.empty(3) ph_shift = [0, 120, 240] t = np.linspace(0, max_episode_steps * delta_t, max_episode_steps) # risetime = 0.0015 -> 15 steps um auf 10A zu kommen: grade = 10/15 grad = 0.66 # 1e-1 irefs = [0, i_ref1[0], i_ref2[0]] ts = [0, max_episode_steps // 2, max_episode_steps] for q in range(len(ph_list)): for p in range(3): amplitude_sp = np.concatenate([np.full(t1 - t0, r1) for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))]) amplitude = np.concatenate( [np.minimum( r0 + grad * np.arange(0, t1 - t0), # ramp up phase np.full(t1 - t0, r1) # max amplitude ) for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))]) mess = amp_list[q] * amplitude * np.cos( 2 * np.pi * 50 * t + (ph_list[q] * np.pi / 180) + (ph_shift[p] * np.pi / 180)) sp = amplitude_sp * np.cos(2 * np.pi * 50 * t + (ph_shift[p] * np.pi / 180)) error_j_min[p] = -np.sum((np.abs((sp - mess)) / iLimit) ** 0.5, axis=0) / max_episode_steps return_j_min[q] = np.sum(error_j_min) # Sum all 3 phases return max(return_j_min) if __name__ == '__main__': rew = Reward(i_limit=iLimit, i_nominal=iNominal, mu_c=mu, max_episode_steps=max_episode_steps, obs_dict=[[f'lc.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0']]) ##################################### # Definitions for the GP prior_mean = 0 # 2 # mean factor of the GP prior mean which is multiplied with the first performance of the # initial set noise_var = 0.001 # 0.001 ** 2 # measurement noise sigma_omega prior_var = 2 # prior variance of the GP bounds = None lengthscale = None if adjust == 'Kp': bounds = [(0.0001, 0.1)] # bounds on the input variable Kp lengthscale = [.025] # length scale for the parameter variation [Kp] for the GP # For 1D example, if Ki should be adjusted if adjust == 'Ki': bounds = [(0, 20)] # bounds on the input variable Ki lengthscale = [10] # length scale for the parameter variation [Ki] for the GP # For 2D example, choose Kp and Ki as mutable parameters (below) and define bounds and lengthscale for both of them if adjust == 'Kpi': bounds = [(0.001, 0.07), (2, 150)] lengthscale = [0.012, 30.] df_len = pd.DataFrame({'lengthscale': lengthscale, 'bounds': bounds, 'balanced_load': balanced_load, 'barrier_param_mu': mu}) # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times # the initial performance: safe_threshold = 0.8 means. Performance measurement for optimization are seen as # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!) # parameter set safe_threshold = 0 j_min = cal_j_min(phase_shift, amp_dev) # Used for normalization # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop # expanding points eventually. # The following variable is multiplied with the first performance of the initial set by the factor below: explore_threshold = 0 # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of # limit exceeded # has to be negative due to normalized performance (regarding J_init = 1) abort_reward = 100 * j_min # Definition of the kernel kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True) ##################################### # Definition of the controllers mutable_params = None current_dqp_iparams = None if adjust == 'Kp': # mutable_params = parameter (Kp gain of the current controller of the inverter) to be optimized using # the SafeOpt algorithm mutable_params = dict(currentP=MutableFloat(0.04)) # Define the PI parameters for the current controller of the inverter current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=12, limits=(-1, 1)) # For 1D example, if Ki should be adjusted elif adjust == 'Ki': mutable_params = dict(currentI=MutableFloat(5)) current_dqp_iparams = PI_params(kP=0.005, kI=mutable_params['currentI'], limits=(-1, 1)) # For 2D example, choose Kp and Ki as mutable parameters elif adjust == 'Kpi': mutable_params = dict(currentP=MutableFloat(0.04), currentI=MutableFloat(11.8)) current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'], limits=(-1, 1)) # Define a current sourcing inverter as master inverter using the pi and droop parameters from above ctrl = MultiPhaseDQCurrentSourcingController(current_dqp_iparams, ts_sim=delta_t, ts_ctrl=undersample * delta_t, name='master', f_nom=net.freq_nom) i_ref = MutableParams([MutableFloat(f) for f in i_ref1]) ##################################### # Definition of the optimization agent # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example # Arguments described above # History is used to store results agent = SafeOptAgent(mutable_params, abort_reward, j_min, kernel, dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold, explore_threshold=explore_threshold), [ctrl], dict(master=[[f'lc.inductor{k}.i' for k in '123'], i_ref]), history=FullHistory() ) ##################################### # Definition of the environment using a FMU created by OpenModelica # (https://www.openmodelica.org/) # Using an inverter supplying a load # - using the reward function described above as callable in the env # - viz_cols used to choose which measurement values should be displayed (here, only the 3 currents across the # inductors of the inverters are plotted. Labels and grid is adjusted using the PlotTmpl (For more information, # see UserGuide) # - inputs to the models are the connection points to the inverters (see user guide for more details) # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors if include_simulate: # Defining unbalanced loads sampling from Gaussian distribution with sdt = 0.2*mean r_load = Load(R, 0.1 * R, balanced=balanced_load, tolerance=0.1) l_load = Load(L, 0.1 * L, balanced=balanced_load, tolerance=0.1) # i_noise = Noise([0, 0, 0], [0.0023, 0.0015, 0.0018], 0.0005, 0.32) # if no noise should be included: # r_load = Load(R, 0 * R, balanced=balanced_load) # l_load = Load(L, 0 * L, balanced=balanced_load) # i_noise = Noise([0, 0, 0], [0.0, 0.0, 0.0], 0.0, 0.0) def reset_loads(): r_load.reset() l_load.reset() plotter = PlotManager(agent, save_results=save_results, save_folder=save_folder, show_plots=show_plots) def reference_step(t): if t >= .05: i_ref[:] = i_ref2 else: i_ref[:] = i_ref1 return partial(l_load.give_value, n=2)(t) env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', # reward_fun=Reward().rew_fun, reward_fun=rew.rew_fun_c, # time_step=delta_t, viz_cols=[ PlotTmpl([[f'lc.inductor{i}.i' for i in '123'], [f'master.SPI{i}' for i in 'abc']], callback=plotter.xylables_i_abc, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ), PlotTmpl([[f'master.m{i}' for i in 'abc']], callback=lambda fig: plotter.update_axes(fig, title='Simulation', ylabel='$m_{\mathrm{abc}}\,/\,\mathrm{}$') ), PlotTmpl([[f'master.CVI{i}' for i in 'dq0'], [f'master.SPI{i}' for i in 'dq0']], callback=plotter.xylables_i_dq0, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ) ], log_level=logging.INFO, viz_mode='episode', max_episode_steps=max_episode_steps, model_params={'lc.resistor1.R': partial(r_load.give_value, n=0), 'lc.resistor2.R': partial(r_load.give_value, n=1), 'lc.resistor3.R': partial(r_load.give_value, n=2), 'lc.inductor1.L': partial(l_load.give_value, n=0), 'lc.inductor2.L': partial(l_load.give_value, n=1), 'lc.inductor3.L': reference_step}, model_path='../../omg_grid/grid.paper.fmu', # model_path='../omg_grid/omg_grid.Grids.Paper_SC.fmu', net=net, history=FullHistory(), action_time_delay=1 * undersample ) runner = MonteCarloRunner(agent, env) runner.run(num_episodes, n_mc=n_MC, visualise=True, prepare_mc_experiment=reset_loads) ##################################### # Results best_agent_plt = runner.run_data['last_agent_plt'] ax = best_agent_plt.axes[0] ax.grid(which='both') ax.set_axisbelow(True) if adjust == 'Ki': ax.set_xlabel(r'$K_\mathrm{i}\,/\,\mathrm{(VA^{-1}s^{-1})}$') ax.set_ylabel(r'$J$') ax.set_ylim([-0.5, 1.5]) elif adjust == 'Kp': ax.set_xlabel(r'$K_\mathrm{p}\,/\,\mathrm{(VA^{-1})}$') ax.set_ylabel(r'$J$') elif adjust == 'Kpi': agent.params.reset() ax.set_ylabel(r'$K_\mathrm{i}\,/\,\mathrm{(VA^{-1}s^{-1})}$') ax.set_xlabel(r'$K_\mathrm{p}\,/\,\mathrm{(VA^{-1})}$') ax.get_figure().axes[1].set_ylabel(r'$J$') plt.title('Lengthscale = {}; balanced = '.format(lengthscale, balanced_load)) # ax.plot([mutable_params['currentP'].val, mutable_params['currentP'].val], bounds[1], 'k-', zorder=1, # lw=4, # alpha=.5) best_agent_plt.show() if save_results: best_agent_plt.savefig(save_folder + '/_agent_plt.pdf') best_agent_plt.savefig(save_folder + '/_agent_plt.pgf') agent.history.df.to_csv(save_folder + '/_result.csv') df_len.to_csv(save_folder + '/_params.csv') print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df[:])) print('\n Experiment finished with best set: \n') print('\n {} = {}'.format(adjust, agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') print(agent.unsafe) if do_measurement: ##################################### # Execution of the experiment # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations) env = TestbenchEnv(num_steps=max_episode_steps, DT=1 / 10000, ref=10, ref2=5, i_limit=iLimit, i_nominal=iNominal, f_nom=60, mu=mu) runner = RunnerHardware(agent, env) runner.run(num_episodes, visualise=True, save_folder=save_folder) print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df[:])) print('\n Experiment finished with best set: \n') print('\n {} = {}'.format(adjust, agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') if save_results: agent.history.df.to_csv(save_folder + '/_meas_result.csv') df_len.to_csv(save_folder + '/_meas_params.csv') # Show last performance plot best_agent_plt = runner.run_data['last_agent_plt'] ax = best_agent_plt.axes[0] ax.grid(which='both') ax.set_axisbelow(True) if adjust == 'Ki': ax.set_xlabel(r'$K_\mathrm{i}\,/\,\mathrm{(VA^{-1}s^{-1})}$') ax.set_ylabel(r'$J$') elif adjust == 'Kp': ax.set_xlabel(r'$K_\mathrm{p}\,/\,\mathrm{(VA^{-1})}$') ax.set_ylabel(r'$J$') elif adjust == 'Kpi': agent.params.reset() ax.set_ylabel(r'$K_\mathrm{i}\,/\,\mathrm{(VA^{-1}s^{-1})}$') ax.set_xlabel(r'$K_\mathrm{p}\,/\,\mathrm{(VA^{-1})}$') ax.get_figure().axes[1].set_ylabel(r'$J$') # plt.plot(bounds[0], [mutable_params['currentP'].val, mutable_params['currentP'].val], 'k-', zorder=1, # lw=4, # alpha=.5) best_agent_plt.show() if save_results: best_agent_plt.savefig(save_folder + '/_meas_agent_plt.pgf') best_agent_plt.savefig(save_folder + '/_meas_agent_plt.pdf') ================================================ FILE: experiments/model_validation/single_inverter_voltage_current_control_safe_opt_includingTB.py ================================================ ##################################### # Experiment : Single voltage forming inverter supplying an RL-load via an LC-filter # Controller: Cascaded PI-PI voltage and current controller gain parameters are optimized by SafeOpt # a) FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters # b) connecting via ssh to a testbench to perform real-world measurement import logging import os from functools import partial from itertools import tee import GPy import gym import matplotlib import matplotlib.pyplot as plt import numpy as np import pandas as pd from experiments.model_validation.env.testbench_voltage_ctrl import TestbenchEnvVoltage from experiments.model_validation.execution.monte_carlo_runner import MonteCarloRunner from experiments.model_validation.execution.runner_hardware import RunnerHardwareGradient from openmodelica_microgrid_gym.agents import SafeOptAgent from openmodelica_microgrid_gym.agents.util import MutableFloat from openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, \ MultiPhaseDQ0PIPIController from openmodelica_microgrid_gym.env import PlotTmpl from openmodelica_microgrid_gym.env.plotmanager import PlotManager from experiments.model_validation.env.rewards import Reward from experiments.model_validation.env.stochastic_components import Load from openmodelica_microgrid_gym.net import Network from openmodelica_microgrid_gym.util import FullHistory # Plot setting params = {'backend': 'ps', 'text.latex.preamble': [r'\usepackage{gensymb}' r'\usepackage{amsmath,amssymb,mathtools}' r'\newcommand{\mlutil}{\ensuremath{\operatorname{ml-util}}}' r'\newcommand{\mlacc}{\ensuremath{\operatorname{ml-acc}}}'], 'axes.labelsize': 8, # fontsize for x and y labels (was 10) 'axes.titlesize': 8, 'font.size': 8, # was 10 'legend.fontsize': 8, # was 10 'xtick.labelsize': 8, 'ytick.labelsize': 8, 'text.usetex': True, 'figure.figsize': [3.9, 3.1], 'font.family': 'serif', 'lines.linewidth': 1 } matplotlib.rcParams.update(params) include_simulate = True show_plots = True balanced_load = False do_measurement = False save_results = False # Files saves results and resulting plots to the folder saves_VI_control_safeopt in the current directory current_directory = os.getcwd() save_folder = os.path.join(current_directory, r'VSim_rebase2_MC3') os.makedirs(save_folder, exist_ok=True) np.random.seed(1) # Simulation definitions net = Network.load('../../net/net_single-inv-Paper_Loadstep.yaml') delta_t = 1e-4 # simulation time step size / s undersample = 1 max_episode_steps = 2000 # number of simulation steps per episode num_episodes = 1 # number of simulation episodes (i.e. SafeOpt iterations) n_MC = 1 # number of Monte-Carlo samples for simulation - samples device parameters (e.g. L,R, noise) from v_DC = 600 # DC-link voltage / V; will be set as model parameter in the FMU nomFreq = 60 # nominal grid frequency / Hz nomVoltPeak = 169.7 # 230 * 1.414 # nominal grid voltage / V iLimit = 16 # inverter current limit / A iNominal = 12 # nominal inverter current / A vNominal = 190 # nominal inverter current / A vLimit = vNominal * 1.5 # inverter current limit / A funnelFactor = 0.02 vFunnel = np.array([vNominal * funnelFactor, vNominal * funnelFactor, vNominal * funnelFactor]) mu = 400 # factor for barrier function (see below) DroopGain = 0.0 # virtual droop gain for active power / W/Hz QDroopGain = 0.0 # virtual droop gain for reactive power / VAR/V # plant L_filter = 2.3e-3 # / H R_filter = 400e-3 # / Ohm C_filter = 10e-6 # / F R = 28 # nomVoltPeak / 7.5 # / Ohm phase_shift = 5 amp_dev = 1.1 # Observer matrices A = np.array([[-R_filter, -1 / L_filter, 0], [1 / C_filter, 0, -1 / C_filter], [0, 0, 0]]) B = np.array([[1 / L_filter, 0, 0]]).T C = np.array([[1, 0, 0], [0, 1, 0]]) # Observer values L_iL_iL = 2e3 L_vc_iL = -435 # influence from delta_y_vc onto xdot = iL L_iL_vc = 100229 L_vc_vc = 4000 L_iL_io = -13.22 L_vc_io = -80 L = np.array([[L_iL_iL, L_vc_iL], [L_iL_vc, L_vc_vc], [L_iL_io, L_vc_io]]) def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = tee(iterable) next(b, None) return zip(a, b) def cal_J_min(phase_shift, amp_dev): """ Calulated the miminum performance for safeopt Best case error of all safe boundary scenarios is used (max) to indicate which typ of error tears the safe boarder first (the weakest link in the chain) """ ph_list = [phase_shift, 0] amp_list = [1, amp_dev] return_Jmin = np.empty(len(ph_list)) error_Jmin = np.empty(3) ph_shift = [0, 120, 240] t = np.linspace(0, max_episode_steps * delta_t, max_episode_steps) grad = 0.3 irefs = [0, nomVoltPeak, nomVoltPeak] ts = [0, max_episode_steps // 2, max_episode_steps] noiseH = 0.02 * np.sin(2 * np.pi * 1500 * t) # add noise for dev return for l in range(len(ph_list)): for p in range(3): amplitudeSP = np.concatenate([np.full(t1 - t0, r1) for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))]) amplitude = np.concatenate( [np.minimum( r0 + grad * np.arange(0, t1 - t0), # ramp up phase np.full(t1 - t0, r1) # max amplitude ) for (r0, t0), (r1, t1) in pairwise(zip(irefs, ts))]) Mess = noiseH * amplitude + amp_list[l] * amplitude * np.cos( 2 * np.pi * 60 * t + (ph_list[l] * np.pi / 180) + (ph_shift[p] * np.pi / 180)) SP = amplitudeSP * np.cos(2 * np.pi * 60 * t + (ph_shift[p] * np.pi / 180)) error_Jmin[p] = -np.sum((np.abs((SP - Mess)) / vLimit) ** 0.5, axis=0) / max_episode_steps w2 = noiseH * amplitude + amplitude dw2 = np.gradient(w2) SP_sattle = (amplitude > amplitudeSP * (1 - 0.12)).astype(int) error2 = -np.mean(abs(SP_sattle * dw2)) error_Jmin[p] += error2 * .5 # add gradient error return_Jmin[l] = np.sum(error_Jmin) # Sum all 3 phases return max(return_Jmin) if __name__ == '__main__': rew = Reward(i_limit=iLimit, i_nominal=iNominal, mu_v=mu, max_episode_steps=max_episode_steps, v_limit=vLimit, v_nominal=vNominal, obs_dict=[[f'lc.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'], [f'lc.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0'], [f'master.CVV{k}' for k in 'dq0']]) ##################################### # Definitions for the GP prior_mean = 0 # 2 # mean factor of the GP prior mean which is multiplied with the first performance of the initial set noise_var = 0.001 # ** 2 # measurement noise sigma_omega prior_var = 2 # prior variance of the GP # Choose Kp and Ki (current and voltage controller) as mutable parameters (below) and define bounds and lengthscale # for both of them bounds = [(0.000, 0.045), (4, 450)] # bounds on the input variable current-Ki&Kp and voltage-Ki&Kp lengthscale = [.003, 50.] # length scale for the parameter variation [current-Ki&Kp and voltage-Ki&Kp] for the GP # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times # the initial performance: safe_threshold = 1.2 means: performance measurement for optimization are seen as # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!) # parameter set safe_threshold = 0 j_min = cal_J_min(phase_shift, amp_dev) # cal min allowed performance # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop # expanding points eventually. # The following variable is multiplied with the first performance of the initial set by the factor below: explore_threshold = 0 # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of # limit exceeded abort_reward = 100 * j_min # Definition of the kernel kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True) ##################################### # Definition of the controllers # Choose Kp and Ki for the current and voltage controller as mutable parameters mutable_params = dict(voltageP=MutableFloat(0.0175), voltageI=MutableFloat(12)) # 300Hz voltage_dqp_iparams = PI_params(kP=mutable_params['voltageP'], kI=mutable_params['voltageI'], limits=(-iLimit, iLimit)) kp_c = 0.04 ki_c = 11.8 current_dqp_iparams = PI_params(kP=kp_c, kI=ki_c, limits=(-1, 1)) # Current controller values # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for the # filter and the nominal frequency # Droop controller used to calculate the virtual frequency drop due to load changes droop_param = DroopParams(DroopGain, 0.005, net.freq_nom) # Define the Q-droop parameters for the inverter of the reactive power VAR/Volt, delta_t (0.002) used for the # filter and the nominal voltage qdroop_param = DroopParams(QDroopGain, 0.002, net.v_nom) # Define a voltage forming inverter using the PIPI and droop parameters from above # Controller with observer # ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, delta_t, droop_param, qdroop_param, # observer=[Lueneberger(*params) for params in # repeat((A, B, C, L, delta_t * undersample, v_DC / 2), 3)], undersampling=undersample, # name='master') # Controller without observer ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param, ts_sim=delta_t, ts_ctrl=undersample * delta_t, name='master') ##################################### # Definition of the optimization agent # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example # Arguments described above # History is used to store results agent = SafeOptAgent(mutable_params, abort_reward, j_min, kernel, dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold, explore_threshold=explore_threshold), [ctrl], dict(master=[[f'lc.inductor{k}.i' for k in '123'], [f'lc.capacitor{k}.v' for k in '123'] ]), history=FullHistory(), ) if include_simulate: ##################################### # Definition of the environment using a FMU created by OpenModelica # (https://www.openmodelica.org/) # Using an inverter supplying a load # - using the reward function described above as callable in the env # - viz_cols used to choose which measurement values should be displayed. # Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide) # generated figures are stored to file # - inputs to the models are the connection points to the inverters (see user guide for more details) # - model outputs are the 3 currents through the inductors and the 3 voltages across the capacitors # If without noise: # r_filt = Load(R_filt, 0 * R_filt, balanced=balanced_load) # l_filt = Load(L_filt, 0 * L_filt, balanced=balanced_load) # c_filt = Load(C_filt, 0 * C_filt, balanced=balanced_load) # r_load = Load(R, 0 * R, balanced=balanced_load) # 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) r_filt = Load(R_filter, 0.1 * R_filter, balanced=balanced_load) l_filt = Load(L_filter, 0.1 * L_filter, balanced=balanced_load) c_filt = Load(C_filter, 0.1 * C_filter, balanced=balanced_load) r_load = Load(R, 0.1 * R, balanced=balanced_load) # meas_noise = Noise([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # [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) def reset_loads(): r_load.reset() r_filt.reset() l_filt.reset() c_filt.reset() plotter = PlotManager(agent, save_results=save_results, save_folder=save_folder, show_plots=show_plots) env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', reward_fun=rew.rew_fun_v, viz_cols=[ PlotTmpl([[f'lc.capacitor{i}.v' for i in '123'], [f'master.SPV{i}' for i in 'abc']], callback=plotter.xylables_v_abc, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ), PlotTmpl([[f'master.CVV{i}' for i in 'dq0'], [f'master.SPV{i}' for i in 'dq0']], callback=plotter.xylables_v_dq0, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ), PlotTmpl([[f'lc.inductor{i}.i' for i in '123'], [f'master.SPI{i}' for i in 'abc']], callback=plotter.xylables_i_abc, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ), # PlotTmpl([[f'master.I_hat{i}' for i in 'abc'], [f'r_load.resistor{i}.i' for i in '123'], ], # callback=lambda fig: plotter.update_axes(fig, title='Simulation', # ylabel='$i_{\mathrm{o estimate,abc}}\,/\,\mathrm{A}$'), # color=[['b', 'r', 'g'], ['b', 'r', 'g']], # style=[['-*'], ['--*']] # ), # PlotTmpl([[f'master.m{i}' for i in 'dq0']], # callback=lambda fig: plotter.update_axes(fig, title='Simulation', # ylabel='$m_{\mathrm{dq0}}\,/\,\mathrm{}$', # filename='Sim_m_dq0') # ), PlotTmpl([[f'master.CVi{i}' for i in 'dq0'], [f'master.SPI{i}' for i in 'dq0']], callback=plotter.xylables_i_dq0, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ) ], log_level=logging.INFO, viz_mode='episode', max_episode_steps=max_episode_steps, model_params={'lc.resistor1.R': partial(r_filt.give_value, n=0), 'lc.resistor2.R': partial(r_filt.give_value, n=1), 'lc.resistor2.R': partial(r_filt.give_value, n=1), 'lc.resistor3.R': partial(r_filt.give_value, n=2), 'lc.resistor4.R': 0.0000001, 'lc.resistor5.R': 0.0000001, 'lc.resistor6.R': 0.0000001, 'lc.inductor1.L': partial(l_filt.give_value, n=0), 'lc.inductor2.L': partial(l_filt.give_value, n=1), 'lc.inductor3.L': partial(l_filt.give_value, n=2), 'lc.capacitor1.C': partial(c_filt.give_value, n=0), 'lc.capacitor2.C': partial(c_filt.give_value, n=1), 'lc.capacitor3.C': partial(c_filt.give_value, n=2), 'r_load.resistor1.R': partial(r_load.load_step, n=0), 'r_load.resistor2.R': partial(r_load.load_step, n=1), 'r_load.resistor3.R': partial(r_load.load_step, n=2), }, net=net, model_path='../../omg_grid/grid.paper_loadstep.fmu', history=FullHistory(), action_time_delay=1 * undersample ) runner = MonteCarloRunner(agent, env) runner.run(num_episodes, n_mc=n_MC, visualise=True, prepare_mc_experiment=reset_loads, return_gradient_extend=True) df_len = pd.DataFrame({'lengthscale': lengthscale, 'bounds': bounds, 'balanced_load': balanced_load, 'barrier_param_mu': mu, 'J_min': j_min}) if save_results: agent.history.df.to_csv(save_folder + '/_result.csv') df_len.to_csv(save_folder + '/_params.csv') best_agent_plt = runner.run_data['last_agent_plt'] ax = best_agent_plt.axes[0] ax.grid(which='both') ax.set_axisbelow(True) agent.params.reset() ax.set_ylabel(r'$K_\mathrm{i}\,/\,\mathrm{(AV^{-1}s^{-1})}$') ax.set_xlabel(r'$K_\mathrm{p}\,/\,\mathrm{(AV^{-1})}$') ax.get_figure().axes[1].set_ylabel(r'$J$') plt.title('Lengthscale = {}; balanced = '.format(lengthscale, balanced_load)) # ax.plot([0.01, 0.01], [0, 250], 'k') # ax.plot([mutable_params['currentP'].val, mutable_params['currentP'].val], bounds[1], 'k-', zorder=1, # lw=4, # alpha=.5) best_agent_plt.show() if save_results: best_agent_plt.savefig(save_folder + '/_agent_plt.pdf') best_agent_plt.savefig(save_folder + '/_agent_plt.pgf') agent.history.df.to_csv(save_folder + '/_result.csv') print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4}))) print('\n Experiment finished with best set: \n') print('\n Current-Ki&Kp and voltage-Ki&Kp = {}'.format( agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') if do_measurement: ##################################### # Execution of the experiment # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations) env = TestbenchEnvVoltage(num_steps=max_episode_steps, DT=1 / 10000, v_nominal=nomVoltPeak, kP=kp_c, kI=ki_c, v_limit=vLimit, f_nom=nomFreq, mu=mu) # runner = RunnerHardware(agent, env) runner = RunnerHardwareGradient(agent, env) runner.run(num_episodes, visualise=True, save_folder=save_folder) print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df[:])) print('\n Experiment finished with best set: \n') print('\n Current-Kp&Ki and voltage-Kp&Ki = {}'.format( agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') df_len = pd.DataFrame({'lengthscale': lengthscale, 'bounds': bounds, 'balanced_load': balanced_load, 'barrier_param_mu': mu, 'J_min': j_min}) if save_results: agent.history.df.to_csv(save_folder + '/_meas_result.csv') df_len.to_csv(save_folder + '/_meas_params.csv') # Show last performance plot best_agent_plt = runner.run_data['last_agent_plt'] ax = best_agent_plt.axes[0] ax.grid(which='both') ax.set_axisbelow(True) agent.params.reset() ax.set_ylabel(r'$K_\mathrm{i}\,/\,\mathrm{(AV^{-1}s^{-1})}$') ax.set_xlabel(r'$K_\mathrm{p}\,/\,\mathrm{(AV^{-1})}$') ax.get_figure().axes[1].set_ylabel(r'$J$') # plt.plot(bounds[0], [mutable_params['currentP'].val, mutable_params['currentP'].val], 'k-', zorder=1, # lw=4, # alpha=.5) best_agent_plt.show() if save_results: best_agent_plt.savefig(save_folder + '/_meas_agent_plt.png') best_agent_plt.savefig(save_folder + '/_meas_agent_plt.pdf') best_agent_plt.savefig(save_folder + '/_meas_agent_plt.pgf') ================================================ FILE: experiments/model_validation/single_inverter_voltage_current_control_safe_opt_includingTB_4D.py ================================================ ##################################### # Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters # Simulation setup: Single voltage forming inverter supplying an RL-load via an LC-filter # Controller: Cascaded PI-PI voltage and current controller gain parameters are optimized by SafeOpt import logging import os from functools import partial import GPy import gym import matplotlib import numpy as np import pandas as pd from openmodelica_microgrid_gym.env.plotmanager import PlotManager from experiments.model_validation.env.rewards import Reward params = {'backend': 'ps', 'text.latex.preamble': [r'\usepackage{gensymb}' r'\usepackage{amsmath,amssymb,mathtools}' r'\newcommand{\mlutil}{\ensuremath{\operatorname{ml-util}}}' r'\newcommand{\mlacc}{\ensuremath{\operatorname{ml-acc}}}'], 'axes.labelsize': 8, # fontsize for x and y labels (was 10) 'axes.titlesize': 8, 'font.size': 8, # was 10 'legend.fontsize': 8, # was 10 'xtick.labelsize': 8, 'ytick.labelsize': 8, 'text.usetex': True, 'figure.figsize': [3.9, 3.1], 'font.family': 'serif', 'lines.linewidth': 1 } matplotlib.rcParams.update(params) from openmodelica_microgrid_gym.agents import SafeOptAgent from openmodelica_microgrid_gym.agents.util import MutableFloat from openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, MultiPhaseDQ0PIPIController from openmodelica_microgrid_gym.env import PlotTmpl from experiments.model_validation.env.stochastic_components import Load from experiments.model_validation.env.testbench_voltage_ctrl import TestbenchEnvVoltage from experiments.model_validation.execution.monte_carlo_runner import MonteCarloRunner from experiments.model_validation.execution.runner_hardware import RunnerHardware, RunnerHardwareGradient from openmodelica_microgrid_gym.net import Network from openmodelica_microgrid_gym.util import FullHistory include_simulate = True show_plots = False balanced_load = False do_measurement = False save_results = True current_directory = os.getcwd() save_folder = os.path.join(current_directory, r'4Dtest1') os.makedirs(save_folder, exist_ok=True) np.random.seed(1) # Simulation definitions net = Network.load('../../net/net_single-inv-Paper_Loadstep.yaml') delta_t = 1e-4 # simulation time step size / s undersample = 1 max_episode_steps = 2000 # number of simulation steps per episode num_episodes = 60 # number of simulation episodes (i.e. SafeOpt iterations) n_MC = 10 # number of Monte-Carlo samples for simulation - samples device parameters (e.g. L,R, noise) from v_DC = 600 # DC-link voltage / V; will be set as model parameter in the FMU nomFreq = 60 # nominal grid frequency / Hz nomVoltPeak = 169.7 # 230 * 1.414 # nominal grid voltage / V iLimit = 16 # inverter current limit / A iNominal = 12 # nominal inverter current / A vNominal = 190 # nominal inverter current / A vLimit = vNominal * 1.5 # inverter current limit / A funnelFactor = 0.02 vFunnel = np.array([vNominal * funnelFactor, vNominal * funnelFactor, vNominal * funnelFactor]) mu = 400 # factor for barrier function (see below) mu_cc = 80 DroopGain = 0.0 # virtual droop gain for active power / W/Hz QDroopGain = 0.0 # virtual droop gain for reactive power / VAR/V # plant L_filter = 2.3e-3 # / H R_filter = 400e-3 # / Ohm C_filter = 10e-6 # / F R = 28 # nomVoltPeak / 7.5 # / Ohm phase_shift = 5 amp_dev = 1.1 # Observer matrices A = np.array([[-R_filter, -1 / L_filter, 0], [1 / C_filter, 0, -1 / C_filter], [0, 0, 0]]) B = np.array([[1 / L_filter, 0, 0]]).T C = np.array([[1, 0, 0], [0, 1, 0]]) # Observer values L_iL_iL = 2e3 L_vc_iL = -435 # influence from delta_y_vc onto xdot = iL L_iL_vc = 100229 L_vc_vc = 4000 L_iL_io = -13.22 L_vc_io = -80 L = np.array([[L_iL_iL, L_vc_iL], [L_iL_vc, L_vc_vc], [L_iL_io, L_vc_io]]) if __name__ == '__main__': rew = Reward(i_limit=iLimit, i_nominal=iNominal, mu_c=mu_cc, mu_v=mu, max_episode_steps=max_episode_steps, v_limit=vLimit, v_nominal=vNominal, obs_dict=[[f'lc.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'], [f'lc.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0'], [f'master.CVV{k}' for k in 'dq0']]) ##################################### # Definitions for the GP prior_mean = 0 # 2 # mean factor of the GP prior mean which is multiplied with the first performance of the initial set noise_var = 0.001 # ** 2 # measurement noise sigma_omega prior_var = 2 # prior variance of the GP # Choose Kp and Ki (current and voltage controller) as mutable parameters (below) and define bounds and lengthscale # for both of them bounds = [(0.001, 0.07), (2, 150), (0.000, 0.045), (4, 450)] # bounds on the input variable current-Ki&Kp and voltage-Ki&Kp lengthscale = [0.005, 25., .003, 50.] # length scale for the parameter variation [current-Ki&Kp and voltage-Ki&Kp] for the GP # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times # the initial performance: safe_threshold = 1.2 means: performance measurement for optimization are seen as # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!) # parameter set safe_threshold = 0 j_min = -10 # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop # expanding points eventually. # The following variable is multiplied with the first performance of the initial set by the factor below: explore_threshold = 0 # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of # limit exceeded abort_reward = 100 * j_min # Definition of the kernel kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True) ##################################### # Definition of the controllers # Choose Kp and Ki for the current and voltage controller as mutable parameters # mutable_params = dict(currentP=MutableFloat(10e-3), currentI=MutableFloat(10), voltageP=MutableFloat(0.05), # voltageI=MutableFloat(75)) # Vdc = 600 V mutable_params = dict(currentP=MutableFloat(0.04), currentI=MutableFloat(11.8), voltageP=MutableFloat(0.0175), voltageI=MutableFloat(12)) voltage_dqp_iparams = PI_params(kP=mutable_params['voltageP'], kI=mutable_params['voltageI'], limits=(-iLimit, iLimit)) current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'], limits=(-1, 1)) # Best set from paper III-D # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for the # filter and the nominal frequency # Droop controller used to calculate the virtual frequency drop due to load changes droop_param = DroopParams(DroopGain, 0.005, nomFreq) # Define the Q-droop parameters for the inverter of the reactive power VAR/Volt, delta_t (0.002) used for the # filter and the nominal voltage qdroop_param = DroopParams(QDroopGain, 0.002, nomVoltPeak) # Define a voltage forming inverter using the PIPI and droop parameters from above # Controller with observer # ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, delta_t, droop_param, qdroop_param, # observer=[Lueneberger(*params) for params in # repeat((A, B, C, L, delta_t * undersample, v_DC / 2), 3)], undersampling=undersample, # name='master') # Controller without observer ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, delta_t, droop_param, qdroop_param, undersampling=undersample, name='master') ##################################### # Definition of the optimization agent # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example # Arguments described above # History is used to store results agent = SafeOptAgent(mutable_params, abort_reward, j_min, kernel, dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold, explore_threshold=explore_threshold), [ctrl], dict(master=[[f'lc.inductor{k}.i' for k in '123'], [f'lc.capacitor{k}.v' for k in '123'] ]), history=FullHistory() ) if include_simulate: ##################################### # Definition of the environment using a FMU created by OpenModelica # (https://www.openmodelica.org/) # Using an inverter supplying a load # - using the reward function described above as callable in the env # - viz_cols used to choose which measurement values should be displayed. # Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide) # generated figures are stored to file # - inputs to the models are the connection points to the inverters (see user guide for more details) # - model outputs are the 3 currents through the inductors and the 3 voltages across the capacitors # Defining unbalanced loads sampling from Gaussian distribution with sdt = 0.2*mean # r_load = Load(R, 0.1 * R, balanced=balanced_load, tolerance=0.1) # l_load = Load(L, 0.1 * L, balanced=balanced_load, tolerance=0.1) # i_noise = Noise([0, 0, 0], [0.0822, 0.103, 0.136], 0.05, 0.2) # r_filt = Load(R_filt, 0 * R_filt, balanced=balanced_load) # l_filt = Load(L_filt, 0 * L_filt, balanced=balanced_load) # c_filt = Load(C_filt, 0 * C_filt, balanced=balanced_load) # r_load = Load(R, 0 * R, balanced=balanced_load) # 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) r_filt = Load(R_filter, 0.1 * R_filter, balanced=balanced_load) l_filt = Load(L_filter, 0.1 * L_filter, balanced=balanced_load) c_filt = Load(C_filter, 0.1 * C_filter, balanced=balanced_load) r_load = Load(R, 0.1 * R, balanced=balanced_load) # meas_noise = Noise([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # [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) def reset_loads(): r_load.reset() r_filt.reset() l_filt.reset() c_filt.reset() plotter = PlotManager(agent, save_results=save_results, save_folder=save_folder, show_plots=show_plots) env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', reward_fun=rew.rew_fun_vc, viz_cols=[ PlotTmpl([[f'lc.capacitor{i}.v' for i in '123'], [f'master.SPV{i}' for i in 'abc']], callback=plotter.xylables_v_abc, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ), PlotTmpl([[f'master.CVV{i}' for i in 'dq0'], [f'master.SPV{i}' for i in 'dq0']], callback=plotter.xylables_v_dq0, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ), PlotTmpl([[f'lc.inductor{i}.i' for i in '123'], [f'master.SPI{i}' for i in 'abc']], callback=plotter.xylables_i_abc, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ), PlotTmpl([[f'master.I_hat{i}' for i in 'abc'], [f'r_load.resistor{i}.i' for i in '123'], ], callback=lambda fig: plotter.update_axes(fig, title='Simulation', ylabel='$i_{\mathrm{o estimate,abc}}\,/\,\mathrm{A}$'), color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[['-*'], ['--*']] ), PlotTmpl([[f'master.m{i}' for i in 'dq0']], callback=lambda fig: plotter.update_axes(fig, title='Simulation', ylabel='$m_{\mathrm{dq0}}\,/\,\mathrm{}$', filename='Sim_m_dq0') ), PlotTmpl([[f'master.CVi{i}' for i in 'dq0'], [f'master.SPI{i}' for i in 'dq0']], callback=plotter.xylables_i_dq0, color=[['b', 'r', 'g'], ['b', 'r', 'g']], style=[[None], ['--']] ) ], log_level=logging.INFO, viz_mode='episode', max_episode_steps=max_episode_steps, model_params={'lc.resistor1.R': R_filter, 'lc.resistor2.R': R_filter, 'lc.resistor3.R': R_filter, 'lc.resistor4.R': 0.0000001, 'lc.resistor5.R': 0.0000001, 'lc.resistor6.R': 0.0000001, 'lc.inductor1.L': L_filter, 'lc.inductor2.L': L_filter, 'lc.inductor3.L': L_filter, 'lc.capacitor1.C': partial(c_filt.load_step, n=0), 'lc.capacitor2.C': partial(c_filt.load_step, n=1), 'lc.capacitor3.C': partial(c_filt.load_step, n=2), 'r_load.resistor1.R': partial(r_load.load_step, n=0), 'r_load.resistor2.R': partial(r_load.load_step, n=1), 'r_load.resistor3.R': partial(r_load.load_step, n=2), }, net=net, model_path='../../omg_grid/grid.paper_loadstep.fmu', history=FullHistory(), action_time_delay=1 * undersample ) runner = MonteCarloRunner(agent, env) runner.run(num_episodes, n_mc=n_MC, visualise=True, prepare_mc_experiment=reset_loads, return_gradient_extend=True) env.history.df.to_hdf('env_hist_obs2plt.hd5', 'hist') # df2 = pd.read_hdf('env_hist_obs2plt.hd5', 'hist') print(agent.unsafe) df_len = pd.DataFrame({'lengthscale': lengthscale, 'bounds': bounds, 'balanced_load': balanced_load, 'barrier_param_mu': mu, 'J_min': j_min}) if save_results: agent.history.df.to_csv(save_folder + '/_result.csv') df_len.to_csv(save_folder + '/_params.csv') print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4}))) print('\n Experiment finished with best set: \n') print('\n Current-Ki&Kp and voltage-Ki&Kp = {}'.format( agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') if do_measurement: ##################################### # Execution of the experiment # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations) env = TestbenchEnvVoltage(num_steps=max_episode_steps, DT=1 / 10000, v_nominal=nomVoltPeak, v_limit=vLimit, f_nom=nomFreq, mu=mu) runner = RunnerHardware(agent, env) runner = RunnerHardwareGradient(agent, env) runner.run(num_episodes, visualise=True, save_folder=save_folder) print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df[:])) print('\n Experiment finished with best set: \n') print('\n Current-Kp&Ki and voltage-Kp&Ki = {}'.format( agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') df_len = pd.DataFrame({'lengthscale': lengthscale, 'bounds': bounds, 'balanced_load': balanced_load, 'barrier_param_mu': mu, 'J_min': j_min}) if save_results: agent.history.df.to_csv(save_folder + '/_meas_result.csv') df_len.to_csv(save_folder + '/_meas_params.csv') ================================================ FILE: experiments/testing_framework_control/metrics.py ================================================ from math import sqrt import numpy as np from scipy.signal import argrelextrema from sklearn.metrics import mean_squared_error ##################################### # Calculation of Metrics class Metrics: def __init__(self, quantity, ref_value: float, ts: float, max_episode_steps: int, position_steady_state: int = 0, position_settling_time: int = 0): """ :param quantity: analysed quantity from the history (e.g. current idq0) :param ref_value: setpoint of the quantity :param position_steady_state: step where quantity reached steady state :param position_settling_time: step where quantity reached settling time :param ts: absolute time resolution of the env :param max_episode_steps: number of simulation steps per episode """ self.quantity = quantity self.ts = ts self.ref_value = ref_value # upper bound --> important for settling time self.upper_bound = 1.02 * ref_value # lower bound --> important for settling time self.lower_bound = 0.98 * ref_value self.position_steady_state = position_steady_state self.position_settling_time = position_settling_time self.max_episode_steps = max_episode_steps # creates interval before load steps are applied self.interval_before_load_steps = self.quantity.iloc[0:position_steady_state] self.max_quantity = 0 # important for command 'argrelextrema' self.n = 5 def overshoot(self): """ calculation of overshoot :return: """ # tries to find all maxima before load steps are implemented self.interval_before_load_steps['max'] = \ self.interval_before_load_steps.iloc[ argrelextrema(self.interval_before_load_steps.values, np.greater_equal, order=self.n)[0]] # return the highest max self.max_quantity = self.interval_before_load_steps['max'].max() overshoot = (self.max_quantity / self.ref_value) - 1 if self.max_quantity > self.ref_value: return round(overshoot, 4) def rise_time(self): """ calculation of rise time :return: """ # 10% of its final value position_start_rise_time = self.quantity[self.quantity.iloc[:, 0] >= 0.1 * self.ref_value].index[0] # 90 % of its final value position_end_rise_time = self.quantity[self.quantity.iloc[:, 0] >= 0.9 * self.ref_value].index[0] position_rise_time = position_end_rise_time - position_start_rise_time rise_time_in_seconds = position_rise_time * self.ts return round(rise_time_in_seconds, 4) def settling_time(self): """ identification of settling time :return: """ if self.position_settling_time == 0: raise RuntimeError( "Steady State could not be reached. The controller need to be improved. PROGRAM EXECUTION STOP") # is calculated in the class LoadstepCallback return self.position_settling_time * self.ts # def settling_time_vd_droop(self): """ identification of settling time, only for vd in in tf_primarylevel_vdq_slavefreq.py :return: """ # interval_before_steady_state = self.quantity['master.CVVd'].iloc[0:self.position_steady_state] # find the beginning of the last period settled period # is_settled = False # for index, data in interval_before_steady_state.items(): # if self.lower_bound < data < self.upper_bound and not is_settled: # is_settled = True # self.position_settling_time_CVVd = index # else: # is_settled = False # if self.position_settling_time_CVVd == 0: # raise RuntimeError( # "Steady State could not be reached. The controller need to be improved. PROGRAM EXECUTION STOP") # return self.position_settling_time_CVVd * self.ts def settling_time_vd_droop( self): # identification of settling time, only for vd in in tf_primarylevel_vdq_slavefreq.py interval_before_steady_state = self.quantity['master.CVVd'].iloc[0:self.position_steady_state] for index, row in interval_before_steady_state.iteritems(): # iteration if row > self.lower_bound and row < self.upper_bound and self.settling_time_check == False: self.settling_time_check = True self.position_settling_time_CVVd = index if row < self.lower_bound or row > self.upper_bound: self.settling_time_check = False if self.position_settling_time_CVVd == 0: raise RuntimeError( "Steady State could not be reached. The controller need to be improved. PROGRAM EXECUTION STOP") settling_time_value = self.position_settling_time_CVVd * self.ts return settling_time_value def RMSE(self): # converts and reshapes it into an array Y_true = self.quantity.to_numpy().reshape(-1) # drops nan Y_true = Y_true[~np.isnan(Y_true)] # creates an list with the set value of the voltage and the length of the real voltages (Y_true) Y_pred = [self.ref_value] * (len(Y_true)) # converts this list into an array Y_pred = np.array(Y_pred) # returns the RMSE from sklearn return round(sqrt(mean_squared_error(Y_true, Y_pred)), 4) def steady_state_error(self): """ for a controller with an integral part, steady_state_error may be zero :return: """ # the last value of the quantity is stored last_value_quantity = self.quantity.iloc[self.max_episode_steps - 1] # calculation of the steady-state-error steady_state_error = np.abs(self.ref_value - last_value_quantity[0]) return round(steady_state_error, 4) def absolute_peak(self): """ absolute peak is calculated :return: """ max_quantity = self.quantity.abs().max() return round(max_quantity[0], 4) ================================================ FILE: experiments/testing_framework_control/net.yaml ================================================ v_nom: 230*sqrt(2) freq_nom: 50 ts: .5e-4 components: inv1: id: inverter1 #i_nom: 20 #i_lim: 30 #v_DC: 1000 cls: MasterInverter in: u: [i1p1, i1p2, i1p3] # names of the inputs out: v: [lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v] i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i] # iref: [0,0,0] # vref: [1,0,0] inv2: id: inverter2 cls: SlaveInverter #pll: # kP: 10 # kI: 200 in: u: [i2p1, i2p2, i2p3] out: v: [lcl1.capacitor1.v, lcl1.capacitor2.v, lcl1.capacitor3.v] i: [lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i] #i: [.resistor1.R, .resistor2.R, .resistor3.R, .resistor1.i, .resistor2.i, .resistor3.i ] #i_ref: [15,0,0] load: id: rl1 cls: Load out: i: [.resistor1.R, .resistor2.R, .resistor3.R, .inductor1.L, .inductor2.L, .inductor3.L] #.inductor1.L, inductor2.L, inductor3.L ================================================ FILE: experiments/testing_framework_control/net_RL_load.yaml ================================================ v_nom: 230*sqrt(2) freq_nom: 50 ts: .5e-4 components: inv1: id: inverter1 #i_nom: 20 #i_lim: 30 #v_DC: 1000 cls: MasterInverter v_noise: fun: normal: # np.random.* loc: 0 scale: 0.001 i_noise: fun: normal: # np.random.* loc: 0 scale: 0.001 clip: # np.clip a_min: -1 a_max: 1 in: u: [ i1p1, i1p2, i1p3 ] # names of the inputs out: v: [ lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v ] i: [ lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i ] # iref: [0,0,0] # vref: [1,0,0] inv2: id: inverter2 cls: SlaveInverter #pll: # kP: 10 # kI: 200 in: u: [ i2p1, i2p2, i2p3 ] out: v: [ lcl1.capacitor1.v, lcl1.capacitor2.v, lcl1.capacitor3.v ] i: [ lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i ] #i_ref: [15,0,0] load: id: rl1 cls: Load out: i: [ .inductor1.i, .inductor2.i, .inductor3.i ] R: [ .resistor1.R, .resistor2.R, .resistor3.R ] L: [ .inductor1.L, .inductor2.L, .inductor3.L ] ================================================ FILE: experiments/testing_framework_control/net_single-inv-curr.yaml ================================================ v_nom: 230*sqrt(2) #freq_nom: 50 ts: 1e-4 components: inv1: id: inverter1 #i_nom: 20 #i_lim: 30 v_DC: 1000 cls: MasterInverterCurrentSourcing in: u: [i1p1, i1p2, i1p3] # names of the inputs out: v: [lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v] i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i] # iref: [0,0,0] # vref: [1,0,0] load: id: rl1 cls: Load out: i: [.inductor1.L, .inductor2.L, .inductor3.L] R: [ .resistor1.R, .resistor2.R, .resistor3.R ] ================================================ FILE: experiments/testing_framework_control/scoringmodel_innerlevel.py ================================================ ######################################################################## # Scoring Model of the inner level to quantify the controller performance # Reading the metrics dataframes that were saved in a pkl file # Comparison of the metrics # Each Metric is equally weighted --> could be edited by the user import pandas as pd import numpy as np #############Important########################################################### # Save the data frames of the metrics as pkl..... # .....at the corresponding positions in tf_innerlevel.....py # The positions are indicated there. df_id_controller1 = pd.read_pickle("./df_metrics_id_controller1.pkl") df_iq_controller1 = pd.read_pickle("./df_metrics_iq_controller1.pkl") df_vd_controller1 = pd.read_pickle("./df_metrics_vd_controller1.pkl") df_vq_controller1 = pd.read_pickle("./df_metrics_vq_controller1.pkl") ################################################################### df_id_controller2 = pd.read_pickle("./df_metrics_id_controller2.pkl") df_vd_controller2 = pd.read_pickle("./df_metrics_vd_controller2.pkl") df_iq_controller2 = pd.read_pickle("./df_metrics_iq_controller2.pkl") df_vq_controller2 = pd.read_pickle("./df_metrics_vq_controller2.pkl") ###################id Single Inverter Current Control########################################## comparison_id = pd.concat([df_id_controller1, df_id_controller2], axis=1) comparison_id.columns = ['Controller_1', 'Controller_2'] # Weight_Order = Overshoot | Rise Time | Settling Time | RMSE | Steady State Error comparison_id = comparison_id.assign(Weight=[1, 1, 1, 1, 1]) # Metrics are given weights comparison_id = comparison_id.drop(comparison_id[(comparison_id == 'No overshoot').any(1)].index) # drops if no os comparison_id['Controller_1'] = comparison_id['Controller_1'].astype(float).round(4) comparison_id['Controller_2'] = comparison_id['Controller_2'].astype(float).round(4) comparison_id['Better Performer'] = np.where(comparison_id['Controller_1'] > comparison_id['Controller_2'], 'Controller_2', (np.where(comparison_id['Controller_1'] == comparison_id['Controller_2'], 'equal', 'Controller_1'))) # Comparison of controllers Controller_1_better_performer = comparison_id.loc[comparison_id['Better Performer'] == 'Controller_1'] Controller_2_better_performer = comparison_id.loc[comparison_id['Better Performer'] == 'Controller_2'] Controller_1_points_id = Controller_1_better_performer['Weight'].sum() # Scores of Controller 1 are summed up Controller_2_points_id = Controller_2_better_performer['Weight'].sum() # Scores of Controller 2 are summed up ###################iq Single Inverter Current Control########################################## comparison_iq = pd.concat([df_iq_controller1, df_iq_controller2], axis=1) comparison_iq.columns = ['Controller_1', 'Controller_2'] # Weight_Order = RMSE | Steady-State-Error | Absolute Peak Value comparison_iq = comparison_iq.assign(Weight=[1, 1, 1]) # Metrics are given weights comparison_iq['Controller_1'] = comparison_iq['Controller_1'].astype(float).round(4) comparison_iq['Controller_2'] = comparison_iq['Controller_2'].astype(float).round(4) comparison_iq['Better Performer'] = np.where(comparison_iq['Controller_1'] > comparison_iq['Controller_2'], 'Controller_2', (np.where(comparison_iq['Controller_1'] == comparison_iq['Controller_2'], 'equal', 'Controller_1'))) # Comparison of controllers Controller_1_better_performer = comparison_iq.loc[comparison_iq['Better Performer'] == 'Controller_1'] Controller_2_better_performer = comparison_iq.loc[comparison_iq['Better Performer'] == 'Controller_2'] Controller_1_points_iq = Controller_1_better_performer['Weight'].sum() # Scores of Controller 1 are summed up Controller_2_points_iq = Controller_2_better_performer['Weight'].sum() # Scores of Controller 2 are summed up ###################Vd Cascaded Structure (Single Inverter Voltage Current Control)####################### comparison_vd = pd.concat([df_vd_controller1, df_vd_controller2], axis=1) # Df of C1 and C2 are merged comparison_vd.columns = ['Controller_1', 'Controller_2'] # Weight_Order = Overshoot | Rise Time | Settling Time | RMSE | Steady State Error comparison_vd = comparison_vd.assign(Weight=[1, 1, 1, 1, 1]) # Metrics are given weights comparison_vd = comparison_vd.drop( comparison_vd[ (comparison_vd == 'No overshoot').any(1)].index) # If no overshoot, the overshoot will not be considered comparison_vd['Controller_1'] = comparison_vd['Controller_1'].astype(float).round(4) comparison_vd['Controller_2'] = comparison_vd['Controller_2'].astype(float).round(4) comparison_vd['Better Performer'] = np.where(comparison_vd['Controller_1'] > comparison_vd['Controller_2'], 'Controller_2', (np.where(comparison_vd['Controller_1'] == comparison_vd['Controller_2'], 'equal', 'Controller_1'))) # Comparison of controllers Controller_1_better_performer = comparison_vd.loc[comparison_vd['Better Performer'] == 'Controller_1'] Controller_2_better_performer = comparison_vd.loc[comparison_vd['Better Performer'] == 'Controller_2'] Controller_1_points_vd = Controller_1_better_performer['Weight'].sum() # Scores of Controller 1 are summed up Controller_2_points_vd = Controller_2_better_performer['Weight'].sum() # Scores of Controller 2 are summed up ###################Vq Cascaded Structure (Single Inverter Voltage Current Control)####################### comparison_vq = pd.concat([df_vq_controller1, df_vq_controller2], axis=1) comparison_vq.columns = ['Controller_1', 'Controller_2'] # Weight_Order = RMSE | Steady-State-Error | absolute Peak Value comparison_vq = comparison_vq.assign(Weight=[1, 1, 1]) # Metrics are given weights comparison_vq['Controller_1'] = comparison_vq['Controller_1'].astype(float).round(4) comparison_vq['Controller_2'] = comparison_vq['Controller_2'].astype(float).round(4) comparison_vq['Better Performer'] = np.where(comparison_vq['Controller_1'] > comparison_vq['Controller_2'], 'Controller_2', (np.where(comparison_vq['Controller_1'] == comparison_vq['Controller_2'], 'equal', 'Controller_1'))) # Comparison of controllers Controller_1_better_performer = comparison_vq.loc[comparison_vq['Better Performer'] == 'Controller_1'] Controller_2_better_performer = comparison_vq.loc[comparison_vq['Better Performer'] == 'Controller_2'] Controller_1_points_vq = Controller_1_better_performer['Weight'].sum() # Scores of Controller 1 are summed up Controller_2_points_vq = Controller_2_better_performer['Weight'].sum() # Scores of Controller 2 are summed up print() print("###########################################################################################") print("Results for Vd - cascaded control structure (outer voltage control, inner current control)") print("###########################################################################################") print(comparison_vd) print() print("Controller1_Vd_Points: ", Controller_1_points_vd) print("Controller2_Vd_Points: ", Controller_2_points_vd) print() print("#############################################################################################") print("Results for Vq - cascaded control structure (outer control: voltage, inner control: current)") print("#############################################################################################") print(comparison_vq) print() print("Controller1_Vq_Points: ", Controller_1_points_vq) print("Controller2_Vq_Points: ", Controller_2_points_vq) print() print("#################################################") print("Results for id - Single Inverter Current Control") print("#################################################") print(comparison_id) print() print("Controller1_id_Points: ", Controller_1_points_id) print("Controller2_id_Points: ", Controller_2_points_id) print() print("#################################################") print("Results for iq - Single Inverter Current Control") print("#################################################") print(comparison_iq) print() print("Controller1_iq_Points: ", Controller_1_points_iq) print("Controller2_iq_Points: ", Controller_2_points_iq) ###############Overall Results - Output ########### ########Current Control (Idq)############ overall_Controller_1_points_current_control = Controller_1_points_id + Controller_1_points_iq overall_Controller_2_points_current_control = Controller_2_points_id + Controller_2_points_iq print() print() print() print() print() print("Summary") print("##############################") print("Overall Results Current Control") print("##############################") print() print("Controller_1_Points_overall: ", overall_Controller_1_points_current_control) print("Controller_2_Points_overall: ", overall_Controller_2_points_current_control) print() if overall_Controller_2_points_current_control < overall_Controller_1_points_current_control: print("Controller 1 is the better performer for current control.The results still need to be") print("treated with caution, because depending on the purpose of the controller in the microgrid, ") print("individual metrics may be more important than others. ") elif overall_Controller_2_points_current_control == overall_Controller_1_points_current_control: print("No controller is the better performer for current control.The results still need to be") print("treated with caution, because depending on the purpose of the controller in the microgrid,") print("individual metrics may be more important than others.") else: print("Controller 2 is the better performer for current control.The results still need to be") print("treated with caution, because depending on the purpose of the controller in the microgrid,") print("individual metrics may be more important than others.") ################Cascaded Control Structure (Vdq)########### overall_Controller_1_points_vd = Controller_1_points_vd + Controller_1_points_vq overall_Controller_2_points_vq = Controller_2_points_vd + Controller_2_points_vq print() print("Summary") print("####################################################") print("Overall Results Voltage Control (Cascaded Structure)") print("####################################################") print() print("Controller_1_Points_overall: ", overall_Controller_1_points_vd) print("Controller_2_Points_overall: ", overall_Controller_2_points_vq) print() if overall_Controller_2_points_vq < overall_Controller_1_points_vd: print("Controller 1 is the better performer for voltage control.The results still need to be") print("treated with caution,because depending on the purpose of the controller in the microgrid, ") print("individual metrics may be more important than others. ") elif overall_Controller_2_points_vq == overall_Controller_1_points_vd: print("No controller is the better performer for the voltage control.The results still need to be treated") print("with caution, because depending on the purpose of the controller in the microgrid,") print("individual metrics may be more important than others.") else: print("Controller 2 is the better performer for voltage control.The results still need to be") print("treated with caution, because depending on the purpose of the controller in the microgrid,") print("individual metrics may be more important than others.") ================================================ FILE: experiments/testing_framework_control/scoringmodel_primarylevel.py ================================================ ######################################################################## # Scoring Model of the primary level to quantify the controller performance # Reading the metrics dataframes that were saved in a pkl file # Comparison of the metrics # Each Metric is equally weighted --> could be edited by the user import pandas as pd import numpy as np #############Important########################################################### # Save the data frames of the metrics as pkl..... # .....at the corresponding positions in tf_primarylevel_vdq_slavefreq.py # The positions are indicated there. ########################################################################## df_vd_controller1 = pd.read_pickle("./df_metrics_vd_controller1_droop.pkl") df_vq_controller1 = pd.read_pickle("./df_metrics_vq_controller1_droop.pkl") df_slave_frequency_controller1 = pd.read_pickle("./df_metrics_slave_f_controller1_droop.pkl") ########################################################################## df_vd_controller2 = pd.read_pickle("./df_metrics_vd_controller2_droop.pkl") df_vq_controller2 = pd.read_pickle("./df_metrics_vq_controller2_droop.pkl") df_slave_frequency_controller2 = pd.read_pickle("./df_metrics_slave_f_controller2_droop.pkl") ########################################################################### ########Vd Two Inverter Droop############################################## comparison_vd = pd.concat([df_vd_controller1, df_vd_controller2], axis=1) comparison_vd.columns = ['Controller_1', 'Controller_2'] comparison_vd = comparison_vd.drop(comparison_vd[(comparison_vd == 'No overshoot').any( 1)].index) # If no overshoot, the overshoot will not be considered # Weight_Order = Overshoot | Rise Time | Settling Time | RMSE | Steady State Error comparison_vd = comparison_vd.assign(Weight=[1, 1, 1, 1, 1]) # Metrics are given weights comparison_vd['Controller_1'] = comparison_vd['Controller_1'].astype(float).round(4) comparison_vd['Controller_2'] = comparison_vd['Controller_2'].astype(float).round(4) comparison_vd['Better Performer'] = np.where(comparison_vd['Controller_1'] > comparison_vd['Controller_2'], 'Controller_2', (np.where(comparison_vd['Controller_1'] == comparison_vd['Controller_2'], 'equal', 'Controller_1'))) # Comparison of controllers Controller_1_better_performer = comparison_vd.loc[comparison_vd['Better Performer'] == 'Controller_1'] Controller_2_better_performer = comparison_vd.loc[comparison_vd['Better Performer'] == 'Controller_2'] Controller_1_points_vd = Controller_1_better_performer['Weight'].sum() # Scores of Controller 1 are summed up Controller_2_points_vd = Controller_2_better_performer['Weight'].sum() # Scores of Controller 2 are summed up print() print("###############################") print("Results for Vd - Primary level") print("###############################") print(comparison_vd) print() print("Controller1_Vd_Points: ", Controller_1_points_vd) print("Controller2_Vd_Points: ", Controller_2_points_vd) ########Vq Two Inverter Droop############################################## comparison_vq = pd.concat([df_vq_controller1, df_vq_controller2], axis=1) comparison_vq.columns = ['Controller_1', 'Controller_2'] # Weight_Order = RMSE | Steady-State-Error | Absolute Peak Value comparison_vq = comparison_vq.assign(Weight=[1, 1, 1]) # Metrics are given weights comparison_vq['Controller_1'] = comparison_vq['Controller_1'].astype(float).round(4) comparison_vq['Controller_2'] = comparison_vq['Controller_2'].astype(float).round(4) comparison_vq['Better Performer'] = np.where(comparison_vq['Controller_1'] > comparison_vq['Controller_2'], 'Controller_2', (np.where(comparison_vq['Controller_1'] == comparison_vq['Controller_2'], 'equal', 'Controller_1'))) # Comparison of controllers Controller_1_better_performer = comparison_vq.loc[comparison_vq['Better Performer'] == 'Controller_1'] Controller_2_better_performer = comparison_vq.loc[comparison_vq['Better Performer'] == 'Controller_2'] Controller_1_points_vq = Controller_1_better_performer['Weight'].sum() # Scores of Controller 1 are summed up Controller_2_points_vq = Controller_2_better_performer['Weight'].sum() # Scores of Controller 2 are summed up print() print("###############################") print("Results for Vq - Primary level") print("###############################") print(comparison_vq) print() print("Controller1_Vq_Points: ", Controller_1_points_vq) print("Controller2_Vq_Points: ", Controller_2_points_vq) #######Slave Frequency Two Inverter Droop######## comparison_slave_frequency = pd.concat([df_slave_frequency_controller1, df_slave_frequency_controller2], axis=1) comparison_slave_frequency.columns = ['Controller_1', 'Controller_2'] # Weight_Order = Overshoot | Rise Time | Settling Time | RMSE | Steady State Error comparison_slave_frequency = comparison_slave_frequency.assign(Weight=[1, 1, 1, 1, 1]) # Metrics are given weights comparison_slave_frequency = comparison_slave_frequency.drop(comparison_slave_frequency[ (comparison_slave_frequency == 'No overshoot').any( 1)].index) # If no os, row will be dropped comparison_slave_frequency['Controller_1'] = comparison_slave_frequency['Controller_1'].astype(float).round(4) comparison_slave_frequency['Controller_2'] = comparison_slave_frequency['Controller_2'].astype(float).round(4) comparison_slave_frequency['Better Performer'] = np.where( comparison_slave_frequency['Controller_1'] > comparison_slave_frequency['Controller_2'], 'Controller_2', (np.where(comparison_slave_frequency['Controller_1'] == comparison_slave_frequency['Controller_2'], 'equal', 'Controller_1'))) # Comparison of controllers Controller_1_better_performer = comparison_slave_frequency.loc[ comparison_slave_frequency['Better Performer'] == 'Controller_1'] Controller_2_better_performer = comparison_slave_frequency.loc[ comparison_slave_frequency['Better Performer'] == 'Controller_2'] Controller_1_points_slave_frequency = Controller_1_better_performer[ 'Weight'].sum() # Scores of Controller 1 are summed up Controller_2_points_slave_frequency = Controller_2_better_performer[ 'Weight'].sum() # Scores of Controller 2 are summed up print() print("###########################") print("Results for Slave Frequency") print("###########################") print(comparison_slave_frequency) print() print("Controller1_SlaveFrequency_Points: ", Controller_1_points_slave_frequency) print("Controller2_SlaveFrequency_Points: ", Controller_2_points_slave_frequency) ######Overall Results###### overall_Controller_1_points = Controller_1_points_vd + Controller_1_points_vq + Controller_1_points_slave_frequency overall_Controller_2_points = Controller_2_points_vd + Controller_2_points_vq + Controller_2_points_slave_frequency print() print() print() print("Summary") print("#################################") print("Overall Results for Primary level") print("#################################") print() print() print("Controller_1_Points_overall: ", overall_Controller_1_points) print("Controller_2_Points_overall: ", overall_Controller_2_points) print() if overall_Controller_2_points < overall_Controller_1_points: print("Controller 1 is the better performer.The results still need to be treated with caution,") print("because depending on the purpose of the controller in the microgrid, ") print("individual metrics may be more important than others. ") elif overall_Controller_2_points == overall_Controller_1_points: print("No controller is better than the other.The results still need to be treated with caution,") print("because depending on the purpose of the controller in the microgrid,") print("individual metrics may be more important than others.") else: print("Controller 2 is the better performer.The results still need to be treated with caution,") print("because depending on the purpose of the controller in the microgrid,") print("individual metrics may be more important than others.") ================================================ FILE: experiments/testing_framework_control/tf_innerlevel_idq.py ================================================ ##################################### # Example using a FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters # Simulation setup: Single inverter supplying 15 A d-current to an RL-load via a LC filter # Controller: PI current controller gain parameters are optimized by SafeOpt # Testing Framework for current control # Definition of Benchmark, Implementation of load steps, Measurement of control metrics import logging from typing import List import GPy import gym import matplotlib.pyplot as plt import numpy as np import pandas as pd from metrics import Metrics pd.options.mode.chained_assignment = None # default='warn' from openmodelica_microgrid_gym import Runner from openmodelica_microgrid_gym.agents import SafeOptAgent from openmodelica_microgrid_gym.agents.util import MutableFloat from openmodelica_microgrid_gym.aux_ctl import PI_params, MultiPhaseDQCurrentSourcingController from openmodelica_microgrid_gym.env import PlotTmpl from openmodelica_microgrid_gym.execution import Callback from openmodelica_microgrid_gym.net import Network from openmodelica_microgrid_gym.util import dq0_to_abc, nested_map, FullHistory from random import random # Choose which controller parameters should be adjusted by SafeOpt. # - Kp: 1D example: Only the proportional gain Kp of the PI controller is adjusted # - Ki: 1D example: Only the integral gain Ki of the PI controller is adjusted # - Kpi: 2D example: Kp and Ki are adjusted simultaneously adjust = 'Kpi' # Check if really only one simulation scenario was selected if adjust not in {'Kp', 'Ki', 'Kpi'}: raise ValueError("Please set 'adjust' to one of the following values: 'Kp', 'Ki', 'Kpi'") # Simulation definitions net = Network.load('net_single-inv-curr.yaml') max_episode_steps = 1200 # number of simulation steps per episode num_episodes = 1 # number of simulation episodes (i.e. SafeOpt iterations) iLimit = 30 # inverter current limit / A iNominal = 20 # nominal inverter current / A mu = 2 # factor for barrier function (see below) id_ref = np.array([15, 0, 0]) # exemplary set point i.e. id = 15, iq = 0, i0 = 0 / A iq_ref = 0 # iq = 0 R = 20 # resistance value / Ohm L = 0.001 # resistance value / Henry ts = 1e-4 # duration of episode / s load_step_resistance = 5 # load step / Ohm load_step_inductance = 0.0004 # sets the inductive load step / H movement_1_resistor = -load_step_resistance # first load step is negative movement_2_resistor = (load_step_resistance if random() < 0.5 else -1 * load_step_resistance) + movement_1_resistor # randomly chosen load step movement_1_inductance = -load_step_inductance # first load step is negative movement_2_inductance = (load_step_inductance if movement_2_resistor >= 0 else -load_step_inductance) + movement_1_inductance # same sign as resistive load step ##################################### # Definition of Random Walk # Starting Value: R = 20 Ohm, L= 1 mH # Constant noise is created def load_step_random_walk_resistor(): random_walk = [] random_walk.append(R) # R = 20 Ohm for i in range(1, max_episode_steps): movement = -0.01 if random() < 0.5 else 0.01 # constant slight and random fluctuation of load value = random_walk[i - 1] + movement random_walk.append(value) return random_walk def load_step_inductance_random_walk(): random_walk_inductance = [] random_walk_inductance.append(L) # L = 1 mH for i in range(1, max_episode_steps): movement = -0.000001 if random() < 0.5 else 0.000001 # constant slight and random fluctuation of load value = random_walk_inductance[i - 1] + movement random_walk_inductance.append(value) return random_walk_inductance list_resistor = load_step_random_walk_resistor() list_inductance = load_step_inductance_random_walk() class Reward: def __init__(self): self._idx = None def set_idx(self, obs): if self._idx is None: self._idx = nested_map( lambda n: obs.index(n), [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'], [f'master.CVI{k}' for k in 'dq0']]) def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and setpoints to evaluate the quality of the used parameters. Takes current measurement and setpoints so calculate the mean-root-error control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx Iabc_master = data[idx[0]] # 3 phase currents at LC inductors phase = data[idx[1]] # phase from the master controller needed for transformation # setpoints ISPdq0_master = data[idx[2]] # setting dq reference ISPabc_master = dq0_to_abc(ISPdq0_master, phase) # convert dq set-points into three-phase abc coordinates # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) # plus barrier penalty for violating the current constraint error = np.sum((np.abs((ISPabc_master - Iabc_master)) / iLimit) ** 0.5, axis=0) \ + -np.sum(mu * np.log(1 - np.maximum(np.abs(Iabc_master) - iNominal, 0) / (iLimit - iNominal)), axis=0) \ * max_episode_steps return -error.squeeze() ######################################################################################### # Implementation of Load Steps # It is checked, if quantity reaches steady state # Values are stored in a databuffer # If the databuffer fulfills several conditions, the first load steps will be implemented # Programming 'on the fly' class LoadstepCallback(Callback): def __init__(self): self.steady_state_reached = False self.settling_time = None self.databuffer = None self._idx = None self.interval_check = False self.steady_state_check = False self.list_data = [] self.upper_bound = 1.02 * id_ref[0] self.lower_bound = 0.98 * id_ref[0] self.databuffer_settling_time = [] def set_idx(self, obs): if self._idx is None: self._idx = nested_map( lambda n: obs.index(n), [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'], [f'master.CVI{k}' for k in 'dq0']]) def reset(self): self.databuffer = np.empty(20) # creates a databuffer with a length of twenty def __call__(self, cols, obs): self.set_idx(cols) self.databuffer[-1] = obs[self._idx[3][0]] # last element is replaced with current value self.list_data.append(obs[self._idx[3][0]]) # current value is also appended to the list_data self.databuffer = np.roll(self.databuffer, -1) # all values are shifted to the left by one # condition: it is checked whether the current is outside of the interval if (obs[self._idx[3][0]] < self.lower_bound or obs[ self._idx[3][0]] > self.upper_bound) and self.steady_state_reached == False: self.interval_check = False # pre-condition: checks if observation is within the specified bound --> settling time may be reached if self.lower_bound < obs[self._idx[3][0]] < self.upper_bound: if not self.interval_check: global position_settling_time position_settling_time = len(self.list_data) self.interval_check = True if self.databuffer.std() < 0.05 and np.round(self.databuffer.mean(), decimals=1) == id_ref[ 0]: # if the databuffer fulfills the conditions, steady state is reached self.steady_state_reached = True if not self.steady_state_check: global position_steady_state position_steady_state = len(self.list_data) # position steady state is returned self.steady_state_check = True # After steady state is reached, a fixed load step of + 5 Ohm is implemented # After 3/4 of the simulation time, another random load step of +/- 5 Ohm is implemented def load_step_resistance(self, t): for i in range(1, len(list_resistor)): if self.steady_state_reached == False and t <= ts * i + ts: # no steady state, no load step return list_resistor[i] # for every step, it contains values that fluctuate a little bit elif self.steady_state_reached == True and t <= ts * i + ts and i <= max_episode_steps * 0.75: return list_resistor[i] + movement_1_resistor # when steady state reached, first load step elif self.steady_state_reached == True and t <= ts * i + ts and i > max_episode_steps * 0.75: # 75 % ts return list_resistor[i] + movement_2_resistor # After steady state is reached, a fixed load step of + 0.4 mH is implemented # After 3/4 of the simulation time, another random load step of +/- 0.4 mH is implemented def load_step_inductance(self, t): for i in range(1, len(list_inductance)): if self.steady_state_reached == False and t <= ts * i + ts: # no load step, no steady state return list_inductance[i] # for every step, it contains values that fluctuate a little bit elif self.steady_state_reached == True and t <= ts * i + ts and i <= max_episode_steps * 0.75: return list_inductance[i] + movement_1_inductance # when steady state reached, first load step elif self.steady_state_reached == True and t <= ts * i + ts and i > max_episode_steps * 0.75: # 75 % ts return list_inductance[i] + movement_2_inductance if __name__ == '__main__': ##################################### # Definitions for the GP prior_mean = 0 # mean factor of the GP prior mean which is multiplied with the first performance of the init set noise_var = 0.001 ** 2 # measurement noise sigma_omega prior_var = 0.1 # prior variance of the GP bounds = None lengthscale = None if adjust == 'Kp': bounds = [(0.00, 0.03)] # bounds on the input variable Kp lengthscale = [.01] # length scale for the parameter variation [Kp] for the GP # For 1D example, if Ki should be adjusted if adjust == 'Ki': bounds = [(0, 300)] # bounds on the input variable Ki lengthscale = [50.] # length scale for the parameter variation [Ki] for the GP # For 2D example, choose Kp and Ki as mutable parameters (below) and define bounds and lengthscale for both if adjust == 'Kpi': bounds = [(0.0, 0.07), (0, 300)] lengthscale = [.02, 50.] # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times # the initial performance: safe_threshold = 1.2 means. Performance measurement for optimization are seen as # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!) # parameter set safe_threshold = 0 j_min = -2 # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop # expanding points eventually. # The following variable is multiplied with the first performance of the initial set by the factor below: explore_threshold = 0 ##2 # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of # limit exceeded abort_reward = 10 * j_min # 10 # Definition of the kernel kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True) ##################################### # Definition of the controllers mutable_params = None current_dqp_iparams = None if adjust == 'Kp': # mutable_params = parameter (Kp gain of the current controller of the inverter) to be optimized using # the SafeOpt algorithm mutable_params = dict(currentP=MutableFloat(5e-3)) # Define the PI parameters for the current controller of the inverter current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=115, limits=(-1, 1)) # For 1D example, if Ki should be adjusted elif adjust == 'Ki': mutable_params = dict(currentI=MutableFloat(10)) current_dqp_iparams = PI_params(kP=10e-3, kI=mutable_params['currentI'], limits=(-1, 1)) # For 2D example, choose Kp and Ki as mutable parameters elif adjust == 'Kpi': mutable_params = dict(currentP=MutableFloat(15e-3), currentI=MutableFloat(10)) # setP= 10 e^-3 and setQ=10 current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'], limits=(-1, 1)) # Define a current sourcing inverter as master inverter using the pi and droop parameters from above ctrl = MultiPhaseDQCurrentSourcingController(current_dqp_iparams, ts_sim=net.ts, f_nom=net.freq_nom, ts_ctrl=1e-4, name='master') ##################################### # Definition of the optimization agent # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example # Arguments described above # History is used to store results agent = SafeOptAgent(mutable_params, abort_reward, j_min, kernel, dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold, explore_threshold=explore_threshold), [ctrl], dict(master=[[f'lc1.inductor{k}.i' for k in '123'], id_ref]), history=FullHistory() ) ##################################### # Definition of the environment using a FMU created by OpenModelica # (https://www.openmodelica.org/) # Using an inverter supplying a load # - using the reward function described above as callable in the env # - viz_cols used to choose which measurement values should be displayed (here, only the 3 currents across the # inductors of the inverters are plotted. Labels and grid is adjusted using the PlotTmpl (For more information, # see UserGuide) # - inputs to the models are the connection points to the inverters (see user guide for more details) # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors def xylables(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$i_{\mathrm{abc}}\,/\,\mathrm{A}$') ax.grid(which='both') def xylables_R(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$R_{\mathrm{123}}\,/\,\mathrm{\u03A9}$') ax.grid(which='both') def xylables_L(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$L_{\mathrm{123}}\,/\,\mathrm{H}$') ax.grid(which='both') def xylables_i_dq0(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$i_{\mathrm{dq0}}\,/\,\mathrm{i}$') ax.grid(which='both') callback = LoadstepCallback() env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', reward_fun=Reward().rew_fun, viz_cols=[ PlotTmpl([f'lc1.inductor{i}.i' for i in '123'], callback=xylables ), PlotTmpl([f'rl1.resistor{i}.R' for i in '123'], callback=xylables_R ), PlotTmpl([f'master.CVI{s}' for s in 'dq0'], ), PlotTmpl([f'rl1.inductor{i}.L' for i in '123'], callback=xylables_L ) ], log_level=logging.INFO, viz_mode='episode', model_params={'rl1.resistor1.R': callback.load_step_resistance, # see LoadstepCallback(Callback) 'rl1.resistor2.R': callback.load_step_resistance, 'rl1.resistor3.R': callback.load_step_resistance, 'rl1.inductor1.L': callback.load_step_inductance, 'rl1.inductor2.L': callback.load_step_inductance, 'rl1.inductor3.L': callback.load_step_inductance }, max_episode_steps=max_episode_steps, net=net, model_path='../../omg_grid/grid.network_singleInverter.fmu', history=FullHistory() ) ##################################### # Execution of the experiment # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations) runner = Runner(agent, env, callback) runner.run(num_episodes, visualise=True) print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df[:])) print('\n Experiment finished with best set: \n') print('\n {} = {}'.format(adjust, agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') # Show best episode measurment (current) plot best_env_plt = runner.run_data['best_env_plt'] if best_env_plt: ax = best_env_plt[0].axes[0] ax.set_title('Best Episode') best_env_plt[0].show() best_env_plt[0].savefig('best_env_plt.png') # Show last performance plot best_agent_plt = runner.run_data['last_agent_plt'] if best_agent_plt: ax = best_agent_plt.axes[0] ax.grid(which='both') ax.set_axisbelow(True) if adjust == 'Ki': ax.set_xlabel(r'$K_\mathrm{i}\,/\,\mathrm{(VA^{-1}s^{-1})}$') ax.set_ylabel(r'$J$') elif adjust == 'Kp': ax.set_xlabel(r'$K_\mathrm{p}\,/\,\mathrm{(VA^{-1})}$') ax.set_ylabel(r'$J$') elif adjust == 'Kpi': agent.params.reset() ax.set_xlabel(r'$K_\mathrm{i}\,/\,\mathrm{(VA^{-1}s^{-1})}$') ax.set_ylabel(r'$K_\mathrm{p}\,/\,\mathrm{(VA^{-1})}$') ax.get_figure().axes[1].set_ylabel(r'$J$') plt.plot(bounds[0], [mutable_params['currentP'].val, mutable_params['currentP'].val], 'k-', zorder=1, lw=4, alpha=.5) best_agent_plt.show() best_agent_plt.savefig('agent_plt.png') ##################################### # Calculation of Metrics # Here, the d- term of idq is analysed df_master_CVId = env.history.df[['master.CVId']] current_controller_metrics_id = Metrics(df_master_CVId, id_ref[0], ts, max_episode_steps, position_steady_state=position_steady_state, position_settling_time=position_settling_time) d = {'Overshoot': [current_controller_metrics_id.overshoot()], 'Rise Time/s ': [current_controller_metrics_id.rise_time()], 'Settling Time/s ': [current_controller_metrics_id.settling_time()], 'Root Mean Squared Error/A': [current_controller_metrics_id.RMSE()], 'Steady State Error/A': [current_controller_metrics_id.steady_state_error()]} df_metrics_id = pd.DataFrame(data=d).T df_metrics_id.columns = ['Value'] print() print('Metrics of id') print(df_metrics_id) ######IMPORTANT FOR THE SCORING MODEL INNER LEVEL############################## # Use the following code, to create a pkl-File in which the Dataframe is stored # df_metrics_id.to_pickle("./df_metrics_id_controller1.pkl") # Maybe you need to replace 'controller1.pkl' with 'controller2.pkl' ##################################### # Calculation of Metrics # Here, the q- term of idq is analysed df_master_CVIq = env.history.df[['master.CVIq']] current_controller_metrics_iq = Metrics(df_master_CVIq, iq_ref, ts, max_episode_steps) d = {'Root Mean Squared Error/A': [current_controller_metrics_iq.RMSE()], 'Steady State Error/A': [current_controller_metrics_iq.steady_state_error()], 'absolute Peak Value/A': [current_controller_metrics_iq.absolute_peak()]} df_metrics_iq = pd.DataFrame(data=d).T df_metrics_iq.columns = ['Value'] print() print('Metrics of iq') print(df_metrics_iq) ######IMPORTANT FOR THE SCORING MODEL INNER LEVEL############################## # Use the following code, to create a pkl-File in which the Dataframe is stored # df_metrics_id.to_pickle("./df_metrics_iq_controller1.pkl") # Maybe you need to replace 'controller1.pkl' with 'controller2.pkl' ================================================ FILE: experiments/testing_framework_control/tf_innerlevel_vdq.py ================================================ ##################################### # Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters # Simulation setup: Single voltage forming inverter supplying an RL-load via an LC-filter # Controller: Cascaded PI-PI voltage and current controller gain parameters are optimized by SafeOpt # Testing Framework for voltage control # Definition of Benchmark, Implementation of load steps, Measurement of control metrics import logging import os from random import random from time import strftime, gmtime from typing import List import GPy import gym import numpy as np import pandas as pd pd.options.mode.chained_assignment = None # default='warn' from openmodelica_microgrid_gym import Runner from openmodelica_microgrid_gym.agents import SafeOptAgent from openmodelica_microgrid_gym.agents.util import MutableFloat from openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, MultiPhaseDQ0PIPIController from openmodelica_microgrid_gym.env import PlotTmpl from openmodelica_microgrid_gym.execution import Callback from openmodelica_microgrid_gym.net import Network from openmodelica_microgrid_gym.util import dq0_to_abc, nested_map, FullHistory # Simulation definitions net = Network.load('net_single-inv-curr.yaml') max_episode_steps = 1200 # number of simulation steps per episode num_episodes = 1 # number of simulation episodes (i.e. SafeOpt iterations) iLimit = 30 # inverter current limit / A iNominal = 20 # nominal inverter current / A mu = 2 # factor for barrier function (see below) DroopGain = 0 # virtual droop gain for active power / W/Hz 40000 QDroopGain = 0 # virtual droop gain for reactive power / VAR/V 1000 vd_ref = np.array([325, 0, 0]) # exemplary set point i.e. vd = 325, vq = 0, v0 = 0 / A vq_ref = 0 # vq = 0 R = 20 # resistance value / Ohm L = 0.001 # resistance value / Henry ts = 1e-4 # duration of episode / s load_step_resistance = 5 # load step / Ohm load_step_inductance = 0.0004 # sets the inductive load step / H movement_1_resistor = load_step_resistance # first load step is negative movement_2_resistor = (load_step_resistance if random() < 0.5 else -1 * load_step_resistance) + movement_1_resistor # randomly chosen load step movement_1_inductance = load_step_inductance # first load step is negative movement_2_inductance = (load_step_inductance if movement_2_resistor >= 0 else -load_step_inductance) + movement_1_inductance # same sign as resistive load step ##################################### # Definition of Random Walk # Starting Value: R = 20 Ohm, L= 1 mH # Constant noise is created def load_step_random_walk_resistor(): random_walk = [] random_walk.append(R) # R = 20 Ohm for i in range(1, max_episode_steps): movement = -0.01 if random() < 0.5 else 0.01 # constant slight and random fluctuation of load value = random_walk[i - 1] + movement random_walk.append(value) return random_walk def load_step_inductance_random_walk(): random_walk_inductance = [] random_walk_inductance.append(L) # L = 1 mH for i in range(1, max_episode_steps): movement = -0.000001 if random() < 0.5 else 0.000001 # constant slight and random fluctuation of load value = random_walk_inductance[i - 1] + movement random_walk_inductance.append(value) return random_walk_inductance list_resistor = load_step_random_walk_resistor() list_inductance = load_step_inductance_random_walk() # Files saves results and resulting plots to the folder saves_VI_control_safeopt in the current directory current_directory = os.getcwd() save_folder = os.path.join(current_directory, r'saves_VI_control_safeopt') os.makedirs(save_folder, exist_ok=True) class Reward: def __init__(self): self._idx = None def set_idx(self, obs): # [f'lc1.inductor{k}.i' for k in '123'] if self._idx is None: self._idx = nested_map( lambda n: obs.index(n), [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'], [f'lc1.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0']]) def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of the used parameters. Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx iabc_master = data[idx[0]] # 3 phase currents at LC inductors phase = data[idx[1]] # phase from the master controller needed for transformation vabc_master = data[idx[3]] # 3 phase currents at LC inductors # set points (sp) isp_dq0_master = data[idx[2]] # setting dq current reference isp_abc_master = dq0_to_abc(isp_dq0_master, phase) # convert dq set-points into three-phase abc coordinates vsp_dq0_master = data[idx[4]] # setting dq voltage reference vsp_abc_master = dq0_to_abc(vsp_dq0_master, phase) # convert dq set-points into three-phase abc coordinates # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) # plus barrier penalty for violating the current constraint error = np.sum((np.abs((isp_abc_master - iabc_master)) / iLimit) ** 0.5, axis=0) \ + -np.sum(mu * np.log(1 - np.maximum(np.abs(iabc_master) - iNominal, 0) / (iLimit - iNominal)), axis=0) \ + np.sum((np.abs((vsp_abc_master - vabc_master)) / net.v_nom) ** 0.5, axis=0) return -error.squeeze() ######################################################################################### # Implementation of Load Steps # It is checked, if quantity reaches steady state # Values are stored in a databuffer # If the databuffer fulfills several conditions, the first load steps will be implemented # Programming 'on the fly' class LoadstepCallback(Callback): def __init__(self): self.steady_state_reached = False self.settling_time = 0 self.databuffer = None self._idx = None self.interval_check = False self.steady_state_check = False self.list_data = [] self.upper_bound = 1.02 * vd_ref[0] self.lower_bound = 0.98 * vd_ref[0] def set_idx(self, obs): if self._idx is None: self._idx = nested_map( lambda n: obs.index(n), [[f'lc1.inductor{k}.i' for k in '123'], 'master.phase', [f'master.SPI{k}' for k in 'dq0'], [f'lc1.capacitor{k}.v' for k in '123'], [f'master.SPV{k}' for k in 'dq0'], [f'master.CVV{i}' for i in 'dq0']]) def reset(self): self.databuffer = np.empty(10) # creates a databuffer with a length of ten def __call__(self, cols, obs): self.set_idx(cols) self.databuffer[-1] = obs[self._idx[5][0]] # all values are shifted to the left by one self.databuffer = np.roll(self.databuffer, -1) # all values are shifted to the left by one self.list_data.append(obs[self._idx[5][0]]) # current value is appended to the list # condition: it is checked whether the current is outside of the interval --> settling time may not be reached if (obs[self._idx[5][0]] < self.lower_bound or obs[ self._idx[5][0]] > self.upper_bound) and not self.steady_state_reached: self.interval_check = False # pre-condition: checks if observation is within the specified interval --> settling time may be reached if self.lower_bound < obs[self._idx[5][0]] < self.upper_bound: if not self.interval_check: global position_settling_time position_settling_time = len(self.list_data) self.interval_check = True if self.databuffer.std() < 0.1 and np.round(self.databuffer.mean()) == \ vd_ref[0]: # if the databuffer fulfills the conditions, steady state is reached self.steady_state_reached = True if not self.steady_state_check: global position_steady_state position_steady_state = len(self.list_data) # stores the steady state position self.steady_state_check = True # After steady state is reached, a fixed load step of + 5 Ohm is implemented # After 3/4 of the simulation time, another random load step of +/- 5 Ohm is implemented def load_step_resistance(self, t): for i in range(1, len(list_resistor)): if self.steady_state_reached == False and t <= ts * i + ts: # no steady state, no load step return list_resistor[i] # for every step, it contains values that fluctuate a little bit elif self.steady_state_reached == True and t <= ts * i + ts and i <= max_episode_steps * 0.75: return list_resistor[i] + movement_1_resistor # when steady state reached, first load step elif self.steady_state_reached == True and t <= ts * i + ts and i > max_episode_steps * 0.75: # 75 % ts return list_resistor[i] + movement_2_resistor # After steady state is reached, a fixed load step of + 0.4 mH is implemented # After 3/4 of the simulation time, another random load step of +/- 0.4 mH is implemented def load_step_inductance(self, t): for i in range(1, len(list_inductance)): if self.steady_state_reached == False and t <= ts * i + ts: # no load step, no steady state return list_inductance[i] # for every step, it contains values that fluctuate a little bit elif self.steady_state_reached == True and t <= ts * i + ts and i <= max_episode_steps * 0.75: return list_inductance[i] + movement_1_inductance # when steady state reached, first load step elif self.steady_state_reached == True and t <= ts * i + ts and i > max_episode_steps * 0.75: # 75 % ts return list_inductance[i] + movement_2_inductance if __name__ == '__main__': ##################################### # Definitions for the GP prior_mean = 0 # mean factor of the GP prior mean which is multiplied with the first performance of the init set noise_var = 0.001 ** 2 # measurement noise sigma_omega prior_var = 2 # prior variance of the GP # Choose Kp and Ki (current and voltage controller) as mutable parameters (below) and define bounds and lengthscale # for both of them bounds = [(0.0, 0.03), (0, 300), (0.0, 0.03), (0, 300)] # bounds on the input variable current-Ki&Kp and voltage-Ki&Kp lengthscale = [.005, 50., .005, 50.] # length scale for the parameter variation [current-Ki&Kp and voltage-Ki&Kp] for the GP # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times # the initial performance: safe_threshold = 1.2 means: performance measurement for optimization are seen as # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!) # parameter set safe_threshold = 0.5 # minimal allowed performance (depending on episode return) j_min = -2 # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop # expanding points eventually. # The following variable is multiplied with the first performance of the initial set by the factor below: explore_threshold = 0 # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of # limit exceeded abort_reward = -10 * j_min # Definition of the kernel kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True) ##################################### # Definition of the controllers # Choose Kp and Ki for the current and voltage controller as mutable parameters mutable_params = dict(currentP=MutableFloat(10e-3), currentI=MutableFloat(10), voltageP=MutableFloat(25e-3), voltageI=MutableFloat(60)) voltage_dqp_iparams = PI_params(kP=mutable_params['voltageP'], kI=mutable_params['voltageI'], limits=(-iLimit, iLimit)) current_dqp_iparams = PI_params(kP=mutable_params['currentP'], kI=mutable_params['currentI'], limits=(-1, 1)) # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for # the filter and the nominal frequency # Droop controller used to calculate the virtual frequency drop due to load changes droop_param = DroopParams(DroopGain, 0.005, net.freq_nom) # Define the Q-droop parameters for the inverter of the reactive power VAR/Volt, delta_t (0.002) used for the # filter and the nominal voltage qdroop_param = DroopParams(QDroopGain, 0.002, net.v_nom) # Define a voltage forming inverter using the PIPI and droop parameters from above # ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, net.ts, droop_param, qdroop_param, # undersampling=2, name='master') ctrl = MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param, ts_sim=net.ts, ts_ctrl=2 * net.ts, name='master') ##################################### # Definition of the optimization agent # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example agent = SafeOptAgent(mutable_params, abort_reward, j_min, kernel, dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold, explore_threshold=explore_threshold), ctrls=[ctrl], obs_template=dict(master=[[f'lc1.inductor{k}.i' for k in '123'], [f'lc1.capacitor{k}.v' for k in '123'], ]), history=FullHistory() ) # Arguments described above # History is used to store results ##################################### # Definition of the environment using a FMU created by OpenModelica # (https://www.openmodelica.org/) # Using an inverter supplying a load # - using the reward function described above as callable in the env # - viz_cols used to choose which measurement values should be displayed. # Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide) # generated figures are stored to file # - inputs to the models are the connection points to the inverters (see user guide for more details) # - model outputs are the 3 currents through the inductors and the 3 voltages across the capacitors def xylables_i(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$i_{\mathrm{abc}}\,/\,\mathrm{A}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/Inductor_currents' + time + '.pdf') def xylables_v_abc(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$v_{\mathrm{abc}}\,/\,\mathrm{V}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/abc_voltage' + time + '.pdf') def xylables_R(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') # zeit ax.set_ylabel('$R_{\mathrm{123}}\,/\,\mathrm{\u03A9}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/Resistor_Course_Load_Step' + time + '.pdf') def xylables_i_dq0(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$i_{\mathrm{dq0}}\,/\,\mathrm{i}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/dq0_current' + time + '.pdf') def xylables_v_dq0(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$v_{\mathrm{dq0}}\,/\,\mathrm{V}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/dq0_voltage' + time + '.pdf') def xylables_L(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$L_{\mathrm{123}}\,/\,\mathrm{H}$') ax.grid(which='both') callback = LoadstepCallback() env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', reward_fun=Reward().rew_fun, viz_cols=[ PlotTmpl([f'lc1.inductor{i}.i' for i in '123'], callback=xylables_i ), PlotTmpl([f'lc1.capacitor{i}.v' for i in '123'], callback=xylables_v_abc ), PlotTmpl([f'rl1.resistor{i}.R' for i in '123'], callback=xylables_R ), PlotTmpl([f'master.CVi{s}' for s in 'dq0'], callback=xylables_i_dq0 ), PlotTmpl([f'master.CVV{i}' for i in 'dq0'], callback=xylables_v_dq0 ), PlotTmpl([f'rl1.inductor{i}.L' for i in '123'], callback=xylables_L ) ], log_level=logging.INFO, viz_mode='episode', model_params={'rl1.resistor1.R': callback.load_step_resistance, 'rl1.resistor2.R': callback.load_step_resistance, 'rl1.resistor3.R': callback.load_step_resistance, 'rl1.inductor1.L': callback.load_step_inductance, 'rl1.inductor2.L': callback.load_step_inductance, 'rl1.inductor3.L': callback.load_step_inductance }, max_episode_steps=max_episode_steps, net=net, model_path='../../omg_grid/grid.network_singleInverter.fmu', history=FullHistory() ) ##################################### # Execution of the experiment # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations) runner = Runner(agent, env, callback) runner.run(num_episodes, visualise=True) ##################################### # Performance results and parameters as well as plots are stored in folder pipi_signleInvALT agent.history.df.to_csv(save_folder + '/result.csv') print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4}))) print('\n Experiment finished with best set: \n') print('\n Current-Ki&Kp and voltage-Ki&Kp = {}'.format( agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') # Show best episode measurment (current) plot best_env_plt = runner.run_data['best_env_plt'] for ii in range(len(best_env_plt)): ax = best_env_plt[ii].axes[0] ax.set_title('Best Episode') best_env_plt[ii].show() # best_env_plt[0].savefig('best_env_plt.png') ##################################### # Calculation of Metrics # Here, the d- term of vdq is analysed df_master_CVVd = env.history.df[['master.CVVd']] from metrics import Metrics voltage_controller_metrics_vd = Metrics(df_master_CVVd, vd_ref[0], ts, max_episode_steps, position_steady_state=position_steady_state, position_settling_time=position_settling_time) d = {'Overshoot': [voltage_controller_metrics_vd.overshoot()], 'Rise Time/s ': [voltage_controller_metrics_vd.rise_time()], 'Settling Time/s ': [voltage_controller_metrics_vd.settling_time()], 'Root Mean Squared Error/V': [voltage_controller_metrics_vd.RMSE()], 'Steady State Error/V': [voltage_controller_metrics_vd.steady_state_error()]} print() df_metrics_vd = pd.DataFrame(data=d).T df_metrics_vd.columns = ['Value'] print('Metrics of Vd') print(df_metrics_vd) ######IMPORTANT FOR THE SCORING MODEL INNER LEVEL############################## # Use the following code, to create a pkl-File in which the Dataframe is stored # df_metrics_vd.to_pickle("./df_metrics_vd_controller1.pkl") # Maybe you need to replace 'controller1.pkl' with 'controller2.pkl' ##################################### # Calculation of Metrics # Here, the q- term of vdq is analysed df_master_CVVq = env.history.df[['master.CVVq']] from metrics import Metrics voltage_controller_metrics_vq = Metrics(df_master_CVVq, vq_ref, ts, max_episode_steps) d = {'Root Mean Squared Error/V': [voltage_controller_metrics_vq.RMSE()], 'Steady State Error/V': [voltage_controller_metrics_vq.steady_state_error()], 'absolute Peak Value/V': [voltage_controller_metrics_vq.absolute_peak()]} print() df_metrics_vq = pd.DataFrame(data=d).T df_metrics_vq.columns = ['Value'] print('Metrics of Vq') print(df_metrics_vq) ######IMPORTANT FOR THE SCORING MODEL INNER LEVEL############################## # Use the following code, to create a pkl-File in which the Dataframe is stored # df_metrics_vq.to_pickle("./df_metrics_vq_controller1.pkl") # Maybe you need to replace 'controller1.pkl' with 'controller2.pkl' ================================================ FILE: experiments/testing_framework_control/tf_primarylevel_vdq_slavefreq.py ================================================ ##################################### # Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal controller parameters # Simulation setup: Single voltage forming inverter supplying an RL-load via an LC-filter # Controller: Cascaded PI-PI voltage and current controller gain parameters are optimized by SafeOpt # Testing Framework for primary level (frequency, voltage) # Definition of Benchmark, Implementation of load steps, Measurement of control metrics import logging import os from random import random from time import strftime, gmtime from typing import List import GPy import gym import numpy as np import pandas as pd from scipy.stats import linregress from openmodelica_microgrid_gym import Runner from openmodelica_microgrid_gym.agents import SafeOptAgent from openmodelica_microgrid_gym.agents.util import MutableFloat from openmodelica_microgrid_gym.aux_ctl import PI_params, DroopParams, \ MultiPhaseDQ0PIPIController, PLLParams, InverseDroopParams, MultiPhaseDQCurrentController from openmodelica_microgrid_gym.env import PlotTmpl from openmodelica_microgrid_gym.execution import Callback from openmodelica_microgrid_gym.util import nested_map, FullHistory pd.options.mode.chained_assignment = None # default='warn' # Simulation definitions ts = .5e-4 # simulation time step size / s max_episode_steps = 3000 # number of simulation steps per episode num_episodes = 1 # number of simulation episodes (i.e. SafeOpt iterations) v_DC = 1000 # DC-link voltage / V; will be set as model parameter in the FMU nomFreq = 50 # nominal grid frequency / Hz nomVoltPeak = 230 * 1.414 # nominal grid voltage / V iLimit = 30 # inverter current limit / A iNominal = 20 # nominal inverter current / A mu = 2 # factor for barrier function (see below) DroopGain = 40000.0 # virtual droop gain for active power / W/Hz QDroopGain = 1000.0 # virtual droop gain for reactive power / VA/V vd_ref = np.array([325, 0, 0]) # exemplary set point i.e. vd = 325, vq = 0, v0 = 0 / A vq_ref = 0 # vq = 0 R = 20 # resistance value / Ohm L = 0.001 # resistance value / Henry load_step_resistance = 7.5 # load step / Ohm load_step_inductance = 0.015 # sets the inductive load step / H movement_1_resistor = load_step_resistance # first load step is negative movement_2_resistor = (load_step_resistance if random() < 0.5 else -1 * load_step_resistance) + movement_1_resistor # randomly chosen load step movement_1_inductance = load_step_inductance # first load step is negative movement_2_inductance = (load_step_inductance if movement_2_resistor >= 0 else -load_step_inductance) + movement_1_inductance # same sign as resistive load step # Files saves results and resulting plots to the folder saves_VI_control_safeopt in the current directory current_directory = os.getcwd() save_folder = os.path.join(current_directory, r'saves_droop_control_safeopt') os.makedirs(save_folder, exist_ok=True) ##################################### # Definition of Random Walk # Starting Value: R = 20 Ohm, L= 1 mH # Constant noise is created def load_step_random_walk_resistor(): random_walk = [R] for i in range(1, max_episode_steps): movement = -0.01 if random() < 0.5 else 0.01 # constant slight and random fluctuation of load value = random_walk[i - 1] + movement random_walk.append(value) return random_walk def load_step_inductance_random_walk(): random_walk_inductance = [L] for i in range(1, max_episode_steps): movement = -0.000001 if random() < 0.5 else 0.000001 # constant slight and random fluctuation of load value = random_walk_inductance[i - 1] + movement random_walk_inductance.append(value) return random_walk_inductance list_resistor = load_step_random_walk_resistor() list_inductance = load_step_inductance_random_walk() class Reward: def __init__(self): self._idx = None def set_idx(self, obs): if self._idx is None: self._idx = nested_map( lambda n: obs.index(n), [[f'slave.freq'], [f'master.CVV{s}' for s in 'dq0']]) def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of the used parameters. Takes current measurement and set-points so calculate the mean-root control error :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx freq = data[idx[0]] vdq0_master = data[idx[1]] # 3 phase voltages at LC capacitor # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) error = np.sum((np.abs((nomFreq - freq)) / nomFreq) ** 0.5, axis=0) + \ np.sum((np.abs(([nomVoltPeak, 0, 0] - vdq0_master)) / nomVoltPeak) ** 0.5, axis=0) return -error.squeeze() ######################################################################################### # Implementation of Load Steps # It is checked, if quantity reaches steady state # Values are stored in a databuffer # If the databuffer fulfills several conditions, the first load steps will be implemented # Programming 'on the fly' class LoadstepCallback(Callback): def __init__(self): self.steady_state_reached = False self.settling_time = None self.databuffer_droop = None self._idx = None self.interval_check = False self.steady_state_check = False self.list_data = [] self.list_abcissa = [] self.upper_bound = 1.02 * nomFreq self.lower_bound = 0.98 * nomFreq self.databuffer_settling_time = 0 self.databuffer_length = 150 self.slope_info = None def set_idx(self, obs): if self._idx is None: self._idx = nested_map( lambda n: obs.index(n), [[f'slave.freq'], [f'master.CVV{s}' for s in 'dq0']]) def reset(self): self.databuffer_droop = np.empty(self.databuffer_length) # creates a databuffer with a length of ten def __call__(self, cols, obs): self.set_idx(cols) self.databuffer_droop[-1] = obs[self._idx[0][0]] self.list_abcissa.append(len(self.list_data)) self.list_data.append(obs[self._idx[0][0]]) # the current value is appended to the list # only when a certain number of values is available, slopeinfo is created --> used for conditions below if len(self.list_data) > self.databuffer_length: del self.list_abcissa[0] self.slope_info = linregress(self.list_abcissa, self.databuffer_droop) self.databuffer_droop = np.roll(self.databuffer_droop, -1) # all values are shifted to the left by one # condition: it is checked whether the current is outside of the interval --> settling time may not be reached if (obs[self._idx[0][0]] < self.lower_bound or obs[ self._idx[0][0]] > self.upper_bound) and not self.steady_state_reached: self.interval_check = False # pre-condition: checks if observation is within the specified interval --> settling time may be reached if self.lower_bound < obs[self._idx[0][0]] < self.upper_bound: if not self.interval_check: global position_settling_time position_settling_time = len(self.list_data) self.interval_check = True if self.databuffer_droop.std() < 0.003 and np.abs(self.slope_info[0]) < 0.00001 and np.round( self.databuffer_droop.mean()) == nomFreq: # if databuffer fulfills the conditions-->steady state self.steady_state_reached = True if not self.steady_state_check: global position_steady_state position_steady_state = len(self.list_data) # stores the steady state position self.steady_state_check = True # After steady state is reached, a fixed load step of + 7.5 Ohm is implemented # After 3/4 of the simulation time, another random load step of +/- 7.5 Ohm is implemented def load_step_resistance(self, t): for i in range(1, len(list_resistor)): if not self.steady_state_reached and t <= ts * i + ts: # no steady state, no load step return list_resistor[i] # for every step, it contains values that fluctuate a little bit elif self.steady_state_reached and t <= ts * i + ts and i <= max_episode_steps * 0.75: return list_resistor[i] + movement_1_resistor # when steady state reached, first load step elif self.steady_state_reached and t <= ts * i + ts and i > max_episode_steps * 0.75: # 75 % ts return list_resistor[i] + movement_2_resistor # After steady state is reached, a fixed load step of + 150 mH is implemented # After 3/4 of the simulation time, another random load step of +/- 150 mH is implemented def load_step_inductance(self, t): for i in range(1, len(list_inductance)): if not self.steady_state_reached and t <= ts * i + ts: # no load step, no steady state return list_inductance[i] # for every step, it contains values that fluctuate a little bit elif self.steady_state_reached and t <= ts * i + ts and i <= max_episode_steps * 0.75: return list_inductance[i] + movement_1_inductance # when steady state reached, first load step elif self.steady_state_reached and t <= ts * i + ts and i > max_episode_steps * 0.75: # 75 % ts return list_inductance[i] + movement_2_inductance if __name__ == '__main__': ctrl = [] # Empty dict which shall include all controllers ##################################### # Definitions for the GP prior_mean = 2 # mean factor of the GP prior mean which is multiplied with the first performance of the init set noise_var = 0.001 ** 2 # measurement noise sigma_omega prior_var = 0.2 # prior variance of the GP # Choose all droop params as mutable parameters (below) and define bounds and lengthscale for both of them bounds = [(0, 100000), (0, 100000), (0, 3000), (0, 100)] # bounds on the input variable lengthscale = [10000, 10000, 300., 10.] # length scale for the parameter variation for the GP # The performance should not drop below the safe threshold, which is defined by the factor safe_threshold times # the initial performance: safe_threshold = 1.2 means. Performance measurement for optimization are seen as # unsafe, if the new measured performance drops below 20 % of the initial performance of the initial safe (!) # parameter set safe_threshold = 0.5 # minimal allowed performance (depending on episode return) j_min = -2 # The algorithm will not try to expand any points that are below this threshold. This makes the algorithm stop # expanding points eventually. # The following variable is multiplied with the first performance of the initial set by the factor below: explore_threshold = 0 # Factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of # limit exceeded abort_reward = -10 * j_min # Definition of the kernel kernel = GPy.kern.Matern32(input_dim=len(bounds), variance=prior_var, lengthscale=lengthscale, ARD=True) ##################################### # Definition of the controllers # Choose all droop parameter as mutable parameters mutable_params = dict(pDroop_master=MutableFloat(30000.0), pDroop_slave=MutableFloat(30000.0), qDroop_master=MutableFloat(QDroopGain), qDroop_slave=MutableFloat(10)) # Define the droop parameters for the inverter of the active power Watt/Hz (DroopGain), delta_t (0.005) used for # the filter and the nominal frequency # Droop controller used to calculate the virtual frequency drop due to load changes droop_param_master = DroopParams(mutable_params['pDroop_master'], 0.005, nomFreq) # droop parameter used to react on frequency drop droop_param_slave = InverseDroopParams(mutable_params['pDroop_slave'], ts, nomFreq, tau_filt=0.04) # Droop characteristic for the reactive power VAR/Volt Var.s/Volt # qDroop parameter used for virtual voltage drop qdroop_param_master = DroopParams(mutable_params['qDroop_master'], 0.002, nomVoltPeak) # Droop characteristic for the reactive power VAR/Volt Var.s/Volt qdroop_param_slave = InverseDroopParams(mutable_params['qDroop_slave'], ts, nomVoltPeak, tau_filt=0.01) ############### # define Master voltage_dqp_iparams = PI_params(kP=0.025, kI=60, limits=(-iLimit, iLimit)) # kp=0.025 # Current control PI gain parameters for the voltage sourcing inverter current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1)) # kp=0.012 # Define a current sourcing inverter as master inverter using the pi and droop parameters from above ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param_master, qdroop_param_master, ts_sim=ts, ts_ctrl=2 * ts, name='master')) ############### # define slave current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1)) # # PI gain parameters for the PLL in the current forming inverter pll_params = PLLParams(kP=10, kI=200, limits=(-10000, 10000), f_nom=nomFreq) # Droop characteristic for the active power Watts/Hz, W.s/Hz # Add to dict ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, iLimit, droop_param_slave, qdroop_param_slave, ts_sim=ts, name='slave')) ##################################### # Definition of the optimization agent # The agent is using the SafeOpt algorithm by F. Berkenkamp (https://arxiv.org/abs/1509.01066) in this example # Arguments described above # History is used to store results agent = SafeOptAgent(mutable_params, abort_reward, j_min, kernel, dict(bounds=bounds, noise_var=noise_var, prior_mean=prior_mean, safe_threshold=safe_threshold, explore_threshold=explore_threshold), ctrl, {'master': [[f'lc1.inductor{k}.i' for k in '123'], [f'lc1.capacitor{k}.v' for k in '123'], ], 'slave': [[f'lcl1.inductor{k}.i' for k in '123'], [f'lcl1.capacitor{k}.v' for k in '123'], np.zeros(3)]}, history=FullHistory() ) ##################################### # Definition of the environment using a FMU created by OpenModelica # (https://www.openmodelica.org/) # Using two inverters supplying a load # - using the reward function described above as callable in the env # - viz_cols used to choose which measurement values should be displayed # Labels and grid is adjusted using the PlotTmpl (For more information, see UserGuide) # figures are stored to folder # - inputs to the models are the connection points to the inverters (see user guide for more details) # - model outputs are the the 3 currents through the inductors and the 3 voltages across the capacitors for each # inverter and the 3 currents through the load def xylables_i(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$i_{\mathrm{abc}}\,/\,\mathrm{A}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/Inductor_currents' + time + '.pdf') def xylables_v_abc(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$v_{\mathrm{abc}}\,/\,\mathrm{V}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/abc_voltage' + time + '.pdf') def xylables_v_dq0(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$v_{\mathrm{dq0}}\,/\,\mathrm{V}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/dq0_voltage' + time + '.pdf') def xylables_P_master(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$P_{\mathrm{master}}\,/\,\mathrm{W}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/P_master' + time + '.pdf') def xylables_P_slave(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$P_{\mathrm{slave}}\,/\,\mathrm{W}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/P_slave' + time + '.pdf') def xylables_freq(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$f_{\mathrm{slave}}\,/\,\mathrm{Hz}$') ax.grid(which='both') time = strftime("%Y-%m-%d %H_%M_%S", gmtime()) fig.savefig(save_folder + '/f_slave' + time + '.pdf') fig.show() def xylables_R(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') ax.set_ylabel('$R_{\mathrm{123}}\,/\,\mathrm{\u03A9}$') ax.grid(which='both') def xylables_L(fig): ax = fig.gca() ax.set_xlabel(r'$t\,/\,\mathrm{s}$') # zeit ax.set_ylabel('$L_{\mathrm{123}}\,/\,\mathrm{H}$') ax.grid(which='both') callback = LoadstepCallback() env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', reward_fun=Reward().rew_fun, viz_cols=[ PlotTmpl([f'slave.freq'], callback=xylables_freq ), PlotTmpl([f'master.CVV{i}' for i in 'dq0'], callback=xylables_v_dq0 ), PlotTmpl([f'rl1.resistor{i}.R' for i in '123'], # Plot Widerstand RL callback=xylables_R ), PlotTmpl([f'rl1.inductor{i}.L' for i in '123'], # Plot Widerstand RL callback=xylables_L ), ], log_level=logging.INFO, viz_mode='episode', max_episode_steps=max_episode_steps, model_params={'rl1.resistor1.R': callback.load_step_resistance, 'rl1.resistor2.R': callback.load_step_resistance, 'rl1.resistor3.R': callback.load_step_resistance, 'rl1.inductor1.L': callback.load_step_inductance, 'rl1.inductor2.L': callback.load_step_inductance, 'rl1.inductor3.L': callback.load_step_inductance }, model_path='../../omg_grid/grid.network.fmu', net='net_RL_load.yaml', history=FullHistory() ) ##################################### # Execution of the experiment # Using a runner to execute 'num_episodes' different episodes (i.e. SafeOpt iterations) runner = Runner(agent, env, callback) runner.run(num_episodes, visualise=True) ##################################### # Performance results and Parameters as well as plots are stored in folder saves_droop agent.history.df.to_csv(save_folder + '/result.csv') print('\n Experiment finished with best set: \n\n {}'.format(agent.history.df.round({'J': 4, 'Params': 4}))) print('\n Experiment finished with best set: \n') print('\n [pDroop_master, pDroop_slave, qDroop_master, qDroop_slave]= {}'.format( agent.history.df.at[np.argmax(agent.history.df['J']), 'Params'])) print(' Resulting in a performance of J = {}'.format(np.max(agent.history.df['J']))) print('\n\nBest experiment results are plotted in the following:') print() ##################################### # Calculation of Metrics # Here, the d- term of vd is analysed df_master_CVVd = env.history.df[['master.CVVd']] from metrics import Metrics voltage_controller_metrics_vd = Metrics(df_master_CVVd, vd_ref[0], ts, max_episode_steps, position_steady_state=position_steady_state, position_settling_time=position_settling_time) d = {'Overshoot': [voltage_controller_metrics_vd.overshoot()], 'Rise Time/s ': [voltage_controller_metrics_vd.rise_time()], 'Settling Time/s ': [voltage_controller_metrics_vd.settling_time_vd_droop()], 'Root Mean Squared Error/V': [voltage_controller_metrics_vd.RMSE()], 'Steady State Error/V': [voltage_controller_metrics_vd.steady_state_error()]} print("\n") print() df_metrics_vd = pd.DataFrame(data=d).T df_metrics_vd.columns = ['Value'] print('Metrics of Vd') print(df_metrics_vd) ######IMPORTANT FOR THE SCORING MODEL PRIMARY LEVEL############################## # Use the following code, to create a pkl-File in which the Dataframe is stored # df_metrics_vd.to_pickle("./df_metrics_vd_controller1_droop.pkl") # Maybe you need to replace 'controller1_droop.pkl' with 'controller2_droop.pkl' ##################################### # Calculation of Metrics # Here, the q-term of vdq is analysed df_master_CVVq = env.history.df[['master.CVVq']] from metrics import Metrics voltage_controller_metrics_vq = Metrics(df_master_CVVq, vq_ref, ts, max_episode_steps, position_steady_state=position_steady_state, position_settling_time=position_settling_time) d = {'Root Mean Squared Error/V': [voltage_controller_metrics_vq.RMSE()], 'Steady State Error/V': [voltage_controller_metrics_vq.steady_state_error()], 'Peak Value/V': [voltage_controller_metrics_vq.absolute_peak()]} print("\n") print() df_metrics_vq = pd.DataFrame(data=d).T df_metrics_vq.columns = ['Value'] print('Metrics of Vq') print(df_metrics_vq) ######IMPORTANT FOR THE SCORING MODEL PRIMARY LEVEL############################## # Use the following code, to create a pkl-File in which the Dataframe is stored # df_metrics_vq.to_pickle("./df_metrics_vq_controller1_droop.pkl") # Maybe you need to replace 'controller1_droop.pkl' with 'controller2_droop.pkl' ##################################### # Calculation of Metrics # Here, the slave frequency is analysed df_slave_frequency = env.history.df[['slave.freq']] from metrics import Metrics frequency_controller_metrics = Metrics(df_slave_frequency, nomFreq, ts, max_episode_steps, position_steady_state=position_steady_state, position_settling_time=position_settling_time) d = {'Overshoot': [frequency_controller_metrics.overshoot()], 'Rise Time/s ': [frequency_controller_metrics.rise_time()], 'Settling Time/s ': [frequency_controller_metrics.settling_time()], 'Root Mean Squared Error/Hz': [frequency_controller_metrics.RMSE()], 'Steady State Error/Hz': [frequency_controller_metrics.steady_state_error()]} print("\n") print() df_metrics_slave_f = pd.DataFrame(data=d).T df_metrics_slave_f.columns = ['Value'] print('Metrics of Slave Frequency') print(df_metrics_slave_f) ######IMPORTANT FOR THE SCORING MODEL PRIMARY LEVEL############################## # Use the following code, to create a pkl-File in which the Dataframe is stored # df_metrics_slave_f.to_pickle("./df_metrics_slave_f_controller1_droop.pkl") # Maybe you need to replace 'controller1.pkl' with 'controller2.pkl' ================================================ FILE: net/net.yaml ================================================ v_nom: 230*sqrt(2) freq_nom: 50 ts: .5e-4 components: inv1: id: inverter1 #i_nom: 20 #i_lim: 30 #v_DC: 1000 cls: MasterInverter v_noise: fun: normal: # np.random.* loc: 0 scale: 0.001 i_noise: fun: normal: # np.random.* loc: 0 scale: 0.001 clip: # np.clip a_min: -1 a_max: 1 in: u: [ i1p1, i1p2, i1p3 ] # names of the inputs out: v: [ lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v ] i: [ lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i ] # iref: [0,0,0] # vref: [1,0,0] inv2: id: inverter2 cls: SlaveInverter #pll: # kP: 10 # kI: 200 in: u: [ i2p1, i2p2, i2p3 ] out: v: [ lcl1.capacitor1.v, lcl1.capacitor2.v, lcl1.capacitor3.v ] i: [ lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i ] #i_ref: [15,0,0] load: id: rl1 cls: Load out: i: [ .inductor1.i, .inductor2.i, .inductor3.i ] R: [ .resistor1.R, .resistor2.R, .resistor3.R ] ================================================ FILE: net/net_dupinputs.yaml ================================================ v_nom: 230 #freq_nom: 50 ts: .5e-4 components: inv1: #i_nom: 20 #i_lim: 30 #v_DC: 1000 id: inverter1 cls: MasterInverter in: u: [i1p1, i1p2, i1p3] # names of the inputs out: v: [lc1.capacitor1.v,lc1.capacitor2.v, lc1.capacitor3.v] i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i] # iref: [0,0,0] # vref: [1,0,0] inv2: id: inverter2 cls: SlaveInverter #pll: # kP: 10 # kI: 200 in: u: [i1p1, i1p2, i1p3] # names of the inputs out: v: [lcl1.capacitor1.v,lcl1.capacitor2.v, lcl1.capacitor3.v] i: [lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i] i_ref: [15,0,0] load: id: rl1 cls: Load out: i: [.inductor1.i, .inductor2.i, .inductor3.i] ================================================ FILE: net/net_single-inv-Paper_Loadstep.yaml ================================================ v_nom: 169.7 freq_nom: 60 ts: 1e-4 components: inv1: id: inverter1 #i_nom: 20 #i_lim: 30 v_DC: 600 v_noise: fun: normal: # np.random.* loc: 0 scale: 0.4 clip: a_min: 0.3 a_max: 0.5 i_noise: fun: normal: # np.random.* loc: 0 scale: 0.0018 clip: # np.clip a_min: 0.0005 a_max: 0.32 cls: MasterInverter in: u: [ i1p1, i1p2, i1p3 ] # names of the inputs out: v: [ lc.capacitor1.v, lc.capacitor2.v, lc.capacitor3.v ] i: [ lc.inductor1.i, lc.inductor2.i, lc.inductor3.i ] # iref: [0,0,0] # vref: [1,0,0] load: id: r_load cls: Load out: i: [ .resistor1.R, .resistor2.R, .resistor3.R, .resistor1.i, .resistor2.i, .resistor3.i ] ================================================ FILE: net/net_single-inv-curr.yaml ================================================ v_nom: 230*sqrt(2) #freq_nom: 50 ts: .5e-4 components: inv1: id: inverter1 #i_nom: 20 #i_lim: 30 v_DC: 1000 cls: MasterInverterCurrentSourcing in: u: [ i1p1, i1p2, i1p3 ] # names of the inputs out: v: [ lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v ] i: [ lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i ] i_ref: [ 15,0,0 ] # v_ref: [1,0,0] ================================================ FILE: net/net_single-inv-curr_Paper_SC.yaml ================================================ v_nom: 230*sqrt(2) freq_nom: 60 ts: 1e-4 components: inv1: id: inverter1 #i_nom: 20 #i_lim: 30 v_DC: 600 i_noise: fun: normal: # np.random.* loc: 0 scale: 0.0018 clip: # np.clip a_min: 0.0005 a_max: 0.32 cls: MasterInverterCurrentSourcing in: u: [ i1p1, i1p2, i1p3 ] # names of the inputs out: v: [ lc.capacitor1.v, lc.capacitor2.v, lc.capacitor3.v ] i: [ lc.inductor1.i, lc.inductor2.i, lc.inductor3.i ] # iref: [0,0,0] # vref: [1,0,0] ================================================ FILE: net/net_single-inv-volt.yaml ================================================ v_nom: 230*sqrt(2) freq_nom: 50 ts: .5e-4 components: inv1: id: inverter1 #i_nom: 20 #i_lim: 30 v_DC: 1000 cls: MasterInverterCurrentSourcing in: u: [ i1p1, i1p2, i1p3 ] # names of the inputs out: v: [ lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v ] i: [ lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i ] ================================================ FILE: net/net_singleinverter.yaml ================================================ v_nom: 230 #freq_nom: 50 ts: .5e-4 components: inv1: cls: MasterInverter id: inverter1 #i_nom: 20 #i_lim: 30 #v_DC: 1000 in: u: [i1p1, i1p2, i1p3] # names of the inputs out: v: [lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v] i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i] # iref: [0,0,0] # vref: [1,0,0] load: id: rl1 cls: Load out: i: [.inductor1.i, .inductor2.i, .inductor3.i] ================================================ FILE: net/net_static_droop_controller.yaml ================================================ # Simulation definitions ts : 0.5e-4 freq_nom: 50 # grid frequency / Hz v_nom: 325.22 # nominal grid voltage / V components: inv1: cls: MasterInverter id: inverter1 i_lim: 30 # current limit / A i_nom: 20 # nominal current / A in: u: [i1p1,i1p2,i1p3] out: v: [lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v] i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i] inv2: cls: SlaveInverter id: inverter2 i_lim: 30 # current limit / A i_nom: 20 # nominal current / A in: u: [i2p1,i2p2,i2p3] out: v: [lcl1.capacitor1.v, lcl1.capacitor2.v, lcl1.capacitor3.v] i: [lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i] load: id: rl1 cls: Load out: i: [.inductor1.i, .inductor2.i, .inductor3.i] ================================================ FILE: net/net_test.yaml ================================================ v_nom: 230*sqrt(2) freq_nom: 50 ts: 1e-4 components: inv1: id: inverter1 #i_nom: 20 #i_lim: 30 v_DC: 1000 cls: MasterInverter in: u: [i1p1, i1p2, i1p3] # names of the inputs out: v: [lc1.capacitor1.v, lc1.capacitor2.v, lc1.capacitor3.v] i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i] # iref: [0,0,0] # vref: [1,0,0] inv2: id: inverter2 cls: SlaveInverter #pll: # kP: 10 # kI: 200 in: u: [i2p1, i2p2, i2p3] out: v: [lcl1.capacitor1.v, lcl1.capacitor2.v, lcl1.capacitor3.v] i: [lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i] #i_ref: [15,0,0] ================================================ FILE: net/net_valid.yaml ================================================ v_nom: 230 #freq_nom: 50 ts: .5e-4 components: inv1: #i_nom: 20 #i_lim: 30 #v_DC: 1000 id: inverter1 cls: MasterInverter in: u: [i1p1, i1p2, i1p3] # names of the inputs out: v: [lc1.capacitor1.v,lc1.capacitor2.v, lc1.capacitor3.v] i: [lc1.inductor1.i, lc1.inductor2.i, lc1.inductor3.i] # iref: [0,0,0] # vref: [1,0,0] inv2: id: inverter2 cls: SlaveInverter #pll: # kP: 10 # kI: 200 in: u: [i2p1, i2p2, i2p3] out: v: [lcl1.capacitor1.v,lcl1.capacitor2.v, lcl1.capacitor3.v] i: [lcl1.inductor1.i, lcl1.inductor2.i, lcl1.inductor3.i] i_ref: [15,0,0] load: id: rl1 cls: Load out: i: [.inductor1.i, .inductor2.i, .inductor3.i] ================================================ FILE: omg_grid/ActiveLoads/ActiveLoad.mo ================================================ within omg_grid.ActiveLoads; model ActiveLoad parameter SI.Power p_ref(start = 5000); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); omg_grid.PLLs.PLL_DQ pll_dq annotation( Placement(visible = true, transformation(origin = {-50, 76}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Transformations.ABC2DQ_Currents abc2dq_current annotation( Placement(visible = true, transformation(origin = {-58, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Inverters.Inverter inverter annotation( Placement(visible = true, transformation(origin = {-4, -12}, extent = {{-10, -10}, {10, 10}}, rotation = 90))); Modelica.Blocks.Continuous.LimPID PID2(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation( Placement(visible = true, transformation(origin = {34, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Transformations.DQ2ABC dq2abc annotation( Placement(visible = true, transformation(origin = {68, -66}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.LimPID PID1(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation( Placement(visible = true, transformation(origin = {34, -78}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.LimPID pid(Td = 0, Ti = 0.006, k = 0.023, limitsAtInit = true, yMax = 30) annotation( Placement(visible = true, transformation(origin = {-38, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.LimPID PID(Td = 0, Ti = 0.006, k = 0.023, limitsAtInit = true, yMax = 30) annotation( Placement(visible = true, transformation(origin = {-46, -80}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression(y = 1000 / 325) annotation( Placement(visible = true, transformation(origin = {-84, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression2(y = 1000 / 325) annotation( Placement(visible = true, transformation(origin = {-82, -72}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.LC lc(L1 = 0.004, L2 = 0.004, L3 = 0.004) annotation( Placement(visible = true, transformation(origin = {-24, 8}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); Modelica.Blocks.Sources.RealExpression realExpression1(y = 1) annotation( Placement(visible = true, transformation(origin = {-4, -58}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression3(y = 0) annotation( Placement(visible = true, transformation(origin = {-6, -72}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Components.StartValues startvalues(startTime = 0.1) annotation( Placement(visible = true, transformation(origin = {38, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); omg_grid.Components.StartValues startvalues1(startTime = 0.1) annotation( Placement(visible = true, transformation(origin = {38, 34}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); omg_grid.Components.StartValues startvalues2(startTime = 0.1) annotation( Placement(visible = true, transformation(origin = {38, 66}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); equation connect(pin1, pll_dq.c) annotation( Line(points = {{-100, -60}, {-80, -60}, {-80, 70}, {-60, 70}}, color = {0, 0, 255})); connect(pin2, pll_dq.b) annotation( Line(points = {{-100, 0}, {-84, 0}, {-84, 76}, {-60, 76}}, color = {0, 0, 255})); connect(pin3, pll_dq.a) annotation( Line(points = {{-100, 60}, {-88, 60}, {-88, 82}, {-60, 82}}, color = {0, 0, 255})); connect(pin1, abc2dq_current.c) annotation( Line(points = {{-100, -60}, {-80, -60}, {-80, -6}, {-68, -6}, {-68, -6}}, color = {0, 0, 255})); connect(pin2, abc2dq_current.b) annotation( Line(points = {{-100, 0}, {-68, 0}, {-68, 0}, {-68, 0}}, color = {0, 0, 255})); connect(pin3, abc2dq_current.a) annotation( Line(points = {{-100, 60}, {-88, 60}, {-88, 6}, {-68, 6}, {-68, 6}}, color = {0, 0, 255})); connect(pll_d.theta, abc2dq_current.theta) annotation( Line(points = {{-38, 82}, {-30, 82}, {-30, 24}, {-58, 24}, {-58, 12}, {-58, 12}}, color = {0, 0, 127})); connect(PID2.y, dq2abc.d) annotation( Line(points = {{46, -48}, {52, -48}, {52, -62}, {58, -62}}, color = {0, 0, 127})); connect(PID1.y, dq2abc.q) annotation( Line(points = {{46, -78}, {52, -78}, {52, -70}, {58, -70}}, color = {0, 0, 127})); connect(abc2dq_current.d, pid.u_m) annotation( Line(points = {{-62, -10}, {-62, -10}, {-62, -64}, {-38, -64}, {-38, -60}, {-38, -60}}, color = {0, 0, 127})); connect(abc2dq_current.q, PID.u_m) annotation( Line(points = {{-54, -10}, {-54, -10}, {-54, -16}, {-64, -16}, {-64, -96}, {-46, -96}, {-46, -92}, {-46, -92}}, color = {0, 0, 127})); connect(realExpression2.y, pid.u_s) annotation( Line(points = {{-70, -72}, {-68, -72}, {-68, -48}, {-50, -48}, {-50, -48}}, color = {0, 0, 127})); connect(realExpression.y, PID.u_s) annotation( Line(points = {{-72, -86}, {-70, -86}, {-70, -80}, {-58, -80}, {-58, -80}}, color = {0, 0, 127})); connect(pll_dq.d, PID2.u_m) annotation( Line(points = {{-38, 76}, {16, 76}, {16, -64}, {34, -64}, {34, -60}, {34, -60}}, color = {0, 0, 127})); connect(pll_dq.q, PID1.u_m) annotation( Line(points = {{-38, 70}, {14, 70}, {14, -94}, {34, -94}, {34, -90}, {34, -90}}, color = {0, 0, 127})); connect(pll_dq.theta, dq2abc.theta) annotation( Line(points = {{-38, 82}, {64, 82}, {64, -54}, {64, -54}}, color = {0, 0, 127})); connect(abc2dq_current.pin1, lc.pin4) annotation( Line(points = {{-48, -6}, {-44, -6}, {-44, 14}, {-34, 14}}, color = {0, 0, 255})); connect(abc2dq_current.pin2, lc.pin5) annotation( Line(points = {{-48, 0}, {-42, 0}, {-42, 8}, {-34, 8}}, color = {0, 0, 255})); connect(abc2dq_current.pin3, lc.pin6) annotation( Line(points = {{-48, 6}, {-38, 6}, {-38, 2}, {-34, 2}, {-34, 2}}, color = {0, 0, 255})); connect(lc.pin3, inverter.pin3) annotation( Line(points = {{-14, 2}, {-10, 2}, {-10, -2}, {-10, -2}}, color = {0, 0, 255})); connect(lc.pin2, inverter.pin2) annotation( Line(points = {{-14, 8}, {-4, 8}, {-4, -2}, {-4, -2}}, color = {0, 0, 255})); connect(lc.pin1, inverter.pin1) annotation( Line(points = {{-14, 14}, {2, 14}, {2, -2}, {2, -2}}, color = {0, 0, 255})); connect(realExpression1.y, PID2.u_s) annotation( Line(points = {{8, -58}, {8, -58}, {8, -48}, {22, -48}, {22, -48}}, color = {0, 0, 127})); connect(realExpression3.y, PID1.u_s) annotation( Line(points = {{6, -72}, {16, -72}, {16, -78}, {20, -78}, {20, -78}, {22, -78}}, color = {0, 0, 127})); connect(dq2abc.c, startvalues2.u) annotation( Line(points = {{78, -72}, {92, -72}, {92, 66}, {50, 66}, {50, 66}}, color = {0, 0, 127})); connect(inverter.u1, startvalues2.y) annotation( Line(points = {{2, -22}, {2, -22}, {2, -30}, {18, -30}, {18, 66}, {28, 66}, {28, 66}}, color = {0, 0, 127})); connect(dq2abc.b, startvalues1.u) annotation( Line(points = {{78, -66}, {86, -66}, {86, 34}, {52, 34}, {52, 34}, {50, 34}}, color = {0, 0, 127})); connect(dq2abc.a, startvalues.u) annotation( Line(points = {{78, -60}, {80, -60}, {80, 0}, {50, 0}, {50, 0}}, color = {0, 0, 127})); connect(startvalues1.y, inverter.u2) annotation( Line(points = {{28, 34}, {20, 34}, {20, -32}, {-4, -32}, {-4, -22}, {-4, -22}}, color = {0, 0, 127})); connect(inverter.u3, startvalues.y) annotation( Line(points = {{-10, -22}, {-10, -22}, {-10, -34}, {22, -34}, {22, 0}, {28, 0}, {28, 0}}, color = {0, 0, 127})); end ActiveLoad; ================================================ FILE: omg_grid/ActiveLoads/package.mo ================================================ within omg_grid; package ActiveLoads //extends Modelica.Icons.Package; annotation (Icon(coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,100}}), graphics={ Line( origin={7,47}, points={{-84,-6},{-52,-6}}), Rectangle( origin={59,53}, fillColor = {255,255,255}, fillPattern = FillPattern.Solid, extent={{-104,-63},{-64,7}}), Rectangle( origin={146,34}, fillColor = {255,255,255}, fillPattern = FillPattern.Solid, extent={{-104,-63},{-64,7}}), Line( origin={7,15}, points={{-84,-6},{-52,-6}}), Line( origin={79,30}, points={{-84,-6},{-37,-6}}), Line( points={{42,-12},{17,-12},{17,-54},{-71,-54}})})); end ActiveLoads; ================================================ FILE: omg_grid/ActiveLoads/package.order ================================================ ActiveLoad ================================================ FILE: omg_grid/Components/PhaseAngle.mo ================================================ within omg_grid.Components; model PhaseAngle parameter Real freq(start = 50); Modelica.Blocks.Math.Gain deg2rad(k = 2 * 3.1416) annotation( Placement(visible = true, transformation(origin = {14, 0}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Sources.Constant f_nom(k = freq) annotation( Placement(visible = true, transformation(origin = {-18, 0}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Continuous.Integrator f2theta(y_start = 0) annotation( Placement(visible = true, transformation(origin = {0, 0}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Interfaces.RealOutput theta annotation( 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))); equation connect(f_nom.y, f2theta.u) annotation( Line(points = {{-14, 0}, {-5, 0}}, color = {0, 0, 127})); connect(f2theta.y, deg2rad.u) annotation( Line(points = {{4, 0}, {9, 0}}, color = {0, 0, 127})); connect(deg2rad.y, theta) annotation( Line(points = {{18, 0}, {108, 0}}, color = {0, 0, 127})); end PhaseAngle; ================================================ FILE: omg_grid/Components/StartValues.mo ================================================ within omg_grid.Components; block StartValues "Output the input signal filtered with a low pass Butterworth filter of any order" extends Modelica.Blocks.Interfaces.SISO; Real z; Real compare; Real value; parameter Real startTime(start = 0.02); Modelica.Blocks.Sources.RealExpression realExpression(y = 1) annotation( Placement(visible = true, transformation(origin = {-62, 36}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.Integrator integrator annotation( Placement(visible = true, transformation(origin = {-16, 36}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation z = (integrator.y - startTime) * 100000; compare = max(0, z); value = min(compare, 1); y = u * value; connect(realExpression.y, integrator.u) annotation( Line(points = {{-50, 36}, {-28, 36}, {-28, 36}, {-28, 36}}, color = {0, 0, 127})); annotation( 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})})); end StartValues; ================================================ FILE: omg_grid/Components/package.mo ================================================ within omg_grid; package Components extends Modelica.Icons.UtilitiesPackage; end Components; ================================================ FILE: omg_grid/Components/package.order ================================================ PhaseAngle StartValues ================================================ FILE: omg_grid/Examples/ControlledNetworkSC.mo ================================================ within omg_grid.Examples; model ControlledNetworkSC omg_grid.Inverters.Inverter inverter1(v_DC = 5000) annotation( Placement(visible = true, transformation(origin = {-82, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.LC lc1(L1 = 0.001, L2 = 0.001, L3 = 0.001) annotation( Placement(visible = true, transformation(origin = {-26, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.PLLs.PLL_DQ pll_dq annotation( Placement(visible = true, transformation(origin = {2, 62}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Transformations.ABC2DQ_Currents abc2dq_current annotation( Placement(visible = true, transformation(origin = {-54, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression(y = -350) annotation( Placement(visible = true, transformation(origin = {2, 90}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression1(y = 230 * 1.41427) annotation( Placement(visible = true, transformation(origin = {5, 77}, extent = {{-13, -11}, {13, 11}}, rotation = 0))); omg_grid.Transformations.DQ2ABC dq2abc annotation( Placement(visible = true, transformation(origin = {36, -18}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.LimPID pid(Td = 0, Ti = 0.006, k = 0.3, limitsAtInit = true, yMax = 150) annotation( Placement(visible = true, transformation(origin = {78, 86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.LimPID PID(Td = 0, Ti = 0.06, k = 0.3, limitsAtInit = true, yMax = 50) annotation( Placement(visible = true, transformation(origin = {44, 74}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.LimPID PID1(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation( Placement(visible = true, transformation(origin = {-34, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.LimPID PID2(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation( Placement(visible = true, transformation(origin = {-34, -4}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Components.PhaseAngle angle annotation( Placement(visible = true, transformation(origin = {19, 5}, extent = {{-5, -5}, {5, 5}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Ground ground annotation( Placement(visible = true, transformation(origin = {18, 24}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(dq2abc.a, inverter1.u3) annotation( Line(points = {{46, -12}, {78, -12}, {78, -66}, {-116, -66}, {-116, 36}, {-92, 36}}, color = {0, 0, 127})); connect(dq2abc.b, inverter1.u2) annotation( Line(points = {{46, -18}, {72, -18}, {72, -60}, {-110, -60}, {-110, 30}, {-92, 30}}, color = {0, 0, 127})); connect(inverter1.u1, dq2abc.c) annotation( Line(points = {{-92, 24}, {-104, 24}, {-104, -54}, {66, -54}, {66, -24}, {46, -24}}, color = {0, 0, 127})); connect(pll_dq.d, PID.u_m) annotation( Line(points = {{14, 62}, {32, 62}, {32, 60}, {40, 60}, {40, 58}, {44, 58}, {44, 62}, {44, 62}}, color = {0, 0, 127})); connect(realExpression1.y, PID.u_s) annotation( Line(points = {{20, 78}, {30, 78}, {30, 74}, {32, 74}}, color = {0, 0, 127})); connect(pll_dq.q, pid.u_m) annotation( Line(points = {{14, 56}, {78, 56}, {78, 72}, {78, 72}, {78, 74}}, color = {0, 0, 127})); connect(realExpression.y, pid.u_s) annotation( Line(points = {{14, 90}, {58, 90}, {58, 86}, {66, 86}, {66, 86}}, color = {0, 0, 127})); connect(abc2dq_current.d, PID2.u_m) annotation( Line(points = {{-58, 19}, {-58, -20}, {-34, -20}, {-34, -16}}, color = {0, 0, 127})); connect(abc2dq_current.q, PID1.u_m) annotation( Line(points = {{-50, 19}, {-50, -13.5}, {-34, -13.5}, {-34, -46}}, color = {0, 0, 127})); connect(pll_dq.theta, abc2dq_current.theta) annotation( Line(points = {{14, 68}, {22, 68}, {22, 48}, {-54, 48}, {-54, 41}}, color = {0, 0, 127})); connect(pid.y, PID1.u_s) annotation( Line(points = {{90, 86}, {94, 86}, {94, 14}, {-60, 14}, {-60, -34}, {-46, -34}}, color = {0, 0, 127})); connect(PID2.u_s, PID.y) annotation( Line(points = {{-46, -4}, {-56, -4}, {-56, 12}, {60, 12}, {60, 74}, {56, 74}, {56, 74}}, color = {0, 0, 127})); connect(PID2.y, dq2abc.d) annotation( Line(points = {{-22, -4}, {16, -4}, {16, -14}, {26, -14}, {26, -14}}, color = {0, 0, 127})); connect(PID1.y, dq2abc.q) annotation( Line(points = {{-22, -34}, {24, -34}, {24, -22}, {24, -22}, {24, -22}, {26, -22}}, color = {0, 0, 127})); connect(angle.theta, dq2abc.theta) annotation( Line(points = {{24, 6}, {32, 6}, {32, -6}, {32, -6}}, color = {0, 0, 127})); connect(lc1.pin4, pll_dq.c) annotation( Line(points = {{-16, 24}, {-12, 24}, {-12, 56}, {-8, 56}}, color = {0, 0, 255})); connect(lc1.pin5, pll_dq.b) annotation( Line(points = {{-16, 30}, {-14, 30}, {-14, 62}, {-8, 62}}, color = {0, 0, 255})); connect(lc1.pin6, pll_dq.a) annotation( Line(points = {{-16, 36}, {-16, 68}, {-8, 68}}, color = {0, 0, 255})); connect(inverter1.pin3, abc2dq_current.a) annotation( Line(points = {{-72, 36}, {-66, 36}, {-66, 36}, {-64, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, abc2dq_current.b) annotation( Line(points = {{-72, 30}, {-64, 30}, {-64, 30}, {-64, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, abc2dq_current.c) annotation( Line(points = {{-72, 24}, {-64, 24}, {-64, 24}, {-64, 24}}, color = {0, 0, 255})); connect(abc2dq_current.pin3, lc1.pin3) annotation( Line(points = {{-44, 36}, {-36, 36}, {-36, 36}, {-36, 36}}, color = {0, 0, 255})); connect(abc2dq_current.pin2, lc1.pin2) annotation( Line(points = {{-44, 30}, {-36, 30}, {-36, 30}, {-36, 30}}, color = {0, 0, 255})); connect(abc2dq_current.pin1, lc1.pin1) annotation( Line(points = {{-44, 24}, {-36, 24}, {-36, 24}, {-36, 24}}, color = {0, 0, 255})); connect(lc1.pin4, ground.p) annotation( Line(points = {{-16, 24}, {-2, 24}, {-2, 34}, {18, 34}, {18, 34}}, color = {0, 0, 255})); connect(lc1.pin5, ground.p) annotation( Line(points = {{-16, 30}, {-2, 30}, {-2, 34}, {18, 34}, {18, 34}}, color = {0, 0, 255})); connect(lc1.pin6, ground.p) annotation( Line(points = {{-16, 36}, {18, 36}, {18, 34}, {18, 34}}, color = {0, 0, 255})); annotation( Diagram); end ControlledNetworkSC; ================================================ FILE: omg_grid/Examples/ControlledNetworkSingleInverter.mo ================================================ within omg_grid.Examples; model ControlledNetworkSingleInverter omg_grid.Inverters.Inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.LC lc1(L1 = 0.001, L2 = 0.001, L3 = 0.001) annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Loads.RL rl1(L1 = 0.0005, L2 = 0.0005, L3 = 0.0005) annotation( Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.PLLs.PLL_DQ pll_dq annotation( Placement(visible = true, transformation(origin = {2, 62}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Transformations.ABC2DQ_Currents abc2dq_current annotation( Placement(visible = true, transformation(origin = {0, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression(y = -350) annotation( Placement(visible = true, transformation(origin = {2, 90}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression1(y = 230 * 1.41427) annotation( Placement(visible = true, transformation(origin = {5, 77}, extent = {{-13, -11}, {13, 11}}, rotation = 0))); omg_grid.Transformations.DQ2ABC dq2abc annotation( Placement(visible = true, transformation(origin = {36, -18}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.LimPID pid(Td = 0, Ti = 0.006, k = 0.3, limitsAtInit = true, yMax = 150) annotation( Placement(visible = true, transformation(origin = {78, 86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.LimPID PID(Td = 0, Ti = 0.06, k = 0.3, limitsAtInit = true, yMax = 50) annotation( Placement(visible = true, transformation(origin = {44, 74}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.LimPID PID1(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation( Placement(visible = true, transformation(origin = {-34, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Continuous.LimPID PID2(Td = 0, Ti = 1.33, k = 0.013, limitsAtInit = true, yMax = 1 / 2.8284, yMin = -1 / 2.8284) annotation( Placement(visible = true, transformation(origin = {-34, -4}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Components.PhaseAngle angle annotation( Placement(visible = true, transformation(origin = {19, 5}, extent = {{-5, -5}, {5, 5}}, rotation = 0))); equation connect(inverter1.pin3, lc1.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, lc1.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, lc1.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255})); connect(lc1.pin6, abc2dq_current.a) annotation( Line(points = {{-20, 36}, {-10, 36}, {-10, 36}, {-10, 36}}, color = {0, 0, 255})); connect(lc1.pin5, abc2dq_current.b) annotation( Line(points = {{-20, 30}, {-10, 30}, {-10, 30}, {-10, 30}}, color = {0, 0, 255})); connect(lc1.pin4, abc2dq_current.c) annotation( Line(points = {{-20, 24}, {-10, 24}, {-10, 24}, {-10, 24}}, color = {0, 0, 255})); connect(abc2dq_current.pin3, rl1.pin3) annotation( Line(points = {{10, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255})); connect(abc2dq_current.pin2, rl1.pin2) annotation( Line(points = {{10, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255})); connect(abc2dq_current.pin1, rl1.pin1) annotation( Line(points = {{10, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255})); connect(lc1.pin6, pll_dq.a) annotation( Line(points = {{-20, 36}, {-16, 36}, {-16, 68}, {-8, 68}}, color = {0, 0, 255})); connect(lc1.pin5, pll_dq.b) annotation( Line(points = {{-20, 30}, {-14, 30}, {-14, 62}, {-8, 62}, {-8, 62}, {-8, 62}}, color = {0, 0, 255})); connect(lc1.pin4, pll_dq.c) annotation( Line(points = {{-20, 24}, {-12, 24}, {-12, 56}, {-8, 56}, {-8, 56}, {-8, 56}}, color = {0, 0, 255})); connect(dq2abc.a, inverter1.u3) annotation( Line(points = {{46, -12}, {78, -12}, {78, -66}, {-116, -66}, {-116, 36}, {-80, 36}, {-80, 36}}, color = {0, 0, 127})); connect(dq2abc.b, inverter1.u2) annotation( Line(points = {{46, -18}, {72, -18}, {72, -58}, {72, -58}, {72, -60}, {-110, -60}, {-110, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(inverter1.u1, dq2abc.c) annotation( Line(points = {{-80, 24}, {-102, 24}, {-102, 24}, {-104, 24}, {-104, -54}, {66, -54}, {66, -24}, {46, -24}, {46, -24}}, color = {0, 0, 127})); connect(pll_dq.d, PID.u_m) annotation( Line(points = {{14, 62}, {32, 62}, {32, 60}, {40, 60}, {40, 58}, {44, 58}, {44, 62}, {44, 62}}, color = {0, 0, 127})); connect(realExpression1.y, PID.u_s) annotation( Line(points = {{20, 78}, {30, 78}, {30, 74}, {32, 74}}, color = {0, 0, 127})); connect(pll_dq.q, pid.u_m) annotation( Line(points = {{14, 56}, {78, 56}, {78, 72}, {78, 72}, {78, 74}}, color = {0, 0, 127})); connect(realExpression.y, pid.u_s) annotation( Line(points = {{14, 90}, {58, 90}, {58, 86}, {66, 86}, {66, 86}}, color = {0, 0, 127})); connect(abc2dq_current.d, PID2.u_m) annotation( Line(points = {{-4, 20}, {-4, 20}, {-4, 16}, {-58, 16}, {-58, -20}, {-34, -20}, {-34, -16}, {-34, -16}}, color = {0, 0, 127})); connect(abc2dq_current.q, PID1.u_m) annotation( Line(points = {{4, 20}, {4, 20}, {4, 10}, {-20, 10}, {-20, -50}, {-34, -50}, {-34, -46}, {-34, -46}, {-34, -46}}, color = {0, 0, 127})); connect(pll_dq.theta, abc2dq_current.theta) annotation( Line(points = {{14, 68}, {22, 68}, {22, 48}, {0, 48}, {0, 42}, {0, 42}, {0, 42}}, color = {0, 0, 127})); connect(pid.y, PID1.u_s) annotation( Line(points = {{90, 86}, {94, 86}, {94, 14}, {-60, 14}, {-60, -34}, {-46, -34}}, color = {0, 0, 127})); connect(PID2.u_s, PID.y) annotation( Line(points = {{-46, -4}, {-56, -4}, {-56, 12}, {60, 12}, {60, 74}, {56, 74}, {56, 74}}, color = {0, 0, 127})); connect(PID2.y, dq2abc.d) annotation( Line(points = {{-22, -4}, {16, -4}, {16, -14}, {26, -14}, {26, -14}}, color = {0, 0, 127})); connect(PID1.y, dq2abc.q) annotation( Line(points = {{-22, -34}, {24, -34}, {24, -22}, {24, -22}, {24, -22}, {26, -22}}, color = {0, 0, 127})); connect(angle.theta, dq2abc.theta) annotation( Line(points = {{24, 6}, {32, 6}, {32, -6}, {32, -6}}, color = {0, 0, 127})); annotation( Diagram); end ControlledNetworkSingleInverter; ================================================ FILE: omg_grid/Examples/NetworkSineTest.mo ================================================ within omg_grid.Examples; model NetworkSineTest Modelica.Blocks.Sources.Sine sine(amplitude = 230, freqHz = 50) annotation( Placement(visible = true, transformation(origin = {-76, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine1(amplitude = 230, freqHz = 50, phase = 2.0944) annotation( Placement(visible = true, transformation(origin = {-76, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine2(amplitude = 230, freqHz = 50, phase = 4.18879) annotation( Placement(visible = true, transformation(origin = {-76, -16}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine3(amplitude = 230, freqHz = 50) annotation( Placement(visible = true, transformation(origin = {-76, 14}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine4(amplitude = 230, freqHz = 50, phase = 2.0944) annotation( Placement(visible = true, transformation(origin = {-76, 48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine5(amplitude = 230, freqHz = 50, phase = 4.18879) annotation( Placement(visible = true, transformation(origin = {-76, 80}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Grids.Network network1 annotation( Placement(visible = true, transformation(origin = {38, 2}, extent = {{-46, -46}, {46, 46}}, rotation = 0))); equation connect(sine5.y, network1.i1p3) annotation( Line(points = {{-64, 80}, {-34, 80}, {-34, 22}, {-10, 22}, {-10, 22}}, color = {0, 0, 127})); connect(sine4.y, network1.i1p2) annotation( Line(points = {{-64, 48}, {-40, 48}, {-40, 16}, {-10, 16}, {-10, 16}}, color = {0, 0, 127})); connect(sine3.y, network1.i1p1) annotation( Line(points = {{-64, 14}, {-34, 14}, {-34, 10}, {-10, 10}, {-10, 10}}, color = {0, 0, 127})); connect(sine2.y, network1.i2p3) annotation( Line(points = {{-64, -16}, {-64, -16}, {-64, -6}, {-10, -6}, {-10, -6}}, color = {0, 0, 127})); connect(sine1.y, network1.i2p2) annotation( Line(points = {{-64, -48}, {-50, -48}, {-50, -12}, {-10, -12}, {-10, -12}, {-10, -12}}, color = {0, 0, 127})); connect(sine.y, network1.i2p1) annotation( Line(points = {{-64, -82}, {-32, -82}, {-32, -18}, {-10, -18}, {-10, -18}}, color = {0, 0, 127})); end NetworkSineTest; ================================================ FILE: omg_grid/Examples/PLL_Test.mo ================================================ within omg_grid.Examples; model PLL_Test omg_grid.Transformations.ABC2AlphaBeta abc2AlphaBeta annotation( Placement(visible = true, transformation(origin = {-14, 4}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine(amplitude = 230 * 1.414, freqHz = 50) annotation( Placement(visible = true, transformation(origin = {-90, 34}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine1(amplitude = 230 * 1.414, freqHz = 50, phase = -2.0944) annotation( Placement(visible = true, transformation(origin = {-88, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine2(amplitude = 230 * 1.414, freqHz = 50, phase = -4.18879) annotation( Placement(visible = true, transformation(origin = {-88, -26}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Inverters.Inverter inverter annotation( Placement(visible = true, transformation(origin = {-14, 58}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.PLLs.PLL pll annotation( Placement(visible = true, transformation(origin = {28, 56}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(abc2AlphaBeta.a, sine.y) annotation( Line(points = {{-24, 8}, {-44, 8}, {-44, 34}, {-78, 34}, {-78, 34}}, color = {0, 0, 127})); connect(abc2AlphaBeta.b, sine1.y) annotation( Line(points = {{-24, 6}, {-77, 6}}, color = {0, 0, 127})); connect(sine2.y, abc2AlphaBeta.c) annotation( Line(points = {{-76, -26}, {-44, -26}, {-44, 2}, {-24, 2}, {-24, 2}}, color = {0, 0, 127})); connect(inverter.u3, sine.y) annotation( Line(points = {{-24, 64}, {-70, 64}, {-70, 34}, {-78, 34}}, color = {0, 0, 127})); connect(inverter.u2, sine1.y) annotation( Line(points = {{-24, 58}, {-60, 58}, {-60, 6}, {-76, 6}}, color = {0, 0, 127})); connect(inverter.u1, sine2.y) annotation( Line(points = {{-24, 52}, {-54, 52}, {-54, -26}, {-76, -26}}, color = {0, 0, 127})); connect(pll.a, inverter.pin3) annotation( Line(points = {{18, 60}, {4, 60}, {4, 64}, {-4, 64}, {-4, 64}}, color = {0, 0, 255})); connect(pll.b, inverter.pin2) annotation( Line(points = {{18, 58}, {-4, 58}, {-4, 58}, {-4, 58}}, color = {0, 0, 255})); connect(pll.c, inverter.pin1) annotation( Line(points = {{18, 54}, {4, 54}, {4, 52}, {-4, 52}, {-4, 52}}, color = {0, 0, 255})); end PLL_Test; ================================================ FILE: omg_grid/Examples/package.mo ================================================ within omg_grid; package Examples extends Modelica.Icons.ExamplesPackage; end Examples; ================================================ FILE: omg_grid/Examples/package.order ================================================ NetworkSineTest ControlledNetworkSingleInverter PLL_Test ControlledNetworkSC ================================================ FILE: omg_grid/Filter/IdealFilter/L.mo ================================================ within omg_grid.Filter.IdealFilter; model L parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); equation connect(inductor3.n, pin6) annotation( Line(points = {{-50, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255})); connect(inductor2.n, pin5) annotation( Line(points = {{-50, 44}, {78, 44}, {78, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255})); connect(inductor1.n, pin4) annotation( Line(points = {{-50, 20}, {60, 20}, {60, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255})); end L; ================================================ FILE: omg_grid/Filter/IdealFilter/LC.mo ================================================ within omg_grid.Filter.IdealFilter; model LC parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(inductor1.n, pin4) annotation( Line(points = {{-50, 20}, {54, 20}, {54, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255})); connect(inductor2.n, pin5) annotation( Line(points = {{-50, 44}, {68, 44}, {68, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255})); connect(inductor3.n, pin6) annotation( Line(points = {{-50, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255})); connect(inductor3.n, capacitor3.p) annotation( Line(points = {{-50, 70}, {-8, 70}, {-8, -26}, {-8, -26}}, color = {0, 0, 255})); connect(inductor2.n, capacitor2.p) annotation( Line(points = {{-50, 44}, {12, 44}, {12, -26}, {12, -26}}, color = {0, 0, 255})); connect(inductor1.n, capacitor1.p) annotation( Line(points = {{-50, 20}, {32, 20}, {32, -26}, {32, -26}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor1.n) annotation( Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255})); end LC; ================================================ FILE: omg_grid/Filter/IdealFilter/LCL.mo ================================================ within omg_grid.Filter.IdealFilter; model LCL parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Inductance L4 = 0.001; parameter SI.Inductance L5 = 0.001; parameter SI.Inductance L6 = 0.001; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation( Placement(visible = true, transformation(origin = {68, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation( Placement(visible = true, transformation(origin = {74, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation( Placement(visible = true, transformation(origin = {64, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(inductor2.n, inductor5.p) annotation( Line(points = {{-50, 44}, {-50, 44}, {-50, 44}, {64, 44}}, color = {0, 0, 255})); connect(inductor2.n, capacitor2.p) annotation( Line(points = {{-50, 44}, {12, 44}, {12, -26}, {12, -26}}, color = {0, 0, 255})); connect(inductor1.n, inductor4.p) annotation( Line(points = {{-50, 20}, {-50, 20}, {-50, 20}, {58, 20}}, color = {0, 0, 255})); connect(inductor1.n, capacitor1.p) annotation( Line(points = {{-50, 20}, {32, 20}, {32, -26}, {32, -26}}, color = {0, 0, 255})); connect(inductor3.n, capacitor3.p) annotation( Line(points = {{-50, 70}, {-8, 70}, {-8, -26}, {-8, -26}}, color = {0, 0, 255})); connect(inductor3.n, inductor6.p) annotation( Line(points = {{-50, 70}, {54, 70}, {54, 70}, {54, 70}}, color = {0, 0, 255})); connect(inductor4.n, pin4) annotation( Line(points = {{78, 20}, {80, 20}, {80, -60}, {100, -60}}, color = {0, 0, 255})); connect(inductor6.n, pin6) annotation( Line(points = {{74, 70}, {84, 70}, {84, 60}, {100, 60}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255})); connect(inductor5.n, pin5) annotation( Line(points = {{84, 44}, {88, 44}, {88, 0}, {100, 0}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor1.n) annotation( Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255})); end LCL; ================================================ FILE: omg_grid/Filter/IdealFilter/LCLC.mo ================================================ within omg_grid.Filter.IdealFilter; model LCLC parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Capacitance C4 = 0.00001; parameter SI.Capacitance C5 = 0.00001; parameter SI.Capacitance C6 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Inductance L4 = 0.001; parameter SI.Inductance L5 = 0.001; parameter SI.Inductance L6 = 0.001; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-82, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-82, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-84, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-2, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {-22, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-42, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {16, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation( Placement(visible = true, transformation(origin = {34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation( Placement(visible = true, transformation(origin = {34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation( Placement(visible = true, transformation(origin = {36, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation( Placement(visible = true, transformation(origin = {72, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation( Placement(visible = true, transformation(origin = {52, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation( Placement(visible = true, transformation(origin = {32, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(inductor4.n, capacitor4.p) annotation( Line(points = {{44, 20}, {72, 20}, {72, -28}, {72, -28}}, color = {0, 0, 255})); connect(inductor6.n, capacitor6.p) annotation( Line(points = {{46, 70}, {64, 70}, {64, 4}, {32, 4}, {32, -28}}, color = {0, 0, 255})); connect(capacitor3.p, inductor3.n) annotation( Line(points = {{-42, -28}, {-42, -28}, {-42, 70}, {-74, 70}, {-74, 70}}, color = {0, 0, 255})); connect(capacitor2.p, inductor2.n) annotation( Line(points = {{-22, -28}, {-22, -28}, {-22, 44}, {-72, 44}, {-72, 44}}, color = {0, 0, 255})); connect(inductor5.n, capacitor5.p) annotation( Line(points = {{44, 44}, {52, 44}, {52, -28}, {52, -28}}, color = {0, 0, 255})); connect(inductor3.n, inductor6.p) annotation( Line(points = {{-74, 70}, {26, 70}, {26, 70}, {26, 70}}, color = {0, 0, 255})); connect(inductor2.n, inductor5.p) annotation( Line(points = {{-72, 44}, {-72, 44}, {-72, 44}, {24, 44}}, color = {0, 0, 255})); connect(inductor1.n, inductor4.p) annotation( Line(points = {{-72, 20}, {24, 20}, {24, 20}, {24, 20}}, color = {0, 0, 255})); connect(inductor1.n, capacitor1.p) annotation( Line(points = {{-72, 20}, {-2, 20}, {-2, -28}, {-2, -28}}, color = {0, 0, 255})); connect(inductor4.n, pin4) annotation( Line(points = {{44, 20}, {92, 20}, {92, -60}, {100, -60}}, color = {0, 0, 255})); connect(inductor6.n, pin6) annotation( Line(points = {{46, 70}, {76, 70}, {76, 60}, {100, 60}}, color = {0, 0, 255})); connect(capacitor6.n, ground1.p) annotation( Line(points = {{32, -48}, {16, -48}, {16, -60}}, color = {0, 0, 255})); connect(capacitor6.n, capacitor5.n) annotation( Line(points = {{32, -48}, {52, -48}, {52, -48}, {52, -48}}, color = {0, 0, 255})); connect(capacitor5.n, capacitor4.n) annotation( Line(points = {{52, -48}, {72, -48}, {72, -48}, {72, -48}}, color = {0, 0, 255})); connect(capacitor1.n, ground1.p) annotation( Line(points = {{-2, -48}, {16, -48}, {16, -60}, {16, -60}}, color = {0, 0, 255})); connect(inductor5.n, pin5) annotation( Line(points = {{44, 44}, {94, 44}, {94, 0}, {100, 0}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-42, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255})); connect(capacitor1.n, capacitor2.n) annotation( Line(points = {{-2, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-94, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-95, 0}, {-95, 44}, {-92, 44}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-93, -60}, {-93, 20}, {-92, 20}}, color = {0, 0, 255})); end LCLC; ================================================ FILE: omg_grid/Filter/IdealFilter/PI.mo ================================================ within omg_grid.Filter.IdealFilter; model PI parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Capacitance C4 = 0.00001; parameter SI.Capacitance C5 = 0.00001; parameter SI.Capacitance C6 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {2, -16}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {2, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {0, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-70, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {-48, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation( Placement(visible = true, transformation(origin = {26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation( Placement(visible = true, transformation(origin = {46, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation( Placement(visible = true, transformation(origin = {66, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(inductor3.n, capacitor6.p) annotation( Line(points = {{10, 60}, {66, 60}, {66, -28}, {66, -28}}, color = {0, 0, 255})); connect(capacitor3.p, pin3) annotation( Line(points = {{-26, -28}, {-26, -28}, {-26, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255})); connect(inductor1.n, pin4) annotation( Line(points = {{12, -16}, {70, -16}, {70, -60}, {100, -60}}, color = {0, 0, 255})); connect(inductor1.n, capacitor4.p) annotation( Line(points = {{12, -16}, {26, -16}, {26, -28}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-80, -60}, {-80, -16}, {-8, -16}}, color = {0, 0, 255})); connect(inductor3.n, pin6) annotation( Line(points = {{10, 60}, {100, 60}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-10, 60}}, color = {0, 0, 255})); connect(inductor2.n, pin5) annotation( Line(points = {{12, 0}, {100, 0}}, color = {0, 0, 255})); connect(inductor2.n, capacitor5.p) annotation( Line(points = {{12, 0}, {46, 0}, {46, -28}}, color = {0, 0, 255})); connect(inductor2.p, pin2) annotation( Line(points = {{-8, 0}, {-100, 0}}, color = {0, 0, 255})); connect(capacitor1.p, pin1) annotation( Line(points = {{-70, -28}, {-80, -28}, {-80, -60}, {-100, -60}}, color = {0, 0, 255})); connect(capacitor2.p, pin2) annotation( Line(points = {{-48, -28}, {-48, -28}, {-48, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255})); connect(capacitor3.n, ground1.p) annotation( Line(points = {{-26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255})); connect(capacitor4.n, ground1.p) annotation( Line(points = {{26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor3.n) annotation( Line(points = {{-48, -48}, {-26, -48}, {-26, -48}, {-26, -48}}, color = {0, 0, 255})); connect(capacitor1.n, capacitor2.n) annotation( Line(points = {{-70, -48}, {-48, -48}, {-48, -48}, {-48, -48}}, color = {0, 0, 255})); connect(capacitor5.n, capacitor4.n) annotation( Line(points = {{46, -48}, {26, -48}, {26, -48}, {26, -48}}, color = {0, 0, 255})); connect(capacitor6.n, capacitor5.n) annotation( Line(points = {{66, -48}, {46, -48}, {46, -48}, {46, -48}}, color = {0, 0, 255})); end PI; ================================================ FILE: omg_grid/Filter/IdealFilter/package.mo ================================================ within omg_grid.Filter; package IdealFilter end IdealFilter; ================================================ FILE: omg_grid/Filter/IdealFilter/package.order ================================================ LC LCL PI LCLC L ================================================ FILE: omg_grid/Filter/LossesFilter/L.mo ================================================ within omg_grid.Filter.LossesFilter; model L parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Resistance R1 = 0.01; parameter SI.Resistance R2 = 0.01; parameter SI.Resistance R3 = 0.01; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-32, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {-32, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {-32, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(resistor1.n, pin4) annotation( Line(points = {{-22, 20}, {14, 20}, {14, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255})); connect(resistor2.n, pin5) annotation( Line(points = {{-22, 44}, {70, 44}, {70, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255})); connect(resistor3.n, pin6) annotation( Line(points = {{-22, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255})); connect(inductor1.n, resistor1.p) annotation( Line(points = {{-50, 20}, {-50, 20}, {-50, 20}, {-42, 20}}, color = {0, 0, 255})); connect(inductor2.n, resistor2.p) annotation( Line(points = {{-50, 44}, {-42, 44}, {-42, 44}, {-42, 44}}, color = {0, 0, 255})); connect(inductor3.n, resistor3.p) annotation( Line(points = {{-50, 70}, {-42, 70}, {-42, 70}, {-42, 70}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255})); end L; ================================================ FILE: omg_grid/Filter/LossesFilter/LC.mo ================================================ within omg_grid.Filter.LossesFilter; model LC parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Resistance R1 = 0.01; parameter SI.Resistance R2 = 0.01; parameter SI.Resistance R3 = 0.01; parameter SI.Resistance R4 = 0.01; parameter SI.Resistance R5 = 0.01; parameter SI.Resistance R6 = 0.01; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {-34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {-26, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor4(R = R4) annotation( Placement(visible = true, transformation(origin = {32, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor5(R = R5) annotation( Placement(visible = true, transformation(origin = {12, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor6(R = R6) annotation( Placement(visible = true, transformation(origin = {-8, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(resistor3.n, resistor6.p) annotation( Line(points = {{-16, 70}, {-8, 70}, {-8, 2}}, color = {0, 0, 255})); connect(resistor3.n, pin6) annotation( Line(points = {{-16, 70}, {80, 70}, {80, 60}, {100, 60}}, color = {0, 0, 255})); connect(inductor3.n, resistor3.p) annotation( Line(points = {{-50, 70}, {-36, 70}}, color = {0, 0, 255})); connect(pin4, resistor1.n) annotation( Line(points = {{100, -60}, {62, -60}, {62, 20}, {-24, 20}, {-24, 20}}, color = {0, 0, 255})); connect(resistor4.n, capacitor1.p) annotation( Line(points = {{32, -18}, {32, -18}, {32, -26}, {32, -26}}, color = {0, 0, 255})); connect(resistor5.n, capacitor2.p) annotation( Line(points = {{12, -18}, {12, -18}, {12, -26}, {12, -26}}, color = {0, 0, 255})); connect(resistor6.n, capacitor3.p) annotation( Line(points = {{-8, -18}, {-8, -18}, {-8, -26}, {-8, -26}}, color = {0, 0, 255})); connect(pin5, resistor2.n) annotation( Line(points = {{100, 0}, {78, 0}, {78, 44}, {-24, 44}, {-24, 44}}, color = {0, 0, 255})); connect(resistor2.n, resistor5.p) annotation( Line(points = {{-24, 44}, {12, 44}, {12, 2}, {12, 2}}, color = {0, 0, 255})); connect(resistor1.n, resistor4.p) annotation( Line(points = {{-24, 20}, {32, 20}, {32, 2}, {32, 2}}, color = {0, 0, 255})); connect(inductor1.n, resistor1.p) annotation( Line(points = {{-50, 20}, {-44, 20}, {-44, 20}, {-44, 20}}, color = {0, 0, 255})); connect(inductor2.n, resistor2.p) annotation( Line(points = {{-50, 44}, {-44, 44}, {-44, 44}, {-44, 44}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor1.n) annotation( Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255})); end LC; ================================================ FILE: omg_grid/Filter/LossesFilter/LCL.mo ================================================ within omg_grid.Filter.LossesFilter; model LCL parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Inductance L4 = 0.001; parameter SI.Inductance L5 = 0.001; parameter SI.Inductance L6 = 0.001; parameter SI.Resistance R1 = 0.01; parameter SI.Resistance R2 = 0.01; parameter SI.Resistance R3 = 0.01; parameter SI.Resistance R4 = 0.01; parameter SI.Resistance R5 = 0.01; parameter SI.Resistance R6 = 0.01; parameter SI.Resistance R7 = 0.01; parameter SI.Resistance R8 = 0.01; parameter SI.Resistance R9 = 0.01; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-64, 58}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-72, 86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {38, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-28, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation( Placement(visible = true, transformation(origin = {68, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation( Placement(visible = true, transformation(origin = {70, 40}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation( Placement(visible = true, transformation(origin = {74, 62}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor1 annotation( Placement(visible = true, transformation(origin = {-36, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor2 annotation( Placement(visible = true, transformation(origin = {-32, 48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor3 annotation( Placement(visible = true, transformation(origin = {-30, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor4 annotation( Placement(visible = true, transformation(origin = {42, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor5 annotation( Placement(visible = true, transformation(origin = {8, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor6 annotation( Placement(visible = true, transformation(origin = {-22, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor7 annotation( Placement(visible = true, transformation(origin = {32, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor8 annotation( Placement(visible = true, transformation(origin = {40, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor9 annotation( Placement(visible = true, transformation(origin = {34, 68}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(resistor2.n, resistor5.p) annotation( Line(points = {{-22, 48}, {8, 48}, {8, 0}}, color = {0, 0, 255})); connect(resistor8.p, resistor2.n) annotation( Line(points = {{30, 44}, {2, 44}, {2, 48}, {-22, 48}}, color = {0, 0, 255})); connect(resistor2.p, inductor2.n) annotation( Line(points = {{-42, 48}, {-50, 48}, {-50, 58}, {-54, 58}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 58}, {-74, 58}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 86}, {-82, 86}}, color = {0, 0, 255})); connect(resistor3.p, inductor3.n) annotation( Line(points = {{-40, 82}, {-47, 82}, {-47, 86}, {-62, 86}}, color = {0, 0, 255})); connect(resistor3.n, resistor9.p) annotation( Line(points = {{-20, 82}, {3, 82}, {3, 68}, {24, 68}}, color = {0, 0, 255})); connect(resistor6.p, resistor3.n) annotation( Line(points = {{-22, 2}, {-22, 41}, {-20, 41}, {-20, 82}}, color = {0, 0, 255})); connect(inductor6.n, pin6) annotation( Line(points = {{84, 62}, {84, 60}, {100, 60}}, color = {0, 0, 255})); connect(resistor9.n, inductor6.p) annotation( Line(points = {{44, 68}, {55, 68}, {55, 62}, {64, 62}}, color = {0, 0, 255})); connect(inductor5.n, pin5) annotation( Line(points = {{80, 40}, {88, 40}, {88, 0}, {100, 0}}, color = {0, 0, 255})); connect(resistor8.n, inductor5.p) annotation( Line(points = {{50, 44}, {55, 44}, {55, 40}, {60, 40}}, color = {0, 0, 255})); connect(resistor7.n, inductor4.p) annotation( Line(points = {{42, 30}, {54, 30}, {54, 6}, {58, 6}}, color = {0, 0, 255})); connect(resistor4.p, resistor7.p) annotation( Line(points = {{42, 0}, {42, 14.5}, {22, 14.5}, {22, 30}}, color = {0, 0, 255})); connect(resistor1.n, resistor7.p) annotation( Line(points = {{-26, 20}, {2, 20}, {2, 30}, {22, 30}}, color = {0, 0, 255})); connect(inductor4.n, pin4) annotation( Line(points = {{78, 6}, {80, 6}, {80, -60}, {100, -60}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor1.n) annotation( Line(points = {{12, -46}, {23, -46}, {23, -56}, {38, -56}}, color = {0, 0, 255})); connect(resistor4.n, capacitor1.p) annotation( Line(points = {{42, -20}, {42, -24}, {38, -24}, {38, -36}}, color = {0, 0, 255})); connect(resistor5.n, capacitor2.p) annotation( Line(points = {{8, -20}, {8, -24}, {12, -24}, {12, -26}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-28, -46}, {12, -46}}, color = {0, 0, 255})); connect(resistor6.n, capacitor3.p) annotation( Line(points = {{-22, -18}, {-22, -24}, {-28, -24}, {-28, -26}}, color = {0, 0, 255})); connect(resistor1.p, inductor1.n) annotation( Line(points = {{-46, 20}, {-50, 20}, {-50, 20}, {-50, 20}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255})); end LCL; ================================================ FILE: omg_grid/Filter/LossesFilter/LCLC.mo ================================================ within omg_grid.Filter.LossesFilter; model LCLC parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Capacitance C4 = 0.00001; parameter SI.Capacitance C5 = 0.00001; parameter SI.Capacitance C6 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Inductance L4 = 0.001; parameter SI.Inductance L5 = 0.001; parameter SI.Inductance L6 = 0.001; parameter SI.Resistance R1 = 0.01; parameter SI.Resistance R2 = 0.01; parameter SI.Resistance R3 = 0.01; parameter SI.Resistance R4 = 0.01; parameter SI.Resistance R5 = 0.01; parameter SI.Resistance R6 = 0.01; parameter SI.Resistance R7 = 0.01; parameter SI.Resistance R8 = 0.01; parameter SI.Resistance R9 = 0.01; parameter SI.Resistance R10 = 0.01; parameter SI.Resistance R11 = 0.01; parameter SI.Resistance R12 = 0.01; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-82, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-82, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-84, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-2, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {-22, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-42, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {16, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation( Placement(visible = true, transformation(origin = {34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation( Placement(visible = true, transformation(origin = {34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation( Placement(visible = true, transformation(origin = {36, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation( Placement(visible = true, transformation(origin = {72, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation( Placement(visible = true, transformation(origin = {52, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation( Placement(visible = true, transformation(origin = {32, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-56, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {-56, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {-56, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor4(R = R4) annotation( Placement(visible = true, transformation(origin = {-2, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor5(R = R5) annotation( Placement(visible = true, transformation(origin = {-22, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor6(R = R6) annotation( Placement(visible = true, transformation(origin = {-42, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor7(R = R7) annotation( Placement(visible = true, transformation(origin = {10, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor8(R = R8) annotation( Placement(visible = true, transformation(origin = {10, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor9(R = R9) annotation( Placement(visible = true, transformation(origin = {10, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor10(R = R10) annotation( Placement(visible = true, transformation(origin = {72, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor11(R = R11) annotation( Placement(visible = true, transformation(origin = {52, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor12(R = R12) annotation( Placement(visible = true, transformation(origin = {32, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(resistor10.p, inductor4.n) annotation( Line(points = {{72, -4}, {72, -4}, {72, 20}, {44, 20}, {44, 20}}, color = {0, 0, 255})); connect(inductor5.n, resistor11.p) annotation( Line(points = {{44, 44}, {54, 44}, {54, 44}, {52, 44}, {52, -4}, {52, -4}}, color = {0, 0, 255})); connect(resistor12.p, inductor6.n) annotation( Line(points = {{32, -4}, {32, -4}, {32, 10}, {58, 10}, {58, 70}, {46, 70}, {46, 70}}, color = {0, 0, 255})); connect(resistor11.n, capacitor5.p) annotation( Line(points = {{52, -24}, {52, -24}, {52, -28}, {52, -28}}, color = {0, 0, 255})); connect(resistor12.n, capacitor6.p) annotation( Line(points = {{32, -24}, {32, -24}, {32, -28}, {32, -28}}, color = {0, 0, 255})); connect(resistor1.n, resistor4.p) annotation( Line(points = {{-46, 20}, {-2, 20}, {-2, -4}, {-2, -4}, {-2, -4}}, color = {0, 0, 255})); connect(resistor2.n, resistor5.p) annotation( Line(points = {{-46, 44}, {-22, 44}, {-22, -4}, {-22, -4}, {-22, -4}}, color = {0, 0, 255})); connect(resistor6.p, resistor3.n) annotation( Line(points = {{-42, -4}, {-42, -4}, {-42, 70}, {-46, 70}, {-46, 70}}, color = {0, 0, 255})); connect(resistor1.n, resistor7.p) annotation( Line(points = {{-46, 20}, {-46, 20}, {-46, 20}, {0, 20}}, color = {0, 0, 255})); connect(resistor2.n, resistor8.p) annotation( Line(points = {{-46, 44}, {-46, 44}, {-46, 44}, {0, 44}}, color = {0, 0, 255})); connect(resistor3.n, resistor9.p) annotation( Line(points = {{-46, 70}, {0, 70}, {0, 70}, {0, 70}}, color = {0, 0, 255})); connect(resistor7.n, inductor4.p) annotation( Line(points = {{20, 20}, {24, 20}, {24, 20}, {24, 20}}, color = {0, 0, 255})); connect(resistor6.n, capacitor3.p) annotation( Line(points = {{-42, -24}, {-42, -24}, {-42, -28}, {-42, -28}}, color = {0, 0, 255})); connect(resistor5.n, capacitor2.p) annotation( Line(points = {{-22, -24}, {-22, -24}, {-22, -24}, {-22, -28}}, color = {0, 0, 255})); connect(resistor4.n, capacitor1.p) annotation( Line(points = {{-2, -24}, {-2, -24}, {-2, -28}, {-2, -28}}, color = {0, 0, 255})); connect(resistor10.n, capacitor4.p) annotation( Line(points = {{72, -24}, {72, -24}, {72, -28}, {72, -28}}, color = {0, 0, 255})); connect(resistor8.n, inductor5.p) annotation( Line(points = {{20, 44}, {24, 44}}, color = {0, 0, 255})); connect(resistor9.n, inductor6.p) annotation( Line(points = {{20, 70}, {26, 70}, {26, 70}, {26, 70}}, color = {0, 0, 255})); connect(inductor1.n, resistor1.p) annotation( Line(points = {{-72, 20}, {-66, 20}, {-66, 20}, {-66, 20}}, color = {0, 0, 255})); connect(inductor3.n, resistor3.p) annotation( Line(points = {{-74, 70}, {-74, 70}, {-74, 70}, {-66, 70}}, color = {0, 0, 255})); connect(inductor2.n, resistor2.p) annotation( Line(points = {{-72, 44}, {-66, 44}, {-66, 44}, {-66, 44}}, color = {0, 0, 255})); connect(inductor4.n, pin4) annotation( Line(points = {{44, 20}, {92, 20}, {92, -60}, {100, -60}}, color = {0, 0, 255})); connect(inductor6.n, pin6) annotation( Line(points = {{46, 70}, {76, 70}, {76, 60}, {100, 60}}, color = {0, 0, 255})); connect(capacitor6.n, ground1.p) annotation( Line(points = {{32, -48}, {16, -48}, {16, -60}}, color = {0, 0, 255})); connect(capacitor6.n, capacitor5.n) annotation( Line(points = {{32, -48}, {52, -48}, {52, -48}, {52, -48}}, color = {0, 0, 255})); connect(capacitor5.n, capacitor4.n) annotation( Line(points = {{52, -48}, {72, -48}, {72, -48}, {72, -48}}, color = {0, 0, 255})); connect(capacitor1.n, ground1.p) annotation( Line(points = {{-2, -48}, {16, -48}, {16, -60}, {16, -60}}, color = {0, 0, 255})); connect(inductor5.n, pin5) annotation( Line(points = {{44, 44}, {94, 44}, {94, 0}, {100, 0}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-42, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255})); connect(capacitor1.n, capacitor2.n) annotation( Line(points = {{-2, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-94, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-95, 0}, {-95, 44}, {-92, 44}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-93, -60}, {-93, 20}, {-92, 20}}, color = {0, 0, 255})); end LCLC; ================================================ FILE: omg_grid/Filter/LossesFilter/PI.mo ================================================ within omg_grid.Filter.LossesFilter; model PI parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Capacitance C4 = 0.00001; parameter SI.Capacitance C5 = 0.00001; parameter SI.Capacitance C6 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Resistance R1 = 0.01; parameter SI.Resistance R2 = 0.01; parameter SI.Resistance R3 = 0.01; parameter SI.Resistance R4 = 0.01; parameter SI.Resistance R5 = 0.01; parameter SI.Resistance R6 = 0.01; parameter SI.Resistance R7 = 0.01; parameter SI.Resistance R8 = 0.01; parameter SI.Resistance R9 = 0.01; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-14, 28}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-14, 52}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-14, 78}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-70, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {-48, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation( Placement(visible = true, transformation(origin = {26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation( Placement(visible = true, transformation(origin = {46, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation( Placement(visible = true, transformation(origin = {64, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-70, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {-48, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {-26, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor4(R = R4) annotation( Placement(visible = true, transformation(origin = {10, 28}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor5(R = R5) annotation( Placement(visible = true, transformation(origin = {10, 52}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor6(R = R6) annotation( Placement(visible = true, transformation(origin = {10, 78}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor7(R = R7) annotation( Placement(visible = true, transformation(origin = {26, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor8(R = R8) annotation( Placement(visible = true, transformation(origin = {46, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor9(R = R9) annotation( Placement(visible = true, transformation(origin = {64, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(inductor1.n, resistor4.p) annotation( Line(points = {{-4, 28}, {0, 28}, {0, 28}, {0, 28}}, color = {0, 0, 255})); connect(inductor2.n, resistor5.p) annotation( Line(points = {{-4, 52}, {-4, 52}, {-4, 52}, {0, 52}}, color = {0, 0, 255})); connect(inductor3.n, resistor6.p) annotation( Line(points = {{-4, 78}, {0, 78}, {0, 78}, {0, 78}}, color = {0, 0, 255})); connect(resistor3.p, pin3) annotation( Line(points = {{-26, 2}, {-26, 2}, {-26, 18}, {-36, 18}, {-36, 78}, {-90, 78}, {-90, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255})); connect(resistor2.p, pin2) annotation( Line(points = {{-48, 2}, {-48, 2}, {-48, 52}, {-84, 52}, {-84, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255})); connect(resistor1.p, pin1) annotation( Line(points = {{-70, 2}, {-80, 2}, {-80, -60}, {-98, -60}, {-98, -60}, {-100, -60}}, color = {0, 0, 255})); connect(resistor1.n, capacitor1.p) annotation( Line(points = {{-70, -18}, {-70, -18}, {-70, -28}, {-70, -28}}, color = {0, 0, 255})); connect(resistor2.n, capacitor2.p) annotation( Line(points = {{-48, -18}, {-48, -18}, {-48, -28}, {-48, -28}}, color = {0, 0, 255})); connect(resistor3.n, capacitor3.p) annotation( Line(points = {{-26, -18}, {-26, -18}, {-26, -28}, {-26, -28}}, color = {0, 0, 255})); connect(resistor9.n, capacitor6.p) annotation( Line(points = {{64, -18}, {64, -18}, {64, -28}, {64, -28}}, color = {0, 0, 255})); connect(capacitor6.n, capacitor5.n) annotation( Line(points = {{64, -48}, {46, -48}}, color = {0, 0, 255})); connect(capacitor5.p, resistor8.n) annotation( Line(points = {{46, -28}, {46, -28}, {46, -18}, {46, -18}}, color = {0, 0, 255})); connect(resistor5.n, resistor8.p) annotation( Line(points = {{20, 52}, {46, 52}, {46, 2}}, color = {0, 0, 255})); connect(resistor7.n, capacitor4.p) annotation( Line(points = {{26, -18}, {26, -18}, {26, -28}, {26, -28}}, color = {0, 0, 255})); connect(resistor6.n, pin6) annotation( Line(points = {{20, 78}, {84, 78}, {84, 60}, {100, 60}}, color = {0, 0, 255})); connect(resistor5.n, pin5) annotation( Line(points = {{20, 52}, {84, 52}, {84, 0}, {100, 0}}, color = {0, 0, 255})); connect(resistor4.n, pin4) annotation( Line(points = {{20, 28}, {74, 28}, {74, 28}, {76, 28}, {76, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255})); connect(resistor6.n, resistor9.p) annotation( Line(points = {{20, 78}, {64, 78}, {64, 2}, {64, 2}}, color = {0, 0, 255})); connect(resistor4.n, resistor7.p) annotation( Line(points = {{20, 28}, {26, 28}, {26, 2}, {26, 2}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-80, -60}, {-80, 28}, {-24, 28}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-90, 60}, {-90, 78}, {-24, 78}}, color = {0, 0, 255})); connect(capacitor3.n, ground1.p) annotation( Line(points = {{-26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255})); connect(capacitor4.n, ground1.p) annotation( Line(points = {{26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255})); connect(inductor2.p, pin2) annotation( Line(points = {{-24, 52}, {-84, 52}, {-84, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor3.n) annotation( Line(points = {{-48, -48}, {-26, -48}, {-26, -48}, {-26, -48}}, color = {0, 0, 255})); connect(capacitor1.n, capacitor2.n) annotation( Line(points = {{-70, -48}, {-48, -48}, {-48, -48}, {-48, -48}}, color = {0, 0, 255})); connect(capacitor5.n, capacitor4.n) annotation( Line(points = {{46, -48}, {26, -48}, {26, -48}, {26, -48}}, color = {0, 0, 255})); end PI; ================================================ FILE: omg_grid/Filter/LossesFilter/package.mo ================================================ within omg_grid.Filter; package LossesFilter end LossesFilter; ================================================ FILE: omg_grid/Filter/LossesFilter/package.order ================================================ LC LCL PI LCLC L ================================================ FILE: omg_grid/Filter/package.mo ================================================ within omg_grid; package Filter extends Modelica.Icons.Package; annotation (Icon( coordinateSystem(preserveAspectRatio=true, extent={{-100.0,-100.0},{100.0,100.0}}), graphics={ Line(points={{-80.0,80.0},{-80.0,-88.0}}, color={192,192,192}), Polygon(lineColor={192,192,192}, fillColor={192,192,192}, fillPattern=FillPattern.Solid, points={{-80.0,92.0},{-88.0,70.0},{-72.0,70.0},{-80.0,92.0}}), Line(points={{-90.0,-78.0},{82.0,-78.0}}, color={192,192,192}), Polygon(lineColor={192,192,192}, fillColor={192,192,192}, fillPattern=FillPattern.Solid, points={{90.0,-78.0},{68.0,-70.0},{68.0,-86.0},{90.0,-78.0}}), Text(lineColor={192,192,192}, extent={{-66.0,52.0},{88.0,90.0}}, textString="%order"), Text( extent={{-138.0,-140.0},{162.0,-110.0}}, textString="f_cut=%f_cut"), Rectangle(lineColor={160,160,164}, fillColor={255,255,255}, fillPattern=FillPattern.Backward, extent={{-80.0,-78.0},{22.0,10.0}}), 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)})); end Filter; ================================================ FILE: omg_grid/Filter/package.order ================================================ IdealFilter LossesFilter ================================================ FILE: omg_grid/Grids/Microgrid.mo ================================================ within omg_grid.Grids; model Microgrid omg_grid.Inverters.Inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.LC lc1 annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Inverters.Inverter inverter2 annotation( Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); omg_grid.Filter.IdealFilter.LCL lcl1 annotation( Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Loads.RL rl1 annotation( Placement(visible = true, transformation(origin = {92, 2}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.L l12 annotation( Placement(visible = true, transformation(origin = {2, 0}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); omg_grid.Filter.IdealFilter.L l13 annotation( Placement(visible = true, transformation(origin = {46, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.L l23 annotation( Placement(visible = true, transformation(origin = {48, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(inverter1.pin3, lc1.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, lc1.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, lc1.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255})); connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(i2p3, inverter2.u3) annotation( Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127})); connect(i2p2, inverter2.u2) annotation( Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127})); connect(i2p1, inverter2.u1) annotation( Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127})); connect(inverter2.pin3, lcl1.pin3) annotation( Line(points = {{-60, -24}, {-42, -24}, {-42, -24}, {-42, -24}}, color = {0, 0, 255})); connect(inverter2.pin2, lcl1.pin2) annotation( Line(points = {{-60, -30}, {-42, -30}, {-42, -30}, {-42, -30}}, color = {0, 0, 255})); connect(inverter2.pin1, lcl1.pin1) annotation( Line(points = {{-60, -36}, {-42, -36}, {-42, -36}, {-42, -36}}, color = {0, 0, 255})); connect(lc1.pin6, l12.pin3) annotation( Line(points = {{-20, 36}, {8, 36}, {8, 10}}, color = {0, 0, 255})); connect(lc1.pin5, l12.pin2) annotation( Line(points = {{-20, 30}, {2, 30}, {2, 10}}, color = {0, 0, 255})); connect(lc1.pin4, l12.pin1) annotation( Line(points = {{-20, 24}, {-4, 24}, {-4, 10}}, color = {0, 0, 255})); connect(l12.pin6, lcl1.pin6) annotation( Line(points = {{8, -10}, {8, -24}, {-22, -24}}, color = {0, 0, 255})); connect(l12.pin5, lcl1.pin5) annotation( Line(points = {{2, -10}, {2, -30}, {-22, -30}}, color = {0, 0, 255})); connect(l12.pin4, lcl1.pin4) annotation( Line(points = {{-4, -10}, {-4, -36}, {-22, -36}}, color = {0, 0, 255})); connect(l13.pin3, lc1.pin6) annotation( Line(points = {{36, 36}, {-20, 36}}, color = {0, 0, 255})); connect(l13.pin2, lc1.pin5) annotation( Line(points = {{36, 30}, {-20, 30}, {-20, 30}, {-20, 30}, {-20, 30}}, color = {0, 0, 255})); connect(l13.pin1, lc1.pin4) annotation( Line(points = {{36, 24}, {-20, 24}, {-20, 24}, {-20, 24}}, color = {0, 0, 255})); connect(l23.pin3, lcl1.pin6) annotation( Line(points = {{38, -24}, {-22, -24}, {-22, -24}, {-22, -24}}, color = {0, 0, 255})); connect(l23.pin2, lcl1.pin5) annotation( Line(points = {{38, -30}, {-22, -30}, {-22, -30}, {-22, -30}}, color = {0, 0, 255})); connect(l23.pin1, lcl1.pin4) annotation( Line(points = {{38, -36}, {-22, -36}, {-22, -36}, {-22, -36}}, color = {0, 0, 255})); connect(l13.pin6, rl1.pin3) annotation( Line(points = {{56, 36}, {72, 36}, {72, 8}, {82, 8}, {82, 8}}, color = {0, 0, 255})); connect(l13.pin5, rl1.pin2) annotation( Line(points = {{56, 30}, {66, 30}, {66, 2}, {82, 2}, {82, 2}}, color = {0, 0, 255})); connect(l13.pin4, rl1.pin1) annotation( Line(points = {{56, 24}, {62, 24}, {62, -4}, {82, -4}, {82, -4}}, color = {0, 0, 255})); connect(l23.pin5, rl1.pin2) annotation( Line(points = {{58, -30}, {66, -30}, {66, 2}, {82, 2}, {82, 2}}, color = {0, 0, 255})); connect(l23.pin6, rl1.pin3) annotation( Line(points = {{58, -24}, {72, -24}, {72, 8}, {82, 8}, {82, 8}}, color = {0, 0, 255})); connect(l23.pin4, rl1.pin1) annotation( Line(points = {{58, -36}, {62, -36}, {62, -4}, {82, -4}, {82, -4}}, color = {0, 0, 255})); annotation( Diagram); end Microgrid; ================================================ FILE: omg_grid/Grids/Network.mo ================================================ within omg_grid.Grids; model Network omg_grid.Inverters.Inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.LC lc1 annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Inverters.Inverter inverter2 annotation( Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.LC lc2 annotation( Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); omg_grid.Filter.IdealFilter.LCL lcl1 annotation( Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Loads.RL rl1 annotation( Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(lc1.pin6, lc2.pin3) annotation( Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255})); connect(lc1.pin5, lc2.pin2) annotation( Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255})); connect(lc1.pin4, lc2.pin1) annotation( Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255})); connect(inverter1.pin3, lc1.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, lc1.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, lc1.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255})); connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(i2p3, inverter2.u3) annotation( Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127})); connect(i2p2, inverter2.u2) annotation( Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127})); connect(i2p1, inverter2.u1) annotation( Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127})); connect(inverter2.pin3, lcl1.pin3) annotation( Line(points = {{-60, -24}, {-42, -24}}, color = {0, 0, 255})); connect(inverter2.pin2, lcl1.pin2) annotation( Line(points = {{-60, -30}, {-42, -30}}, color = {0, 0, 255})); connect(inverter2.pin1, lcl1.pin1) annotation( Line(points = {{-60, -36}, {-42, -36}}, color = {0, 0, 255})); connect(lcl1.pin6, lc2.pin3) annotation( Line(points = {{-22, -24}, {-6, -24}, {-6, 36}, {20, 36}}, color = {0, 0, 255})); connect(lcl1.pin5, lc2.pin2) annotation( Line(points = {{-22, -30}, {0, -30}, {0, 30}, {20, 30}}, color = {0, 0, 255})); connect(lcl1.pin4, lc2.pin1) annotation( Line(points = {{-22, -36}, {6, -36}, {6, 24}, {20, 24}}, color = {0, 0, 255})); connect(lc2.pin6, rl1.pin3) annotation( Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255})); connect(lc2.pin5, rl1.pin2) annotation( Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255})); connect(lc2.pin4, rl1.pin1) annotation( Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255})); annotation( Diagram); end Network; ================================================ FILE: omg_grid/Grids/NetworkSineTest.bak-mo ================================================ within omg_grid.Grids; model NetworkSineTest Modelica.Blocks.Sources.Sine sine(amplitude = 230, freqHz = 50) annotation( Placement(visible = true, transformation(origin = {-76, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine1(amplitude = 230, freqHz = 50, phase = 2.0944) annotation( Placement(visible = true, transformation(origin = {-76, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine2(amplitude = 230, freqHz = 50, phase = 4.18879) annotation( Placement(visible = true, transformation(origin = {-76, -16}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine3(amplitude = 230, freqHz = 50) annotation( Placement(visible = true, transformation(origin = {-76, 14}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine4(amplitude = 230, freqHz = 50, phase = 2.0944) annotation( Placement(visible = true, transformation(origin = {-76, 48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine5(amplitude = 230, freqHz = 50, phase = 4.18879) annotation( Placement(visible = true, transformation(origin = {-76, 80}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Grids.Network network1 annotation( Placement(visible = true, transformation(origin = {38, 2}, extent = {{-46, -46}, {46, 46}}, rotation = 0))); equation connect(sine5.y, network1.i1p3) annotation( Line(points = {{-64, 80}, {-34, 80}, {-34, 22}, {-10, 22}, {-10, 22}}, color = {0, 0, 127})); connect(sine4.y, network1.i1p2) annotation( Line(points = {{-64, 48}, {-40, 48}, {-40, 16}, {-10, 16}, {-10, 16}}, color = {0, 0, 127})); connect(sine3.y, network1.i1p1) annotation( Line(points = {{-64, 14}, {-34, 14}, {-34, 10}, {-10, 10}, {-10, 10}}, color = {0, 0, 127})); connect(sine2.y, network1.i2p3) annotation( Line(points = {{-64, -16}, {-64, -16}, {-64, -6}, {-10, -6}, {-10, -6}}, color = {0, 0, 127})); connect(sine1.y, network1.i2p2) annotation( Line(points = {{-64, -48}, {-50, -48}, {-50, -12}, {-10, -12}, {-10, -12}, {-10, -12}}, color = {0, 0, 127})); connect(sine.y, network1.i2p1) annotation( Line(points = {{-64, -82}, {-32, -82}, {-32, -18}, {-10, -18}, {-10, -18}}, color = {0, 0, 127})); end NetworkSineTest; ================================================ FILE: omg_grid/Grids/NetworkSingleInverter.mo ================================================ within omg_grid.Grids; model NetworkSingleInverter omg_grid.Inverters.Inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 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( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); omg_grid.Loads.RL rl1 annotation( Placement(visible = true, transformation(origin = {24, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(inverter1.pin3, lc1.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, lc1.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, lc1.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255})); connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(lc1.pin6, rl1.pin3) annotation( Line(points = {{-20, 36}, {14, 36}}, color = {0, 0, 255})); connect(lc1.pin5, rl1.pin2) annotation( Line(points = {{-20, 30}, {14, 30}}, color = {0, 0, 255})); connect(lc1.pin4, rl1.pin1) annotation( Line(points = {{-20, 24}, {14, 24}}, color = {0, 0, 255})); annotation( Diagram); end NetworkSingleInverter; ================================================ FILE: omg_grid/Grids/PLL.bak-mo ================================================ within omg_grid.Grids; model PLL Modelica.Electrical.Analog.Interfaces.Pin a annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin b annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin c annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground annotation( Placement(visible = true, transformation(origin = {-86, 62}, extent = {{-6, -6}, {6, 6}}, rotation = 180))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_c annotation( Placement(visible = true, transformation(origin = {-88, -8}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_a annotation( Placement(visible = true, transformation(origin = {-86, 50}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_b annotation( Placement(visible = true, transformation(origin = {-88, 22}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); omg_grid.Transformations.ABC2AlphaBeta abc2AlphaBeta annotation( Placement(visible = true, transformation(origin = {-62, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Sin sin annotation( Placement(visible = true, transformation(origin = {-10, -6}, extent = {{-4, -4}, {4, 4}}, rotation = 180))); Modelica.Blocks.Math.Cos cos annotation( Placement(visible = true, transformation(origin = {-10, -18}, extent = {{-4, -4}, {4, 4}}, rotation = 180))); Modelica.Blocks.Math.Gain Norm_U_ref_alpha(k = 1 / (230 * 1.414)) annotation( Placement(visible = true, transformation(origin = {-33, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Gain Norm_U_ref_beta(k = 1 / (230 * 1.414)) annotation( Placement(visible = true, transformation(origin = {-33, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Product alphaSin annotation( Placement(visible = true, transformation(origin = {-7, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Product betaCos annotation( Placement(visible = true, transformation(origin = {-9, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Add add(k1 = -1, k2 = +1) annotation( Placement(visible = true, transformation(origin = {12, 24}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Continuous.PI pi(T = 0.2, k = 15) annotation( Placement(visible = true, transformation(origin = {26, 24}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add_freq_nom_delta_f(k1 = +1, k2 = +1) annotation( Placement(visible = true, transformation(origin = {48, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Sources.Constant f_nom(k = 50) annotation( Placement(visible = true, transformation(origin = {28, 4}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Continuous.Integrator f2theta(y_start = 0) annotation( Placement(visible = true, transformation(origin = {64, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Math.Gain deg2rad(k = 2 * 3.1416) annotation( Placement(visible = true, transformation(origin = {78, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); equation connect(a, voltageSensor_a.p) annotation( Line(points = {{-100, 44}, {-86, 44}}, color = {0, 0, 255})); connect(b, voltageSensor_b.p) annotation( Line(points = {{-100, 16}, {-88, 16}}, color = {0, 0, 255})); connect(c, voltageSensor_c.p) annotation( Line(points = {{-100, -14}, {-88, -14}}, color = {0, 0, 255})); connect(voltageSensor_a.n, ground.p) annotation( Line(points = {{-86, 56}, {-86, 56}}, color = {0, 0, 255})); connect(voltageSensor_b.n, ground.p) annotation( Line(points = {{-88, 28}, {-88, 42}, {-86, 42}, {-86, 56}}, color = {0, 0, 255})); connect(voltageSensor_c.n, ground.p) annotation( Line(points = {{-88, -2}, {-88, 27}, {-86, 27}, {-86, 56}}, color = {0, 0, 255})); connect(abc2AlphaBeta.b, voltageSensor_b.v) annotation( Line(points = {{-72, 21}, {-74, 21}, {-74, 22}, {-82, 22}}, color = {0, 0, 127})); connect(abc2AlphaBeta.a, voltageSensor_a.v) annotation( Line(points = {{-72, 24}, {-76, 24}, {-76, 50}, {-80, 50}}, color = {0, 0, 127})); connect(abc2AlphaBeta.c, voltageSensor_c.v) annotation( Line(points = {{-72, 18}, {-76, 18}, {-76, -8}, {-82, -8}}, color = {0, 0, 127})); connect(Norm_U_ref_alpha.u, abc2AlphaBeta.alpha) annotation( Line(points = {{-37, 29}, {-40, 29}, {-40, 26}, {-52, 26}}, color = {0, 0, 127})); connect(Norm_U_ref_beta.u, abc2AlphaBeta.beta) annotation( Line(points = {{-37, 15}, {-42, 15}, {-42, 17}, {-52, 17}}, color = {0, 0, 127})); connect(Norm_U_ref_alpha.y, alphaSin.u1) annotation( Line(points = {{-30, 30}, {-11, 30}, {-11, 31}}, color = {0, 0, 127})); connect(Norm_U_ref_beta.y, betaCos.u1) annotation( Line(points = {{-30, 16}, {-13, 16}, {-13, 17}}, color = {0, 0, 127})); connect(sin.y, alphaSin.u2) annotation( Line(points = {{-14, -6}, {-22, -6}, {-22, 27}, {-11, 27}}, color = {0, 0, 127})); connect(cos.y, betaCos.u2) annotation( Line(points = {{-14, -18}, {-18, -18}, {-18, 13}, {-13, 13}}, color = {0, 0, 127})); connect(add.u1, alphaSin.y) annotation( Line(points = {{7, 26}, {3.5, 26}, {3.5, 30}, {-4, 30}}, color = {0, 0, 127})); connect(betaCos.y, add.u2) annotation( Line(points = {{-6, 16}, {4, 16}, {4, 22}, {7, 22}}, color = {0, 0, 127})); connect(pi.u, add.y) annotation( Line(points = {{19, 24}, {16, 24}}, color = {0, 0, 127})); connect(add_freq_nom_delta_f.u1, pi.y) annotation( Line(points = {{43, 24}, {33, 24}}, color = {0, 0, 127})); connect(f_nom.y, add_freq_nom_delta_f.u2) annotation( Line(points = {{32, 4}, {36, 4}, {36, 20}, {43, 20}}, color = {0, 0, 127})); connect(f2theta.u, add_freq_nom_delta_f.y) annotation( Line(points = {{59, 22}, {52, 22}}, color = {0, 0, 127})); connect(deg2rad.u, f2theta.y) annotation( Line(points = {{74, 22}, {68, 22}, {68, 22}, {68, 22}}, color = {0, 0, 127})); connect(deg2rad.y, sin.u) annotation( Line(points = {{82, 22}, {92, 22}, {92, -6}, {-6, -6}, {-6, -6}}, color = {0, 0, 127})); connect(cos.u, deg2rad.y) annotation( Line(points = {{-6, -18}, {4, -18}, {4, -6}, {92, -6}, {92, 22}, {82, 22}, {82, 22}, {82, 22}}, color = {0, 0, 127})); end PLL; ================================================ FILE: omg_grid/Grids/PLL_Network.mo ================================================ within omg_grid.Grids; model PLL_Network omg_grid.Inverters.Inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.LC lc1 annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Loads.RC rc1 annotation( Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Inverters.Inverter inverter2 annotation( Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.LC lc2 annotation( Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); omg_grid.Filter.IdealFilter.LCL lcl1 annotation( Placement(visible = true, transformation(origin = {-30, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.PLLs.PLL pll annotation( Placement(visible = true, transformation(origin = {20, -62}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(lc2.pin4, rc1.pin1) annotation( Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255})); connect(lc2.pin5, rc1.pin2) annotation( Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255})); connect(lc2.pin6, rc1.pin3) annotation( Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255})); connect(lc1.pin6, lc2.pin3) annotation( Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255})); connect(lc1.pin5, lc2.pin2) annotation( Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255})); connect(lc1.pin4, lc2.pin1) annotation( Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255})); connect(inverter1.pin3, lc1.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, lc1.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, lc1.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255})); connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(i2p3, inverter2.u3) annotation( Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127})); connect(i2p2, inverter2.u2) annotation( Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127})); connect(i2p1, inverter2.u1) annotation( Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127})); connect(inverter2.pin3, lcl1.pin3) annotation( Line(points = {{-60, -24}, {-40, -24}, {-40, -24}, {-40, -24}}, color = {0, 0, 255})); connect(inverter2.pin2, lcl1.pin2) annotation( Line(points = {{-60, -30}, {-40, -30}, {-40, -30}, {-40, -30}}, color = {0, 0, 255})); connect(inverter2.pin1, lcl1.pin1) annotation( Line(points = {{-60, -36}, {-40, -36}, {-40, -36}, {-40, -36}}, color = {0, 0, 255})); connect(lcl1.pin6, lc2.pin3) annotation( Line(points = {{-20, -24}, {-4, -24}, {-4, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255})); connect(lcl1.pin5, lc2.pin2) annotation( Line(points = {{-20, -30}, {0, -30}, {0, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255})); connect(lcl1.pin4, lc2.pin1) annotation( Line(points = {{-20, -36}, {6, -36}, {6, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255})); connect(pll.a, lcl1.pin6) annotation( Line(points = {{10, -58}, {-14, -58}, {-14, -24}, {-20, -24}}, color = {0, 0, 255})); connect(pll.b, lcl1.pin5) annotation( Line(points = {{10, -60}, {-16, -60}, {-16, -30}, {-20, -30}}, color = {0, 0, 255})); connect(pll.c, lcl1.pin4) annotation( Line(points = {{10, -63}, {-18, -63}, {-18, -36}, {-20, -36}}, color = {0, 0, 255})); annotation( Diagram); end PLL_Network; ================================================ FILE: omg_grid/Grids/RLC_Network.mo ================================================ within omg_grid.Grids; model RLC_Network omg_grid.Inverters.Inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.LC lc1 annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Inverters.Inverter inverter2 annotation( Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.LC lc2 annotation( Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); omg_grid.Filter.IdealFilter.LCL lcl1 annotation( Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Loads.RLC rlc annotation( Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(lc1.pin6, lc2.pin3) annotation( Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255})); connect(lc1.pin5, lc2.pin2) annotation( Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255})); connect(lc1.pin4, lc2.pin1) annotation( Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255})); connect(inverter1.pin3, lc1.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, lc1.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, lc1.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255})); connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(i2p3, inverter2.u3) annotation( Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127})); connect(i2p2, inverter2.u2) annotation( Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127})); connect(i2p1, inverter2.u1) annotation( Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127})); connect(inverter2.pin3, lcl1.pin3) annotation( Line(points = {{-60, -24}, {-42, -24}}, color = {0, 0, 255})); connect(inverter2.pin2, lcl1.pin2) annotation( Line(points = {{-60, -30}, {-42, -30}}, color = {0, 0, 255})); connect(inverter2.pin1, lcl1.pin1) annotation( Line(points = {{-60, -36}, {-42, -36}}, color = {0, 0, 255})); connect(lcl1.pin6, lc2.pin3) annotation( Line(points = {{-22, -24}, {-6, -24}, {-6, 36}, {20, 36}}, color = {0, 0, 255})); connect(lcl1.pin5, lc2.pin2) annotation( Line(points = {{-22, -30}, {0, -30}, {0, 30}, {20, 30}}, color = {0, 0, 255})); connect(lcl1.pin4, lc2.pin1) annotation( Line(points = {{-22, -36}, {6, -36}, {6, 24}, {20, 24}}, color = {0, 0, 255})); connect(lc2.pin6, rlc.pin3) annotation( Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255})); connect(lc2.pin5, rlc.pin2) annotation( Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255})); connect(lc2.pin4, rlc.pin1) annotation( Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255})); annotation( Diagram); end RLC_Network; ================================================ FILE: omg_grid/Grids/SingleModel.mo ================================================ within omg_grid.Grids; model SingleModel omg_grid.Inverters.Inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Inverters.Inverter inverter2 annotation( Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); omg_grid.Loads.R r(R1 = 100, R2 = 100, R3 = 100) annotation( Placement(visible = true, transformation(origin = {8, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); omg_grid.Filter.IdealFilter.L l annotation( Placement(visible = true, transformation(origin = {-40, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(i2p3, inverter2.u3) annotation( Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127})); connect(i2p2, inverter2.u2) annotation( Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127})); connect(i2p1, inverter2.u1) annotation( Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127})); connect(inverter1.pin3, r.pin3) annotation( Line(points = {{-60, 36}, {-2, 36}, {-2, 36}, {-2, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, r.pin2) annotation( Line(points = {{-60, 30}, {-2, 30}, {-2, 30}, {-2, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, r.pin1) annotation( Line(points = {{-60, 24}, {-2, 24}, {-2, 24}, {-2, 24}}, color = {0, 0, 255})); connect(inverter2.pin3, l.pin3) annotation( Line(points = {{-60, -24}, {-50, -24}}, color = {0, 0, 255})); connect(inverter2.pin2, l.pin2) annotation( Line(points = {{-60, -30}, {-50, -30}}, color = {0, 0, 255})); connect(inverter2.pin1, l.pin1) annotation( Line(points = {{-60, -36}, {-50, -36}}, color = {0, 0, 255})); connect(l.pin6, r.pin3) annotation( Line(points = {{-30, -24}, {-20, -24}, {-20, 36}, {-2, 36}, {-2, 36}, {-2, 36}}, color = {0, 0, 255})); connect(l.pin5, r.pin2) annotation( Line(points = {{-30, -30}, {-14, -30}, {-14, 30}, {-2, 30}, {-2, 30}}, color = {0, 0, 255})); connect(l.pin4, r.pin1) annotation( Line(points = {{-30, -36}, {-8, -36}, {-8, 24}, {-2, 24}, {-2, 24}, {-2, 24}}, color = {0, 0, 255})); annotation( Diagram); end SingleModel; ================================================ FILE: omg_grid/Grids/Testbench_SC2.mo ================================================ within omg_grid.Grids; model Testbench_SC2 omg_grid.Inverters.Inverter inverter1(v_DC = 60) annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); omg_grid.Filter.LossesFilter.L rl(L1 = 0.0023, L2 = 0.0023, L3 = 0.0023, R1 = 0.170, R2 = 0.170, R3 = 0.170) annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(inverter1.pin3, rl.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}, {-40, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, rl.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}, {-40, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, rl.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}, {-40, 24}, {-40, 24}}, color = {0, 0, 255})); connect(rl.pin6, rl.pin4) annotation( Line(points = {{-20, 36}, {-14, 36}, {-14, 24}, {-20, 24}, {-20, 24}}, color = {0, 0, 255})); connect(rl.pin5, rl.pin6) annotation( Line(points = {{-20, 30}, {-14, 30}, {-14, 36}, {-20, 36}, {-20, 36}}, color = {0, 0, 255})); connect(rl.pin4, rl.pin5) annotation( Line(points = {{-20, 24}, {-14, 24}, {-14, 30}, {-20, 30}, {-20, 30}}, color = {0, 0, 255})); annotation( Diagram); end Testbench_SC2; ================================================ FILE: omg_grid/Grids/package.mo ================================================ within omg_grid; package Grids extends Modelica.Icons.Package; annotation (Icon(coordinateSystem(preserveAspectRatio=true, extent={{-100.0,-100.0},{100.0,100.0}}), graphics={ Rectangle( origin={20.3125,82.8571}, extent={{-45.3125,-57.8571},{4.6875,-27.8571}}), Line( origin={8.0,48.0}, points={{32.0,-58.0},{72.0,-58.0}}), Line( origin={9.0,54.0}, points={{31.0,-49.0},{71.0,-49.0}}), Line( origin={-2.0,55.0}, points={{-83.0,-50.0},{-33.0,-50.0}}), Line( origin={-3.0,45.0}, points={{-72.0,-55.0},{-42.0,-55.0}}), Line( origin={1.0,50.0}, points={{-61.0,-45.0},{-61.0,-10.0},{-26.0,-10.0}}), Line( origin={7.0,50.0}, points={{18.0,-10.0},{53.0,-10.0},{53.0,-45.0}}), Line( origin={6.2593,48.0}, points={{53.7407,-58.0},{53.7407,-93.0},{-66.2593,-93.0},{-66.2593,-58.0}})})); end Grids; ================================================ FILE: omg_grid/Grids/package.order ================================================ Network NetworkSingleInverter PLL_Network RLC_Network SingleModel Microgrid Testbench_SC2 Paper_SC Paper_Loadstep ================================================ FILE: omg_grid/Inverters/Inverter.mo ================================================ within omg_grid.Inverters; model Inverter parameter Real v_DC = 1000; Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {-74, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage1 annotation( Placement(visible = true, transformation(origin = {-74, -42}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage2 annotation( Placement(visible = true, transformation(origin = {-74, 18}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage3 annotation( Placement(visible = true, transformation(origin = {-74, 78}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Blocks.Interfaces.RealInput u1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput u3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput u2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Blocks.Math.Gain gain3(k = v_DC) annotation( Placement(visible = true, transformation(origin = {-26, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Gain gain1(k = v_DC) annotation( Placement(visible = true, transformation(origin = {-26, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Gain gain2(k = v_DC) annotation( Placement(visible = true, transformation(origin = {-26, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(signalVoltage2.p, pin2) annotation( Line(points = {{-74, 28}, {80, 28}, {80, 0}, {100, 0}}, color = {0, 0, 255})); connect(signalVoltage3.p, pin3) annotation( Line(points = {{-74, 88}, {80, 88}, {80, 60}, {100, 60}}, color = {0, 0, 255})); connect(signalVoltage1.p, pin1) annotation( Line(points = {{-74, -32}, {80, -32}, {80, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255})); connect(signalVoltage3.n, ground1.p) annotation( Line(points = {{-74, 68}, {-74, 48}, {-82, 48}, {-82, -72}, {-74, -72}}, color = {0, 0, 255})); connect(signalVoltage2.n, ground1.p) annotation( Line(points = {{-74, 8}, {-82, 8}, {-82, -72}, {-74, -72}}, color = {0, 0, 255})); connect(signalVoltage1.n, ground1.p) annotation( Line(points = {{-74, -52}, {-74, -72}}, color = {0, 0, 255})); /* connect(signalVoltage1.v, regler1) annotation( Line); connect(signalVoltage2.v, regler2) annotation( Line); connect(signalVoltage3.v, regler3) annotation( Line); */ connect(u1, gain1.u) annotation( Line(points = {{-104, -60}, {-38, -60}, {-38, -60}, {-38, -60}}, color = {0, 0, 127})); connect(gain1.y, signalVoltage1.v) annotation( Line(points = {{-14, -60}, {-6, -60}, {-6, -42}, {-60, -42}, {-60, -42}, {-62, -42}}, color = {0, 0, 127})); connect(u2, gain2.u) annotation( Line(points = {{-104, 0}, {-38, 0}, {-38, 0}, {-38, 0}}, color = {0, 0, 127})); connect(gain2.y, signalVoltage2.v) annotation( Line(points = {{-14, 0}, {-6, 0}, {-6, 18}, {-62, 18}, {-62, 18}}, color = {0, 0, 127})); connect(u3, gain3.u) annotation( Line(points = {{-104, 60}, {-38, 60}, {-38, 60}, {-38, 60}}, color = {0, 0, 127})); connect(gain3.y, signalVoltage3.v) annotation( Line(points = {{-14, 60}, {-6, 60}, {-6, 78}, {-62, 78}, {-62, 78}}, color = {0, 0, 127})); annotation( uses(Modelica(version = "3.2.3"))); end Inverter; ================================================ FILE: omg_grid/Inverters/package.mo ================================================ within omg_grid; package Inverters extends Modelica.Icons.Package; annotation (Icon(graphics={Line( points={{-80,-2},{-68.7,32.2},{-61.5,51.1},{-55.1,64.4},{-49.4,72.6}, {-43.8,77.1},{-38.2,77.8},{-32.6,74.6},{-26.9,67.7},{-21.3,57.4}, {-14.9,42.1},{-6.83,19.2},{10.1,-32.8},{17.3,-52.2},{23.7,-66.2}, {29.3,-75.1},{35,-80.4},{40.6,-82},{46.2,-79.6},{51.9,-73.5},{ 57.5,-63.9},{63.9,-49.2},{72,-26.8},{80,-2}}, color={95,95,95}, smooth=Smooth.Bezier)})); end Inverters; ================================================ FILE: omg_grid/Inverters/package.order ================================================ Inverter ================================================ FILE: omg_grid/Loads/C.mo ================================================ within omg_grid.Loads; model C parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-34, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {4, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {40, 38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {4, -84}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(capacitor3.p, pin3) annotation( Line(points = {{40, 48}, {40, 60}, {-100, 60}}, color = {0, 0, 255})); connect(pin2, capacitor2.p) annotation( Line(points = {{-102, 0}, {4, 0}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{4, -20}, {4, -74}}, color = {0, 0, 255})); connect(pin1, capacitor1.p) annotation( Line(points = {{-100, -60}, {-67, -60}, {-67, -34}, {-34, -34}}, color = {0, 0, 255})); connect(capacitor3.n, ground1.p) annotation( Line(points = {{40, 28}, {40, -54}, {4, -54}, {4, -74}}, color = {0, 0, 255})); connect(capacitor1.n, ground1.p) annotation( Line(points = {{-34, -54}, {4, -54}, {4, -74}, {4, -74}}, color = {0, 0, 255})); end C; ================================================ FILE: omg_grid/Loads/L.mo ================================================ within omg_grid.Loads; model L parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-48, -50}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {0, -16}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {50, 50}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(inductor3.n, ground1.p) annotation( Line(points = {{50, 40}, {50, 40}, {50, -60}, {0, -60}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {50, 60}, {50, 60}, {50, 60}}, color = {0, 0, 255})); connect(inductor2.n, ground1.p) annotation( Line(points = {{0, -26}, {0, -26}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {0, 0}, {0, -6}}, color = {0, 0, 255})); connect(inductor1.n, ground1.p) annotation( Line(points = {{-48, -60}, {0, -60}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-78, -60}, {-78, -40}, {-48, -40}, {-48, -40}, {-48, -40}}, color = {0, 0, 255})); end L; ================================================ FILE: omg_grid/Loads/LC.mo ================================================ within omg_grid.Loads; model LC parameter SI.Capacitance C1(start = 0.00001); parameter SI.Capacitance C2(start = 0.00001); parameter SI.Capacitance C3(start = 0.00001); parameter SI.Inductance L1(start = 0.001); parameter SI.Inductance L2(start = 0.001); parameter SI.Inductance L3(start = 0.001); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-56, -18}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {0, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {56, 42}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-56, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {0, -24}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {56, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(capacitor3.n, inductor3.p) annotation( Line(points = {{56, 32}, {56, 32}, {56, 18}, {56, 18}}, color = {0, 0, 255})); connect(inductor3.n, ground1.p) annotation( Line(points = {{56, -2}, {56, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(capacitor1.n, inductor1.p) annotation( Line(points = {{-56, -28}, {-56, -28}, {-56, -34}, {-56, -34}}, color = {0, 0, 255})); connect(inductor1.n, ground1.p) annotation( Line(points = {{-56, -54}, {-56, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(capacitor1.p, pin1) annotation( Line(points = {{-56, -8}, {-56, 0}, {-88, 0}, {-88, -60}, {-100, -60}}, color = {0, 0, 255})); connect(capacitor2.p, pin2) annotation( Line(points = {{0, 18}, {0, 24}, {-94, 24}, {-94, 0}, {-100, 0}}, color = {0, 0, 255})); connect(capacitor2.n, inductor2.p) annotation( Line(points = {{0, -2}, {0, -2}, {0, -14}, {0, -14}}, color = {0, 0, 255})); connect(inductor2.n, ground1.p) annotation( Line(points = {{0, -34}, {0, -76}}, color = {0, 0, 255})); connect(pin3, capacitor3.p) annotation( Line(points = {{-100, 60}, {56, 60}, {56, 52}}, color = {0, 0, 255})); connect(capacitor3.p, pin3) annotation( Line(points = {{56, 52}, {56, 60}, {-100, 60}}, color = {0, 0, 255})); end LC; ================================================ FILE: omg_grid/Loads/R.mo ================================================ within omg_grid.Loads; model R parameter SI.Resistance R1 = 20; parameter SI.Resistance R2 = 20; parameter SI.Resistance R3 = 20; Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-66, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R1) annotation( Placement(visible = true, transformation(origin = {-66, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R1) annotation( Placement(visible = true, transformation(origin = {-66, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(resistor3.n, ground1.p) annotation( Line(points = {{-56, 60}, {0, 60}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(resistor2.n, ground1.p) annotation( Line(points = {{-56, 0}, {-56, 0}, {-56, 0}, {0, 0}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(resistor1.n, ground1.p) annotation( Line(points = {{-56, -60}, {0, -60}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin1, resistor1.p) annotation( Line(points = {{-100, -60}, {-76, -60}, {-76, -60}, {-76, -60}}, color = {0, 0, 255})); connect(pin2, resistor2.p) annotation( Line(points = {{-100, 0}, {-100, 0}, {-100, 0}, {-76, 0}}, color = {0, 0, 255})); connect(pin3, resistor3.p) annotation( Line(points = {{-100, 60}, {-76, 60}, {-76, 60}, {-76, 60}}, color = {0, 0, 255})); end R; ================================================ FILE: omg_grid/Loads/RC.mo ================================================ within omg_grid.Loads; model RC parameter SI.Resistance R1 = 20; parameter SI.Resistance R2 = 20; parameter SI.Resistance R3 = 20; parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-66, -48}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {-32, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {48, 0}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-50, -48}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {0, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {72, -2}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(resistor2.n, ground1.p) annotation( Line(points = {{0, -20}, {0, -20}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin2, resistor2.p) annotation( Line(points = {{-100, 0}, {0, 0}, {0, 0}, {0, 0}}, color = {0, 0, 255})); connect(resistor1.p, pin1) annotation( Line(points = {{-50, -38}, {-50, -38}, {-50, -22}, {-90, -22}, {-90, -60}, {-100, -60}, {-100, -60}}, color = {0, 0, 255})); connect(resistor1.n, ground1.p) annotation( Line(points = {{-50, -58}, {-50, -58}, {-50, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(resistor3.n, ground1.p) annotation( Line(points = {{72, -12}, {72, -12}, {72, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin3, resistor3.p) annotation( Line(points = {{-100, 60}, {68, 60}, {68, 60}, {72, 60}, {72, 8}, {72, 8}}, color = {0, 0, 255})); connect(capacitor1.p, pin1) annotation( Line(points = {{-66, -38}, {-66, -22}, {-90, -22}, {-90, -60}, {-100, -60}}, color = {0, 0, 255})); connect(capacitor3.p, pin3) annotation( Line(points = {{48, 10}, {48, 60}, {-100, 60}}, color = {0, 0, 255})); connect(pin3, capacitor3.p) annotation( Line(points = {{-100, 60}, {48, 60}, {48, 10}}, color = {0, 0, 255})); connect(capacitor3.n, ground1.p) annotation( Line(points = {{48, -10}, {48, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(capacitor2.p, pin2) annotation( Line(points = {{-32, 0}, {-100, 0}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{-32, -20}, {-32, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(capacitor1.n, ground1.p) annotation( Line(points = {{-66, -58}, {-66, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); end RC; ================================================ FILE: omg_grid/Loads/RL.mo ================================================ within omg_grid.Loads; model RL parameter SI.Resistance R1 = 20; parameter SI.Resistance R2 = 20; parameter SI.Resistance R3 = 20; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-40, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {0, -18}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {60, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor1(R=R1) annotation( Placement(visible = true, transformation(origin = {-40, -20}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor2(R=R2) annotation( Placement(visible = true, transformation(origin = {0, 12}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor3(R=R3) annotation( Placement(visible = true, transformation(origin = {60, 34}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(resistor1.n, inductor1.p) annotation( Line(points = {{-40, -30}, {-40, -30}, {-40, -36}, {-40, -36}}, color = {0, 0, 255})); connect(pin3, resistor3.p) annotation( Line(points = {{-100, 60}, {60, 60}, {60, 44}, {60, 44}}, color = {0, 0, 255})); connect(resistor3.n, inductor3.p) annotation( Line(points = {{60, 24}, {60, 24}, {60, 24}, {60, 18}}, color = {0, 0, 255})); connect(resistor2.n, inductor2.p) annotation( Line(points = {{0, 2}, {0, 2}, {0, -8}, {0, -8}}, color = {0, 0, 255})); connect(pin2, resistor2.p) annotation( Line(points = {{-100, 0}, {-66, 0}, {-66, 22}, {0, 22}}, color = {0, 0, 255})); connect(pin1, resistor1.p) annotation( Line(points = {{-100, -60}, {-74, -60}, {-74, -10}, {-40, -10}, {-40, -10}}, color = {0, 0, 255})); connect(inductor3.n, ground1.p) annotation( Line(points = {{60, -2}, {60, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(inductor1.n, ground1.p) annotation( Line(points = {{-40, -56}, {-40, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(inductor2.n, ground1.p) annotation( Line(points = {{0, -28}, {0, -76}}, color = {0, 0, 255})); end RL; ================================================ FILE: omg_grid/Loads/RLC.mo ================================================ within omg_grid.Loads; model RLC parameter SI.Resistance R1 = 20; parameter SI.Resistance R2 = 20; parameter SI.Resistance R3 = 20; parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-74, -68}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {0, -30}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {74, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-74, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {0, 2}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {74, -4}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-74, -20}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {0, 36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {74, 42}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(resistor1.p, pin1) annotation( Line(points = {{-74, -10}, {-100, -10}, {-100, -60}}, color = {0, 0, 255})); connect(pin2, resistor2.p) annotation( Line(points = {{-100, 0}, {-50, 0}, {-50, 46}, {0, 46}}, color = {0, 0, 255})); connect(resistor3.p, pin3) annotation( Line(points = {{74, 52}, {74, 52}, {74, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{0, -40}, {0, -76}}, color = {0, 0, 255})); connect(capacitor3.n, ground1.p) annotation( Line(points = {{74, -56}, {74, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(capacitor1.p, inductor1.n) annotation( Line(points = {{-74, -58}, {-74, -58}, {-74, -54}, {-74, -54}}, color = {0, 0, 255})); connect(resistor1.n, inductor1.p) annotation( Line(points = {{-74, -30}, {-74, -30}, {-74, -30}, {-74, -34}}, color = {0, 0, 255})); connect(resistor2.n, inductor2.p) annotation( Line(points = {{0, 26}, {0, 26}, {0, 12}, {0, 12}}, color = {0, 0, 255})); connect(inductor2.n, capacitor2.p) annotation( Line(points = {{0, -8}, {0, -8}, {0, -8}, {0, -20}}, color = {0, 0, 255})); connect(resistor3.n, inductor3.p) annotation( Line(points = {{74, 32}, {74, 32}, {74, 6}, {74, 6}}, color = {0, 0, 255})); connect(capacitor3.p, inductor3.n) annotation( Line(points = {{74, -36}, {74, -36}, {74, -14}, {74, -14}}, color = {0, 0, 255})); connect(capacitor1.n, ground1.p) annotation( Line(points = {{-74, -78}, {-46, -78}, {-46, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255})); end RLC; ================================================ FILE: omg_grid/Loads/package.mo ================================================ within omg_grid; package Loads extends Modelica.Icons.Package; annotation (Icon(coordinateSystem(preserveAspectRatio=true, extent={{-100.0,-100.0},{100.0,100.0}}), graphics={ Rectangle( origin={0.0,35.1488}, fillColor={255,255,255}, extent={{-30.0,-20.1488},{30.0,20.1488}}), Rectangle( origin={0.0,-34.8512}, fillColor={255,255,255}, extent={{-30.0,-20.1488},{30.0,20.1488}}), Line( origin={-51.25,0.0}, points={{21.25,-35.0},{-13.75,-35.0},{-13.75,35.0},{6.25,35.0}}), Polygon( origin={-40.0,35.0}, pattern=LinePattern.None, fillPattern=FillPattern.Solid, points={{10.0,0.0},{-5.0,5.0},{-5.0,-5.0}}), Line( origin={51.25,0.0}, points={{-21.25,35.0},{13.75,35.0},{13.75,-35.0},{-6.25,-35.0}}), Polygon( origin={40.0,-35.0}, pattern=LinePattern.None, fillPattern=FillPattern.Solid, points={{-10.0,0.0},{5.0,5.0},{5.0,-5.0}})})); end Loads; ================================================ FILE: omg_grid/Loads/package.order ================================================ RL RC RLC R LC C L ================================================ FILE: omg_grid/PLLs/Inverter.bak-mo ================================================ within omg_grid.PLLs; model Inverter parameter Real v_DC = 1000; Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {-74, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage1 annotation( Placement(visible = true, transformation(origin = {-74, -42}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage2 annotation( Placement(visible = true, transformation(origin = {-74, 18}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage3 annotation( Placement(visible = true, transformation(origin = {-74, 78}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Blocks.Interfaces.RealInput u1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput u3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput u2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Blocks.Math.Gain gain3(k = v_DC) annotation( Placement(visible = true, transformation(origin = {-26, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Gain gain1(k = v_DC) annotation( Placement(visible = true, transformation(origin = {-26, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Gain gain2(k = v_DC) annotation( Placement(visible = true, transformation(origin = {-26, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(signalVoltage2.p, pin2) annotation( Line(points = {{-74, 28}, {80, 28}, {80, 0}, {100, 0}}, color = {0, 0, 255})); connect(signalVoltage3.p, pin3) annotation( Line(points = {{-74, 88}, {80, 88}, {80, 60}, {100, 60}}, color = {0, 0, 255})); connect(signalVoltage1.p, pin1) annotation( Line(points = {{-74, -32}, {80, -32}, {80, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255})); connect(signalVoltage3.n, ground1.p) annotation( Line(points = {{-74, 68}, {-74, 48}, {-82, 48}, {-82, -72}, {-74, -72}}, color = {0, 0, 255})); connect(signalVoltage2.n, ground1.p) annotation( Line(points = {{-74, 8}, {-82, 8}, {-82, -72}, {-74, -72}}, color = {0, 0, 255})); connect(signalVoltage1.n, ground1.p) annotation( Line(points = {{-74, -52}, {-74, -72}}, color = {0, 0, 255})); /* connect(signalVoltage1.v, regler1) annotation( Line); connect(signalVoltage2.v, regler2) annotation( Line); connect(signalVoltage3.v, regler3) annotation( Line); */ connect(u1, gain1.u) annotation( Line(points = {{-104, -60}, {-38, -60}, {-38, -60}, {-38, -60}}, color = {0, 0, 127})); connect(gain1.y, signalVoltage1.v) annotation( Line(points = {{-14, -60}, {-6, -60}, {-6, -42}, {-60, -42}, {-60, -42}, {-62, -42}}, color = {0, 0, 127})); connect(u2, gain2.u) annotation( Line(points = {{-104, 0}, {-38, 0}, {-38, 0}, {-38, 0}}, color = {0, 0, 127})); connect(gain2.y, signalVoltage2.v) annotation( Line(points = {{-14, 0}, {-6, 0}, {-6, 18}, {-62, 18}, {-62, 18}}, color = {0, 0, 127})); connect(u3, gain3.u) annotation( Line(points = {{-104, 60}, {-38, 60}, {-38, 60}, {-38, 60}}, color = {0, 0, 127})); connect(gain3.y, signalVoltage3.v) annotation( Line(points = {{-14, 60}, {-6, 60}, {-6, 78}, {-62, 78}, {-62, 78}}, color = {0, 0, 127})); annotation( uses(Modelica(version = "3.2.3"))); end Inverter; ================================================ FILE: omg_grid/PLLs/PLL.mo ================================================ within omg_grid.PLLs; model PLL Modelica.Electrical.Analog.Interfaces.Pin a annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin b annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin c annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground annotation( Placement(visible = true, transformation(origin = {-86, 62}, extent = {{-6, -6}, {6, 6}}, rotation = 180))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_c annotation( Placement(visible = true, transformation(origin = {-88, -8}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_a annotation( Placement(visible = true, transformation(origin = {-86, 50}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_b annotation( Placement(visible = true, transformation(origin = {-88, 22}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); omg_grid.Transformations.ABC2AlphaBeta abc2AlphaBeta annotation( Placement(visible = true, transformation(origin = {-62, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Sin sin annotation( Placement(visible = true, transformation(origin = {-10, -6}, extent = {{-4, -4}, {4, 4}}, rotation = 180))); Modelica.Blocks.Math.Cos cos annotation( Placement(visible = true, transformation(origin = {-10, -18}, extent = {{-4, -4}, {4, 4}}, rotation = 180))); Modelica.Blocks.Math.Gain Norm_U_ref_alpha(k = 1 / (230 * 1.414)) annotation( Placement(visible = true, transformation(origin = {-33, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Gain Norm_U_ref_beta(k = 1 / (230 * 1.414)) annotation( Placement(visible = true, transformation(origin = {-33, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Product alphaSin annotation( Placement(visible = true, transformation(origin = {-7, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Product betaCos annotation( Placement(visible = true, transformation(origin = {-9, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Add add(k1 = -1, k2 = +1) annotation( Placement(visible = true, transformation(origin = {12, 24}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Continuous.PI pi(T = 0.2, k = 15) annotation( Placement(visible = true, transformation(origin = {26, 24}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add_freq_nom_delta_f(k1 = +1, k2 = +1) annotation( Placement(visible = true, transformation(origin = {48, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Sources.Constant f_nom(k = 50) annotation( Placement(visible = true, transformation(origin = {28, 4}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Continuous.Integrator f2theta(y_start = 0) annotation( Placement(visible = true, transformation(origin = {64, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Math.Gain deg2rad(k = 2 * 3.1416) annotation( Placement(visible = true, transformation(origin = {78, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); equation connect(a, voltageSensor_a.p) annotation( Line(points = {{-100, 44}, {-86, 44}}, color = {0, 0, 255})); connect(b, voltageSensor_b.p) annotation( Line(points = {{-100, 16}, {-88, 16}}, color = {0, 0, 255})); connect(c, voltageSensor_c.p) annotation( Line(points = {{-100, -14}, {-88, -14}}, color = {0, 0, 255})); connect(voltageSensor_a.n, ground.p) annotation( Line(points = {{-86, 56}, {-86, 56}}, color = {0, 0, 255})); connect(voltageSensor_b.n, ground.p) annotation( Line(points = {{-88, 28}, {-88, 42}, {-86, 42}, {-86, 56}}, color = {0, 0, 255})); connect(voltageSensor_c.n, ground.p) annotation( Line(points = {{-88, -2}, {-88, 27}, {-86, 27}, {-86, 56}}, color = {0, 0, 255})); connect(abc2AlphaBeta.b, voltageSensor_b.v) annotation( Line(points = {{-72, 21}, {-74, 21}, {-74, 22}, {-82, 22}}, color = {0, 0, 127})); connect(abc2AlphaBeta.a, voltageSensor_a.v) annotation( Line(points = {{-72, 24}, {-76, 24}, {-76, 50}, {-80, 50}}, color = {0, 0, 127})); connect(abc2AlphaBeta.c, voltageSensor_c.v) annotation( Line(points = {{-72, 18}, {-76, 18}, {-76, -8}, {-82, -8}}, color = {0, 0, 127})); connect(Norm_U_ref_alpha.u, abc2AlphaBeta.alpha) annotation( Line(points = {{-37, 29}, {-40, 29}, {-40, 26}, {-52, 26}}, color = {0, 0, 127})); connect(Norm_U_ref_beta.u, abc2AlphaBeta.beta) annotation( Line(points = {{-37, 15}, {-42, 15}, {-42, 17}, {-52, 17}}, color = {0, 0, 127})); connect(Norm_U_ref_alpha.y, alphaSin.u1) annotation( Line(points = {{-30, 30}, {-11, 30}, {-11, 31}}, color = {0, 0, 127})); connect(Norm_U_ref_beta.y, betaCos.u1) annotation( Line(points = {{-30, 16}, {-13, 16}, {-13, 17}}, color = {0, 0, 127})); connect(sin.y, alphaSin.u2) annotation( Line(points = {{-14, -6}, {-22, -6}, {-22, 27}, {-11, 27}}, color = {0, 0, 127})); connect(cos.y, betaCos.u2) annotation( Line(points = {{-14, -18}, {-18, -18}, {-18, 13}, {-13, 13}}, color = {0, 0, 127})); connect(add.u1, alphaSin.y) annotation( Line(points = {{7, 26}, {3.5, 26}, {3.5, 30}, {-4, 30}}, color = {0, 0, 127})); connect(betaCos.y, add.u2) annotation( Line(points = {{-6, 16}, {4, 16}, {4, 22}, {7, 22}}, color = {0, 0, 127})); connect(pi.u, add.y) annotation( Line(points = {{19, 24}, {16, 24}}, color = {0, 0, 127})); connect(add_freq_nom_delta_f.u1, pi.y) annotation( Line(points = {{43, 24}, {33, 24}}, color = {0, 0, 127})); connect(f_nom.y, add_freq_nom_delta_f.u2) annotation( Line(points = {{32, 4}, {36, 4}, {36, 20}, {43, 20}}, color = {0, 0, 127})); connect(f2theta.u, add_freq_nom_delta_f.y) annotation( Line(points = {{59, 22}, {52, 22}}, color = {0, 0, 127})); connect(deg2rad.u, f2theta.y) annotation( Line(points = {{74, 22}, {68, 22}, {68, 22}, {68, 22}}, color = {0, 0, 127})); connect(deg2rad.y, sin.u) annotation( Line(points = {{82, 22}, {92, 22}, {92, -6}, {-6, -6}, {-6, -6}}, color = {0, 0, 127})); connect(cos.u, deg2rad.y) annotation( Line(points = {{-6, -18}, {4, -18}, {4, -6}, {92, -6}, {92, 22}, {82, 22}, {82, 22}, {82, 22}}, color = {0, 0, 127})); end PLL; ================================================ FILE: omg_grid/PLLs/PLL_DQ.mo ================================================ within omg_grid.PLLs; model PLL_DQ Real Pi = 3.14159265; Modelica.Electrical.Analog.Interfaces.Pin a annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin b annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin c annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground annotation( Placement(visible = true, transformation(origin = {-86, 62}, extent = {{-6, -6}, {6, 6}}, rotation = 180))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_c annotation( Placement(visible = true, transformation(origin = {-88, -8}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_a annotation( Placement(visible = true, transformation(origin = {-86, 50}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_b annotation( Placement(visible = true, transformation(origin = {-88, 22}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); omg_grid.Transformations.ABC2AlphaBeta abc2AlphaBeta annotation( Placement(visible = true, transformation(origin = {-52, 78}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Sin sin annotation( Placement(visible = true, transformation(origin = {-8, 56}, extent = {{-4, -4}, {4, 4}}, rotation = 180))); Modelica.Blocks.Math.Cos cos annotation( Placement(visible = true, transformation(origin = {-8, 46}, extent = {{-4, -4}, {4, 4}}, rotation = 180))); Modelica.Blocks.Math.Gain Norm_U_ref_alpha(k = 1 / (230 * 1.414)) annotation( Placement(visible = true, transformation(origin = {-31, 83}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Gain Norm_U_ref_beta(k = 1 / (230 * 1.414)) annotation( Placement(visible = true, transformation(origin = {-31, 69}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Product alphaSin annotation( Placement(visible = true, transformation(origin = {-5, 83}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Product betaCos annotation( Placement(visible = true, transformation(origin = {-7, 69}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Add add(k1 = -1, k2 = +1) annotation( Placement(visible = true, transformation(origin = {14, 78}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Continuous.PI pi(T = 0.05, k = 20) annotation( Placement(visible = true, transformation(origin = {28, 78}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add_freq_nom_delta_f(k1 = +1, k2 = +1) annotation( Placement(visible = true, transformation(origin = {50, 76}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Sources.Constant f_nom(k = 50) annotation( Placement(visible = true, transformation(origin = {30, 58}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Continuous.Integrator f2theta(y_start = 0) annotation( Placement(visible = true, transformation(origin = {66, 76}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Math.Gain deg2rad(k = 2 * 3.1416) annotation( Placement(visible = true, transformation(origin = {80, 76}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Math.Add add1(k2 = -1) annotation( Placement(visible = true, transformation(origin = {-6, 8}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Gain gain(k = 2 / 3) annotation( Placement(visible = true, transformation(origin = {-64, 40}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Gain gain1(k = 2 / 3) annotation( Placement(visible = true, transformation(origin = {-63, 17}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Product product2 annotation( Placement(visible = true, transformation(origin = {38, 34}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Cos cos2 annotation( Placement(visible = true, transformation(origin = {14, 30}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.MultiSum multiSum(nu = 3) annotation( Placement(visible = true, transformation(origin = {64, 28}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Product product1 annotation( Placement(visible = true, transformation(origin = {32, 10}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Product product annotation( Placement(visible = true, transformation(origin = {44, -14}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add2(k2 = -1) annotation( Placement(visible = true, transformation(origin = {2, -18}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Cos cos1 annotation( Placement(visible = true, transformation(origin = {22, -18}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression1(y = 4 * Pi / 3) annotation( Placement(visible = true, transformation(origin = {-19, -22}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Math.Gain gain2(k = 2 / 3) annotation( Placement(visible = true, transformation(origin = {-63, -9}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Interfaces.RealOutput d annotation( 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))); Modelica.Blocks.Sources.RealExpression realExpression(y = 2 * Pi / 3) annotation( Placement(visible = true, transformation(origin = {-29, 4}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Math.Cos cos3 annotation( Placement(visible = true, transformation(origin = {14, 6}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Interfaces.RealOutput theta annotation( 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))); Modelica.Blocks.Math.Product product3 annotation( Placement(visible = true, transformation(origin = {44, -84}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Product product5 annotation( Placement(visible = true, transformation(origin = {38, -36}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Gain gain3(k = -2 / 3) annotation( Placement(visible = true, transformation(origin = {-63, -53}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Add add3(k2 = -1) annotation( Placement(visible = true, transformation(origin = {2, -88}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression2(y = 2 * Pi / 3) annotation( Placement(visible = true, transformation(origin = {-29, -66}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Math.Sin sin1 annotation( Placement(visible = true, transformation(origin = {15, -65}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Gain gain4(k = -2 / 3) annotation( Placement(visible = true, transformation(origin = {-64, -30}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Gain gain5(k = -2 / 3) annotation( Placement(visible = true, transformation(origin = {-63, -79}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.MultiSum multiSum1(nu = 3) annotation( Placement(visible = true, transformation(origin = {64, -42}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealOutput q annotation( 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))); Modelica.Blocks.Math.Sin sin2 annotation( Placement(visible = true, transformation(origin = {9, -43}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression3(y = 4 * Pi / 3) annotation( Placement(visible = true, transformation(origin = {-19, -92}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Math.Sin sin3 annotation( Placement(visible = true, transformation(origin = {23, -89}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Add add4(k2 = -1) annotation( Placement(visible = true, transformation(origin = {-8, -64}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Product product4 annotation( Placement(visible = true, transformation(origin = {32, -60}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); equation connect(a, voltageSensor_a.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 44}, {-86, 44}}, color = {0, 0, 255})); connect(b, voltageSensor_b.p) annotation( Line(points = {{-102, 0}, {-94, 0}, {-94, 16}, {-88, 16}}, color = {0, 0, 255})); connect(c, voltageSensor_c.p) annotation( Line(points = {{-102, -60}, {-94, -60}, {-94, -14}, {-88, -14}}, color = {0, 0, 255})); connect(voltageSensor_a.n, ground.p) annotation( Line(points = {{-86, 56}, {-86, 56}}, color = {0, 0, 255})); connect(voltageSensor_b.n, ground.p) annotation( Line(points = {{-88, 28}, {-88, 42}, {-86, 42}, {-86, 56}}, color = {0, 0, 255})); connect(voltageSensor_c.n, ground.p) annotation( Line(points = {{-88, -2}, {-88, 27}, {-86, 27}, {-86, 56}}, color = {0, 0, 255})); connect(Norm_U_ref_alpha.u, abc2AlphaBeta.alpha) annotation( Line(points = {{-35, 83}, {-42, 83}, {-42, 84}}, color = {0, 0, 127})); connect(Norm_U_ref_alpha.y, alphaSin.u1) annotation( Line(points = {{-28, 83}, {-18.5, 83}, {-18.5, 85}, {-9, 85}}, color = {0, 0, 127})); connect(Norm_U_ref_beta.y, betaCos.u1) annotation( Line(points = {{-28, 69}, {-19.5, 69}, {-19.5, 71}, {-11, 71}}, color = {0, 0, 127})); connect(sin.y, alphaSin.u2) annotation( Line(points = {{-12, 56}, {-20, 56}, {-20, 81}, {-9, 81}}, color = {0, 0, 127})); connect(cos.y, betaCos.u2) annotation( Line(points = {{-12, 46}, {-16, 46}, {-16, 67}, {-11, 67}}, color = {0, 0, 127})); connect(add.u1, alphaSin.y) annotation( Line(points = {{9, 80}, {-0.25, 80}, {-0.25, 83}, {-2, 83}}, color = {0, 0, 127})); connect(betaCos.y, add.u2) annotation( Line(points = {{-4, 69}, {-1, 69}, {-1, 76}, {9, 76}}, color = {0, 0, 127})); connect(pi.u, add.y) annotation( Line(points = {{21, 78}, {18, 78}}, color = {0, 0, 127})); connect(deg2rad.u, f2theta.y) annotation( Line(points = {{75, 76}, {70, 76}}, color = {0, 0, 127})); connect(cos.u, deg2rad.y) annotation( Line(points = {{-3, 46}, {92, 46}, {92, 76}, {84, 76}}, color = {0, 0, 127})); connect(gain.y, product2.u1) annotation( Line(points = {{-57, 40}, {-13, 40}, {-13, 38}, {31, 38}}, color = {0, 0, 127})); connect(cos3.y, product1.u2) annotation( Line(points = {{21, 6}, {25, 6}}, color = {0, 0, 127})); connect(cos1.y, product.u2) annotation( Line(points = {{29, -18}, {37, -18}}, color = {0, 0, 127})); connect(gain1.y, product1.u1) annotation( Line(points = {{-55, 17}, {25, 17}, {25, 14}}, color = {0, 0, 127})); connect(realExpression.y, add1.u2) annotation( Line(points = {{-21, 4}, {-13, 4}}, color = {0, 0, 127})); connect(product2.y, multiSum.u[3]) annotation( Line(points = {{45, 34}, {55.5, 34}, {55.5, 28}, {54, 28}}, color = {0, 0, 127})); connect(product.y, multiSum.u[1]) annotation( Line(points = {{51, -14}, {54, -14}, {54, 28}}, color = {0, 0, 127})); connect(add1.y, cos3.u) annotation( Line(points = {{1, 8}, {4, 8}, {4, 6}, {7, 6}}, color = {0, 0, 127})); connect(add2.y, cos1.u) annotation( Line(points = {{9, -18}, {15, -18}}, color = {0, 0, 127})); connect(multiSum.y, d) annotation( Line(points = {{76, 28}, {95, 28}, {95, 0}, {110, 0}}, color = {0, 0, 127})); connect(product1.y, multiSum.u[2]) annotation( Line(points = {{39, 10}, {54, 10}, {54, 28}}, color = {0, 0, 127})); connect(cos2.y, product2.u2) annotation( Line(points = {{21, 30}, {31, 30}}, color = {0, 0, 127})); connect(realExpression1.y, add2.u2) annotation( Line(points = {{-11, -22}, {-5, -22}}, color = {0, 0, 127})); connect(gain2.y, product.u1) annotation( Line(points = {{-55, -9}, {37, -9}, {37, -10}}, color = {0, 0, 127})); connect(voltageSensor_a.v, gain.u) annotation( Line(points = {{-80, 50}, {-74, 50}, {-74, 40}, {-71, 40}}, color = {0, 0, 127})); connect(deg2rad.y, cos2.u) annotation( Line(points = {{84, 76}, {92, 76}, {92, 46}, {7, 46}, {7, 30}}, color = {0, 0, 127})); connect(deg2rad.y, add1.u1) annotation( Line(points = {{84, 76}, {92, 76}, {92, 46}, {6, 46}, {6, 24}, {-17, 24}, {-17, 12}, {-13, 12}}, color = {0, 0, 127})); connect(deg2rad.y, add2.u1) annotation( Line(points = {{84, 76}, {92, 76}, {92, 46}, {6, 46}, {6, 24}, {-16, 24}, {-16, -14}, {-5, -14}}, color = {0, 0, 127})); connect(sin.u, deg2rad.y) annotation( Line(points = {{-4, 56}, {0, 56}, {0, 46}, {92, 46}, {92, 76}, {84, 76}}, color = {0, 0, 127})); connect(voltageSensor_c.v, gain2.u) annotation( Line(points = {{-82, -8}, {-71, -8}, {-71, -9}}, color = {0, 0, 127})); connect(realExpression2.y, add4.u2) annotation( Line(points = {{-21, -66}, {-18, -66}, {-18, -68}, {-15, -68}}, color = {0, 0, 127})); connect(gain4.y, product5.u1) annotation( Line(points = {{-57, -30}, {-13, -30}, {-13, -32}, {31, -32}}, color = {0, 0, 127})); connect(gain3.y, product4.u1) annotation( Line(points = {{-55, -53}, {25, -53}, {25, -56}}, color = {0, 0, 127})); connect(sin1.y, product4.u2) annotation( Line(points = {{23, -65}, {23, -60.5}, {25, -60.5}, {25, -64}}, color = {0, 0, 127})); connect(realExpression3.y, add3.u2) annotation( Line(points = {{-11, -92}, {-5, -92}}, color = {0, 0, 127})); connect(sin2.y, product5.u2) annotation( Line(points = {{17, -43}, {28, -43}, {28, -40}, {31, -40}}, color = {0, 0, 127})); connect(product3.y, multiSum1.u[1]) annotation( Line(points = {{51, -84}, {54, -84}, {54, -42}}, color = {0, 0, 127})); connect(gain5.y, product3.u1) annotation( Line(points = {{-55, -79}, {37, -79}, {37, -80}}, color = {0, 0, 127})); connect(product5.y, multiSum1.u[3]) annotation( Line(points = {{45, -36}, {75.5, -36}, {75.5, -42}, {54, -42}}, color = {0, 0, 127})); connect(add3.y, sin3.u) annotation( Line(points = {{9, -88}, {26, -88}, {26, -89}, {15, -89}}, color = {0, 0, 127})); connect(sin1.u, add4.y) annotation( Line(points = {{7, -65}, {16, -65}, {16, -64}, {-1, -64}}, color = {0, 0, 127})); connect(product4.y, multiSum1.u[2]) annotation( Line(points = {{39, -60}, {54, -60}, {54, -42}}, color = {0, 0, 127})); connect(sin3.y, product3.u2) annotation( Line(points = {{31, -89}, {31, -88.5}, {37, -88.5}, {37, -88}}, color = {0, 0, 127})); connect(multiSum1.y, q) annotation( Line(points = {{76, -42}, {93, -42}, {93, -60}, {110, -60}}, color = {0, 0, 127})); connect(abc2AlphaBeta.beta, Norm_U_ref_beta.u) annotation( Line(points = {{-42, 74}, {-38, 74}, {-38, 68}, {-34, 68}, {-34, 70}}, color = {0, 0, 127})); connect(deg2rad.y, sin2.u) annotation( Line(points = {{84, 76}, {92, 76}, {92, 46}, {6, 46}, {6, 24}, {-44, 24}, {-44, -44}, {0, -44}, {0, -42}}, color = {0, 0, 127})); connect(voltageSensor_a.v, gain4.u) annotation( Line(points = {{-80, 50}, {-74, 50}, {-74, -30}, {-72, -30}, {-72, -30}}, color = {0, 0, 127})); connect(voltageSensor_b.v, gain3.u) annotation( Line(points = {{-81, 22}, {-76, 22}, {-76, -54}, {-72, -54}, {-72, -52}}, color = {0, 0, 127})); connect(add4.u1, deg2rad.y) annotation( Line(points = {{-16, -60}, {-44, -60}, {-44, 24}, {6, 24}, {6, 46}, {92, 46}, {92, 76}, {84, 76}, {84, 76}}, color = {0, 0, 127})); connect(add3.u1, deg2rad.y) annotation( Line(points = {{-6, -84}, {-44, -84}, {-44, 24}, {6, 24}, {6, 46}, {92, 46}, {92, 76}, {84, 76}, {84, 76}}, color = {0, 0, 127})); connect(gain5.u, voltageSensor_c.v) annotation( Line(points = {{-72, -78}, {-80, -78}, {-80, -8}, {-82, -8}, {-82, -8}}, color = {0, 0, 127})); connect(voltageSensor_b.v, gain1.u) annotation( Line(points = {{-82, 22}, {-76, 22}, {-76, 16}, {-72, 16}, {-72, 18}}, color = {0, 0, 127})); connect(voltageSensor_c.v, abc2AlphaBeta.c) annotation( Line(points = {{-82, -8}, {-78, -8}, {-78, 76}, {-62, 76}}, color = {0, 0, 127})); connect(voltageSensor_b.v, abc2AlphaBeta.b) annotation( Line(points = {{-82, 22}, {-76, 22}, {-76, 78}, {-62, 78}, {-62, 80}}, color = {0, 0, 127})); connect(abc2AlphaBeta.a, voltageSensor_a.v) annotation( Line(points = {{-62, 82}, {-80, 82}, {-80, 50}, {-80, 50}}, color = {0, 0, 127})); connect(deg2rad.y, theta) annotation( Line(points = {{84, 76}, {92, 76}, {92, 60}, {110, 60}, {110, 60}}, color = {0, 0, 127})); connect(f_nom.y, add_freq_nom_delta_f.u2) annotation( Line(points = {{34, 58}, {37.5, 58}, {37.5, 74}, {45, 74}}, color = {0, 0, 127})); connect(pi.y, add_freq_nom_delta_f.u1) annotation( Line(points = {{34, 78}, {44, 78}, {44, 78}, {46, 78}}, color = {0, 0, 127})); connect(f2theta.u, add_freq_nom_delta_f.y) annotation( Line(points = {{62, 76}, {54, 76}, {54, 76}, {54, 76}}, color = {0, 0, 127})); end PLL_DQ; ================================================ FILE: omg_grid/PLLs/package.mo ================================================ within omg_grid; package PLLs extends Modelica.Icons.SensorsPackage; end PLLs; ================================================ FILE: omg_grid/PLLs/package.order ================================================ PLL PLL_DQ ================================================ FILE: omg_grid/Transformations/ABC2AlphaBeta.mo ================================================ within omg_grid.Transformations; model ABC2AlphaBeta Modelica.Blocks.Interfaces.RealInput a annotation( 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))); Modelica.Blocks.Interfaces.RealInput b annotation( 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))); Modelica.Blocks.Interfaces.RealInput c annotation( 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))); Modelica.Blocks.Math.Gain gain(k = 2 / 3) annotation( Placement(visible = true, transformation(origin = {-40, 78}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Gain gain1(k = -1 / 3) annotation( Placement(visible = true, transformation(origin = {-39, 55}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Gain gain2(k = -1 / 3) annotation( Placement(visible = true, transformation(origin = {-39, 29}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.MultiSum multiSum(nu = 3) annotation( Placement(visible = true, transformation(origin = {58, 66}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Gain gain3(k = -1 / sqrt(3)) annotation( Placement(visible = true, transformation(origin = {-32, -18}, extent = {{-14, -14}, {14, 14}}, rotation = 0))); Modelica.Blocks.Math.Gain gain4(k = 1 / sqrt(3)) annotation( Placement(visible = true, transformation(origin = {-32, -64}, extent = {{-14, -14}, {14, 14}}, rotation = 0))); Modelica.Blocks.Math.MultiSum multiSum1(nu = 2) annotation( Placement(visible = true, transformation(origin = {48, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealOutput alpha annotation( 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))); Modelica.Blocks.Interfaces.RealOutput beta annotation( 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))); equation connect(a, gain.u) annotation( Line(points = {{-104, 40}, {-77, 40}, {-77, 78}, {-48, 78}}, color = {0, 0, 127})); connect(gain1.u, b) annotation( Line(points = {{-48, 56}, {-71, 56}, {-71, 12}, {-104, 12}}, color = {0, 0, 127})); connect(c, gain2.u) annotation( Line(points = {{-104, -18}, {-62, -18}, {-62, 30}, {-48, 30}}, color = {0, 0, 127})); connect(gain.y, multiSum.u[1]) annotation( Line(points = {{-34, 78}, {48, 78}, {48, 66}}, color = {0, 0, 127})); connect(gain1.y, multiSum.u[2]) annotation( Line(points = {{-32, 56}, {48, 56}, {48, 66}, {48, 66}}, color = {0, 0, 127})); connect(gain2.y, multiSum.u[3]) annotation( Line(points = {{-32, 30}, {48, 30}, {48, 66}, {48, 66}}, color = {0, 0, 127})); connect(gain4.u, b) annotation( Line(points = {{-48, -64}, {-73, -64}, {-73, -62}, {-72, -62}, {-72, 12}, {-104, 12}}, color = {0, 0, 127})); connect(gain3.u, c) annotation( Line(points = {{-48, -18}, {-96, -18}, {-96, -18}, {-104, -18}}, color = {0, 0, 127})); connect(gain3.y, multiSum1.u[1]) annotation( Line(points = {{-16, -18}, {38, -18}, {38, -34}, {38, -34}}, color = {0, 0, 127})); connect(gain4.y, multiSum1.u[2]) annotation( Line(points = {{-16, -64}, {38, -64}, {38, -34}, {38, -34}}, color = {0, 0, 127})); connect(multiSum.y, alpha) annotation( Line(points = {{70, 66}, {96, 66}, {96, 64}, {102, 64}}, color = {0, 0, 127})); connect(multiSum1.y, beta) annotation( Line(points = {{60, -34}, {102, -34}}, color = {0, 0, 127})); end ABC2AlphaBeta; ================================================ FILE: omg_grid/Transformations/ABC2DQ_Currents.mo ================================================ within omg_grid.Transformations; model ABC2DQ_Currents Real Pi = 3.14159265; Modelica.Electrical.Analog.Interfaces.Pin a annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin b annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin c annotation( 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))); Modelica.Blocks.Interfaces.RealOutput d annotation( 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))); Modelica.Blocks.Interfaces.RealOutput q annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Blocks.Math.Gain gain(k = 2 / 3) annotation( Placement(visible = true, transformation(origin = {-62, 60}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Gain gain1(k = 2 / 3) annotation( Placement(visible = true, transformation(origin = {-61, 37}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Gain gain2(k = 2 / 3) annotation( Placement(visible = true, transformation(origin = {-61, 11}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression1(y = 4 * Pi / 3) annotation( Placement(visible = true, transformation(origin = {-17, -2}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression(y = 2 * Pi / 3) annotation( Placement(visible = true, transformation(origin = {-27, 24}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Math.Add add1(k2 = -1) annotation( Placement(visible = true, transformation(origin = {-4, 28}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add2(k2 = -1) annotation( Placement(visible = true, transformation(origin = {4, 2}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Cos cos2 annotation( Placement(visible = true, transformation(origin = {16, 50}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Product product annotation( Placement(visible = true, transformation(origin = {46, 6}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Cos cos1 annotation( Placement(visible = true, transformation(origin = {24, 2}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Cos cos3 annotation( Placement(visible = true, transformation(origin = {16, 26}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Product product2 annotation( Placement(visible = true, transformation(origin = {40, 54}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Product product1 annotation( Placement(visible = true, transformation(origin = {34, 30}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.MultiSum multiSum(nu = 3) annotation( Placement(visible = true, transformation(origin = {74, 54}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.MultiSum multiSum1(nu = 3) annotation( Placement(visible = true, transformation(origin = {66, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Product product5 annotation( Placement(visible = true, transformation(origin = {40, -24}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add4(k2 = -1) annotation( Placement(visible = true, transformation(origin = {-6, -52}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add3(k2 = -1) annotation( Placement(visible = true, transformation(origin = {4, -76}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression2(y = 2 * Pi / 3) annotation( Placement(visible = true, transformation(origin = {-27, -54}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression3(y = 4 * Pi / 3) annotation( Placement(visible = true, transformation(origin = {-17, -80}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Math.Product product3 annotation( Placement(visible = true, transformation(origin = {46, -72}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Sin sin3 annotation( Placement(visible = true, transformation(origin = {25, -77}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Gain gain5(k = -2 / 3) annotation( Placement(visible = true, transformation(origin = {-61, -67}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Product product4 annotation( Placement(visible = true, transformation(origin = {34, -48}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Gain gain3(k = -2 / 3) annotation( Placement(visible = true, transformation(origin = {-61, -41}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Sin sin1 annotation( Placement(visible = true, transformation(origin = {17, -53}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Gain gain4(k = -2 / 3) annotation( Placement(visible = true, transformation(origin = {-62, -18}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Sin sin2 annotation( Placement(visible = true, transformation(origin = {11, -31}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput theta annotation( 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))); Modelica.Electrical.Analog.Sensors.CurrentSensor currentSensor3 annotation( Placement(visible = true, transformation(origin = {-90, -50}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); Modelica.Electrical.Analog.Sensors.CurrentSensor currentSensor1 annotation( Placement(visible = true, transformation(origin = {-88, 70}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); Modelica.Electrical.Analog.Sensors.CurrentSensor currentSensor2 annotation( Placement(visible = true, transformation(origin = {-90, 10}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); equation connect(realExpression.y, add1.u2) annotation( Line(points = {{-19, 24}, {-11, 24}}, color = {0, 0, 127})); connect(realExpression1.y, add2.u2) annotation( Line(points = {{-9, -2}, {-3, -2}}, color = {0, 0, 127})); connect(gain2.y, product.u1) annotation( Line(points = {{-53, 11}, {39, 11}, {39, 10}}, color = {0, 0, 127})); connect(add2.y, cos1.u) annotation( Line(points = {{11, 2}, {17, 2}}, color = {0, 0, 127})); connect(cos1.y, product.u2) annotation( Line(points = {{31, 2}, {39, 2}}, color = {0, 0, 127})); connect(add1.y, cos3.u) annotation( Line(points = {{3, 28}, {4, 28}, {4, 26}, {9, 26}}, color = {0, 0, 127})); connect(cos2.y, product2.u2) annotation( Line(points = {{23, 50}, {33, 50}}, color = {0, 0, 127})); connect(gain.y, product2.u1) annotation( Line(points = {{-55, 60}, {-11, 60}, {-11, 58}, {33, 58}}, color = {0, 0, 127})); connect(gain1.y, product1.u1) annotation( Line(points = {{-53, 37}, {27, 37}, {27, 34}}, color = {0, 0, 127})); connect(cos3.y, product1.u2) annotation( Line(points = {{23, 26}, {27, 26}}, color = {0, 0, 127})); connect(multiSum.y, d) annotation( Line(points = {{86, 54}, {86, -94}, {-40, -94}, {-40, -110}}, color = {0, 0, 127})); connect(product1.y, multiSum.u[1]) annotation( Line(points = {{41, 30}, {55.5, 30}, {55.5, 54}, {64, 54}}, color = {0, 0, 127})); connect(product.y, multiSum.u[2]) annotation( Line(points = {{53, 6}, {64, 6}, {64, 54}}, color = {0, 0, 127})); connect(product2.y, multiSum.u[3]) annotation( Line(points = {{47, 54}, {64, 54}}, color = {0, 0, 127})); connect(sin2.y, product5.u2) annotation( Line(points = {{19, -31}, {30, -31}, {30, -28}, {33, -28}}, color = {0, 0, 127})); connect(product4.y, multiSum1.u[2]) annotation( Line(points = {{41, -48}, {56, -48}, {56, -30}}, color = {0, 0, 127})); connect(gain3.y, product4.u1) annotation( Line(points = {{-53, -41}, {27, -41}, {27, -44}}, color = {0, 0, 127})); connect(realExpression3.y, add3.u2) annotation( Line(points = {{-9, -80}, {-3, -80}}, color = {0, 0, 127})); connect(product5.y, multiSum1.u[3]) annotation( Line(points = {{47, -24}, {62.25, -24}, {62.25, -28}, {59.125, -28}, {59.125, -30}, {56, -30}}, color = {0, 0, 127})); connect(add3.y, sin3.u) annotation( Line(points = {{11, -76}, {28, -76}, {28, -77}, {17, -77}}, color = {0, 0, 127})); connect(sin1.u, add4.y) annotation( Line(points = {{9, -53}, {18, -53}, {18, -52}, {1, -52}}, color = {0, 0, 127})); connect(sin3.y, product3.u2) annotation( Line(points = {{33, -77}, {33, -76.5}, {39, -76.5}, {39, -76}}, color = {0, 0, 127})); connect(sin1.y, product4.u2) annotation( Line(points = {{25, -53}, {25, -50.5}, {27, -50.5}, {27, -52}}, color = {0, 0, 127})); connect(gain5.y, product3.u1) annotation( Line(points = {{-53, -67}, {39, -67}, {39, -68}}, color = {0, 0, 127})); connect(realExpression2.y, add4.u2) annotation( Line(points = {{-19, -54}, {-16, -54}, {-16, -56}, {-13, -56}}, color = {0, 0, 127})); connect(product3.y, multiSum1.u[1]) annotation( Line(points = {{53, -72}, {56, -72}, {56, -30}}, color = {0, 0, 127})); connect(gain4.y, product5.u1) annotation( Line(points = {{-55, -18}, {-35, -18}, {-35, -20}, {33, -20}}, color = {0, 0, 127})); connect(multiSum1.y, q) annotation( Line(points = {{78, -30}, {84, -30}, {84, -92}, {40, -92}, {40, -110}, {40, -110}}, color = {0, 0, 127})); connect(theta, cos2.u) annotation( Line(points = {{0, 112}, {0, 112}, {0, 50}, {8, 50}, {8, 50}}, color = {0, 0, 127})); connect(theta, add1.u1) annotation( Line(points = {{0, 112}, {0, 112}, {0, 86}, {-44, 86}, {-44, 32}, {-12, 32}, {-12, 32}}, color = {0, 0, 127})); connect(theta, add2.u1) annotation( Line(points = {{0, 112}, {0, 112}, {0, 86}, {-44, 86}, {-44, 6}, {-4, 6}, {-4, 6}}, color = {0, 0, 127})); connect(theta, sin2.u) annotation( Line(points = {{0, 112}, {0, 112}, {0, 86}, {-44, 86}, {-44, -32}, {2, -32}, {2, -30}}, color = {0, 0, 127})); connect(theta, add4.u1) annotation( Line(points = {{0, 112}, {0, 112}, {0, 86}, {-44, 86}, {-44, -48}, {-14, -48}, {-14, -48}}, color = {0, 0, 127})); connect(theta, add3.u1) annotation( Line(points = {{0, 112}, {0, 112}, {0, 86}, {-44, 86}, {-44, -72}, {-4, -72}, {-4, -72}}, color = {0, 0, 127})); connect(c, currentSensor3.p) annotation( Line(points = {{-102, -60}, {-90, -60}, {-90, -56}, {-90, -56}}, color = {0, 0, 255})); connect(b, currentSensor2.p) annotation( Line(points = {{-102, 0}, {-90, 0}, {-90, 4}, {-90, 4}, {-90, 4}}, color = {0, 0, 255})); connect(a, currentSensor1.p) annotation( Line(points = {{-100, 60}, {-88, 60}, {-88, 64}, {-88, 64}, {-88, 64}}, color = {0, 0, 255})); connect(currentSensor1.n, pin3) annotation( Line(points = {{-88, 76}, {-88, 76}, {-88, 84}, {94, 84}, {94, 60}, {100, 60}}, color = {0, 0, 255})); connect(currentSensor2.n, pin2) annotation( Line(points = {{-90, 16}, {-86, 16}, {-86, 82}, {92, 82}, {92, 0}, {100, 0}}, color = {0, 0, 255})); connect(currentSensor3.n, pin1) annotation( Line(points = {{-90, -44}, {-84, -44}, {-84, 80}, {90, 80}, {90, -60}, {100, -60}}, color = {0, 0, 255})); connect(currentSensor1.i, gain.u) annotation( Line(points = {{-82, 70}, {-72, 70}, {-72, 60}, {-70, 60}, {-70, 60}}, color = {0, 0, 127})); connect(gain4.u, currentSensor1.i) annotation( Line(points = {{-70, -18}, {-72, -18}, {-72, 70}, {-82, 70}, {-82, 70}}, color = {0, 0, 127})); connect(currentSensor2.i, gain1.u) annotation( Line(points = {{-84, 10}, {-80, 10}, {-80, 36}, {-70, 36}, {-70, 38}}, color = {0, 0, 127})); connect(currentSensor2.i, gain3.u) annotation( Line(points = {{-84, 10}, {-80, 10}, {-80, -42}, {-70, -42}, {-70, -40}}, color = {0, 0, 127})); connect(currentSensor3.i, gain2.u) annotation( Line(points = {{-84, -50}, {-76, -50}, {-76, 10}, {-70, 10}, {-70, 12}}, color = {0, 0, 127})); connect(currentSensor3.i, gain5.u) annotation( Line(points = {{-84, -50}, {-76, -50}, {-76, -68}, {-70, -68}, {-70, -66}}, color = {0, 0, 127})); end ABC2DQ_Currents; ================================================ FILE: omg_grid/Transformations/DQ2ABC.mo ================================================ within omg_grid.Transformations; model DQ2ABC Real pi = 2 * Modelica.Math.asin(1.0); Modelica.Blocks.Interfaces.RealInput d annotation( 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))); Modelica.Blocks.Interfaces.RealInput q annotation( 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))); Modelica.Blocks.Interfaces.RealOutput b annotation( 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))); Modelica.Blocks.Sources.RealExpression realExpression1(y = 2 * pi / 3) annotation( Placement(visible = true, transformation(origin = {-7, 6}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Math.Add add1(k2 = -1) annotation( Placement(visible = true, transformation(origin = {14, 10}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Cos cos1 annotation( Placement(visible = true, transformation(origin = {34, 10}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Cos cos2 annotation( Placement(visible = true, transformation(origin = {26, 68}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Product product annotation( Placement(visible = true, transformation(origin = {56, 14}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Product product1 annotation( Placement(visible = true, transformation(origin = {44, 48}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Product product2 annotation( Placement(visible = true, transformation(origin = {50, 72}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Product product3 annotation( Placement(visible = true, transformation(origin = {58, -58}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Product product4 annotation( Placement(visible = true, transformation(origin = {46, -34}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add2(k2 = -1) annotation( Placement(visible = true, transformation(origin = {16, -62}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add3(k2 = -1) annotation( Placement(visible = true, transformation(origin = {6, -38}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression2(y = 4 * pi / 3) annotation( Placement(visible = true, transformation(origin = {-17, -40}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Interfaces.RealOutput c annotation( 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))); Modelica.Blocks.Sources.RealExpression realExpression3(y = 4 * pi / 3) annotation( Placement(visible = true, transformation(origin = {-5, -66}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Math.Sin sin annotation( Placement(visible = true, transformation(origin = {37, -63}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Sin sin5 annotation( Placement(visible = true, transformation(origin = {17, 43}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Add add annotation( Placement(visible = true, transformation(origin = {74, 58}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealOutput a annotation( 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))); Modelica.Blocks.Math.Product product6 annotation( Placement(visible = true, transformation(origin = {54, -8}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add4(k2 = -1) annotation( Placement(visible = true, transformation(origin = {14, -12}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Sources.RealExpression realExpression(y = 2 * pi / 3) annotation( Placement(visible = true, transformation(origin = {-11, -16}, extent = {{-7, -8}, {7, 8}}, rotation = 0))); Modelica.Blocks.Math.Sin sin8 annotation( Placement(visible = true, transformation(origin = {35, -13}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Add add5 annotation( Placement(visible = true, transformation(origin = {80, 2}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Cos cos annotation( Placement(visible = true, transformation(origin = {24, -38}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add6 annotation( Placement(visible = true, transformation(origin = {82, -52}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput theta annotation( 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))); equation connect(realExpression1.y, add1.u2) annotation( Line(points = {{1, 6}, {7, 6}}, color = {0, 0, 127})); connect(add1.y, cos1.u) annotation( Line(points = {{21, 10}, {27, 10}}, color = {0, 0, 127})); connect(cos2.y, product2.u2) annotation( Line(points = {{32, 68}, {42, 68}, {42, 68}, {42, 68}}, color = {0, 0, 127})); connect(cos1.y, product.u2) annotation( Line(points = {{41, 10}, {49, 10}}, color = {0, 0, 127})); connect(realExpression2.y, add3.u2) annotation( Line(points = {{-9, -40}, {-4, -40}, {-4, -42}, {-1, -42}}, color = {0, 0, 127})); connect(realExpression3.y, add2.u2) annotation( Line(points = {{3, -66}, {9, -66}}, color = {0, 0, 127})); connect(add2.y, sin.u) annotation( Line(points = {{22, -62}, {26, -62}, {26, -63}, {29, -63}}, color = {0, 0, 127})); connect(sin.y, product3.u2) annotation( Line(points = {{45, -63}, {45, -62}, {50, -62}}, color = {0, 0, 127})); connect(d, product2.u1) annotation( Line(points = {{-104, 40}, {-60, 40}, {-60, 76}, {42, 76}, {42, 76}}, color = {0, 0, 127})); connect(sin5.y, product1.u2) annotation( Line(points = {{24, 44}, {36, 44}, {36, 44}, {36, 44}}, color = {0, 0, 127})); connect(q, product1.u1) annotation( Line(points = {{-104, -40}, {-50, -40}, {-50, 52}, {36, 52}, {36, 52}}, color = {0, 0, 127})); connect(product1.y, add.u2) annotation( Line(points = {{50, 48}, {56, 48}, {56, 52}, {62, 52}, {62, 52}}, color = {0, 0, 127})); connect(add.y, a) annotation( Line(points = {{86, 58}, {92, 58}, {92, 60}, {106, 60}}, color = {0, 0, 127})); connect(product2.y, add.u1) annotation( Line(points = {{56, 72}, {58, 72}, {58, 64}, {62, 64}, {62, 64}, {62, 64}}, color = {0, 0, 127})); connect(sin8.y, product6.u2) annotation( Line(points = {{43, -13}, {43, -12.5}, {47, -12.5}, {47, -12}}, color = {0, 0, 127})); connect(realExpression.y, add4.u2) annotation( Line(points = {{-3, -16}, {7, -16}}, color = {0, 0, 127})); connect(sin8.u, add4.y) annotation( Line(points = {{27, -13}, {26, -13}, {26, -12}, {21, -12}}, color = {0, 0, 127})); connect(d, product.u1) annotation( Line(points = {{-104, 40}, {-60, 40}, {-60, 18}, {49, 18}}, color = {0, 0, 127})); connect(q, product6.u1) annotation( Line(points = {{-104, -40}, {-50, -40}, {-50, -2}, {47, -2}, {47, -4}}, color = {0, 0, 127})); connect(product6.y, add5.u2) annotation( Line(points = {{61, -8}, {64, -8}, {64, -4}, {68, -4}}, color = {0, 0, 127})); connect(product.y, add5.u1) annotation( Line(points = {{63, 14}, {64, 14}, {64, 8}, {68, 8}}, color = {0, 0, 127})); connect(add5.y, b) annotation( Line(points = {{92, 2}, {94, 2}, {94, 0}, {106, 0}, {106, 0}}, color = {0, 0, 127})); connect(cos.y, product4.u2) annotation( Line(points = {{30, -38}, {38, -38}, {38, -38}, {38, -38}}, color = {0, 0, 127})); connect(d, product4.u1) annotation( Line(points = {{-104, 40}, {-60, 40}, {-60, -30}, {38, -30}, {38, -30}}, color = {0, 0, 127})); connect(add3.y, cos.u) annotation( Line(points = {{12, -38}, {16, -38}, {16, -38}, {16, -38}}, color = {0, 0, 127})); connect(q, product3.u1) annotation( Line(points = {{-104, -40}, {-50, -40}, {-50, -48}, {48, -48}, {48, -54}, {50, -54}, {50, -54}}, color = {0, 0, 127})); connect(product3.y, add6.u2) annotation( Line(points = {{64, -58}, {68, -58}, {68, -58}, {70, -58}}, color = {0, 0, 127})); connect(product4.y, add6.u1) annotation( Line(points = {{52, -34}, {58, -34}, {58, -46}, {68, -46}, {68, -46}, {70, -46}}, color = {0, 0, 127})); connect(add6.y, c) annotation( Line(points = {{94, -52}, {94, -52}, {94, -60}, {106, -60}, {106, -60}}, color = {0, 0, 127})); connect(theta, add4.u1) annotation( Line(points = {{-34, 116}, {-34, -8}, {7, -8}}, color = {0, 0, 127})); connect(theta, sin5.u) annotation( Line(points = {{-34, 116}, {-34, 116}, {-34, 42}, {8, 42}, {8, 44}}, color = {0, 0, 127})); connect(theta, add3.u1) annotation( Line(points = {{-34, 116}, {-34, -32}, {-2.25, -32}, {-2.25, -34}, {-1, -34}}, color = {0, 0, 127})); connect(theta, add2.u1) annotation( Line(points = {{-34, 116}, {-34, 116}, {-34, -58}, {8, -58}, {8, -58}}, color = {0, 0, 127})); connect(theta, cos2.u) annotation( Line(points = {{-34, 116}, {-34, 68}, {18, 68}}, color = {0, 0, 127})); connect(theta, add1.u1) annotation( Line(points = {{-34, 116}, {-34, 14}, {7, 14}}, color = {0, 0, 127})); end DQ2ABC; ================================================ FILE: omg_grid/Transformations/package.mo ================================================ within omg_grid; package Transformations extends Modelica.Icons.Package; annotation (Icon(graphics={ Rectangle( extent={{-60,66},{-30,18}}, lineColor={95,95,95}, fillColor={175,175,175}, fillPattern=FillPattern.Solid), Rectangle( extent={{28,66},{58,18}}, lineColor={95,95,95}, fillColor={175,175,175}, fillPattern=FillPattern.Solid), Rectangle( extent={{-60,-18},{-30,-66}}, lineColor={95,95,95}, fillColor={175,175,175}, fillPattern=FillPattern.Solid), Rectangle( extent={{28,-18},{58,-66}}, lineColor={95,95,95}, fillColor={175,175,175}, fillPattern=FillPattern.Solid)})); end Transformations; ================================================ FILE: omg_grid/Transformations/package.order ================================================ ABC2AlphaBeta DQ2ABC ABC2DQ_Currents ================================================ FILE: omg_grid/UsersGuide/Contact.mo ================================================ within omg_grid.UsersGuide; model Contact "Contact" extends Modelica.Icons.Contact; annotation ( Documentation(info="
The development of this Modelica package is organized by
Henrik Bode
Chair of Power Electronics and Electrical Drives, Paderborn University
Warburger Straße 100
33098 Paderborn
Germany
email: bode@lea.uni-paderborn.de
 
List of contributors:
Daniel Weber
Stefan Heid
Jarren Lange
Oliver Wallscheid

")); end Contact; ================================================ FILE: omg_grid/UsersGuide/ModelicaLicense2.mo ================================================ within omg_grid.UsersGuide; class ModelicaLicense2 "Modelica License 2" extends Modelica.Icons.Information; annotation (Documentation(info=" The Modelica License 2

All files in this directory and in all subdirectories are released under the "Modelica License 2" (if not explicitly noted otherwise).

The Modelica License 2
How to Apply the Modelica License 2
Frequently Asked Questions


The Modelica License 2

Preamble. The goal of this license is that Modelica related model libraries, software, images, documents, data files etc. can be used freely in the original or a modified form, in open source and in commercial environments (as long as the license conditions below are fulfilled, in particular sections 2c) and 2d). The Original Work is provided free of charge and the use is completely at your own risk. Developers of free Modelica packages are encouraged to utilize this license for their work.

The Modelica License applies to any Original Work that contains the following licensing notice adjacent to the copyright notice(s) for this Original Work:

Licensed by <name of Licensor> under the Modelica License 2

1. Definitions.

  1. "License" is this Modelica License.
  2. "Original Work" is any work of authorship, including software, images, documents, data files, that contains the above licensing notice or that is packed together with a licensing notice referencing it.
  3. "Licensor" is the provider of the Original Work who has placed this licensing notice adjacent to the copyright notice(s) for the Original Work. The Original Work is either directly provided by the owner of the Original Work, or by a licensee of the owner.
  4. "Derivative Work" is any modification of the Original Work which represents, as a whole, an original work of authorship. For the matter of clarity and as examples:
    1. Derivative Work shall not include work that remains separable from the Original Work, as well as merely extracting a part of the Original Work without modifying it.
    2. Derivative Work shall not include (a) fixing of errors and/or (b) adding vendor specific Modelica annotations and/or (c) using a subset of the classes of a Modelica package, and/or (d) using a different representation, e.g., a binary representation.
    3. Derivative Work shall include classes that are copied from the Original Work where declarations, equations or the documentation are modified.
    4. Derivative Work shall include executables to simulate the models that are generated by a Modelica translator based on the Original Work (of a Modelica package).
  5. "Modified Work" is any modification of the Original Work with the following exceptions: (a) fixing of errors and/or (b) adding vendor specific Modelica annotations and/or (c) using a subset of the classes of a Modelica package, and/or (d) using a different representation, e.g., a binary representation.
  6. "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work.
  7. "You" means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License.
  8. "Modelica package" means any Modelica library that is defined with the "package <Name> ... end <Name>;" Modelica language element.

2. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:

  1. To reproduce the Original Work in copies, either alone or as part of a collection.

  2. To create Derivative Works according to Section 1d) of this License.

  3. To distribute or communicate to the public copies of the Original Work or a Derivative Work under this License. No fee, neither as a copyright-license fee, nor as a selling fee for the copy as such may be charged under this License. Furthermore, a verbatim copy of this License must be included in any copy of the Original Work or a Derivative Work under this License.
    For the matter of clarity, it is permitted A) to distribute or communicate such copies as part of a (possible commercial) collection where other parts are provided under different licenses and a license fee is charged for the other parts only and B) to charge for mere printing and shipping costs.

  4. To distribute or communicate to the public copies of a Derivative Work, alternatively to Section 2c), under any other license of your choice, especially also under a license for commercial/proprietary software, as long as You comply with Sections 3, 4 and 8 below.
    For the matter of clarity, no restrictions regarding fees, either as to a copyright-license fee or as to a selling fee for the copy as such apply.

  5. To perform the Original Work publicly.

  6. To display the Original Work publicly.

3. Acceptance. Any use of the Original Work or a Derivative Work, or any action according to either Section 2a) to 2f) above constitutes Your acceptance of this License.

4. Designation of Derivative Works and of Modified Works. The identifying designation of Derivative Work and of Modified Work must be different to the corresponding identifying designation of the Original Work. This means especially that the (root-level) name of a Modelica package under this license must be changed if the package is modified (besides fixing of errors, adding vendor specific Modelica annotations, using a subset of the classes of a Modelica package, or using another representation, e.g. a binary representation).

5. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned by the Licensor or licensed to the Licensor by the owners of the Original Work that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works under the conditions as given in Section 2. For the matter of clarity, the license regarding Derivative Works covers patent claims to the extent as they are embodied in the Original Work only.

6. Provision of Source Code. Licensor agrees to provide You with a copy of the Source Code of the Original Work but reserves the right to decide freely on the manner of how the Original Work is provided.
      For the matter of clarity, Licensor might provide only a binary representation of the Original Work. In that case, You may (a) either reproduce the Source Code from the binary representation if this is possible (e.g., by performing a copy of an encrypted Modelica package, if encryption allows the copy operation) or (b) request the Source Code from the Licensor who will provide it to You.

7. Exclusions from License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as otherwise expressly stated in this License and in particular in Sections 2 and 5, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property, and no patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims.
      No license is granted to the trademarks of Licensor even if such trademarks are included in the Original Work, except as expressly stated in this License. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.

8. Attribution Rights. You must retain in the Source Code of the Original Work and of any Derivative Works that You create, all author, copyright, patent, or trademark notices, as well as any descriptive text identified therein as an "Attribution Notice". The same applies to the licensing notice of this License in the Original Work. For the matter of clarity, "author notice" means the notice that identifies the original author(s).
      You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
      In case the Original Work or Derivative Work is not provided in Source Code, the Attribution Notices shall be appropriately displayed, e.g., in the documentation of the Derivative Work.

9. Disclaimer of Warranty.
The Original Work is provided under this License on an "as is" basis and without warranty, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. The entire risk as to the quality of the Original Work is with You. This disclaimer of warranty constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.

10. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor, the owner or a licensee of the Original Work be liable to anyone for any direct, indirect, general, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.

11. Termination. This License conditions your rights to undertake the activities listed in Section 2 and 5, including your right to create Derivative Works based upon the Original Work, and doing so without observing these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations. This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to observe the conditions of this license.

12. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor, any owners of the Original Work or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement through combinations of the Original Work under combination with other software or hardware.

13. Jurisdiction. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.

14. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.

15. Miscellaneous.

  1. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
  2. No verbal ancillary agreements have been made. Changes and additions to this License must appear in writing to be valid. This also applies to changing the clause pertaining to written form.
  3. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.

How to Apply the Modelica License 2

At the top level of your Modelica package and at every important subpackage, add the following notices in the info layer of the package:

Licensed by <Licensor> under the Modelica License 2
Copyright © <year1>-<year2>, <name of copyright holder(s)>.

This Modelica package is free software and the use is completely at your own risk; it can be redistributed and/or modified under the terms of the Modelica License 2. For license conditions (including the disclaimer of warranty) see Modelica.UsersGuide.ModelicaLicense2 or visit http://www.modelica.org/licenses/ModelicaLicense2.

Include a copy of the Modelica License 2 under <library>.UsersGuide.ModelicaLicense2 (use http://www.modelica.org/licenses/ModelicaLicense2.mo). Furthermore, add the list of authors and contributors under <library>.UsersGuide.Contributors or <library>.UsersGuide.Contact.

For example, sublibrary Modelica.Blocks of the Modelica Standard Library may have the following notices:

Licensed by Modelica Association under the Modelica License 2
Copyright © 1998-2008, Modelica Association.

This Modelica package is free software and the use is completely at your own risk; it can be redistributed and/or modified under the terms of the Modelica License 2. For license conditions (including the disclaimer of warranty) see Modelica.UsersGuide.ModelicaLicense2 or visit http://www.modelica.org/licenses/ModelicaLicense2.

For C-source code and documents, add similar notices in the corresponding file.

For images, add a "readme.txt" file to the directories where the images are stored and include a similar notice in this file.

In these cases, save a copy of the Modelica License 2 in one directory of the distribution, e.g., http://www.modelica.org/licenses/ModelicaLicense2.html in directory <library>/Resources/Documentation/ModelicaLicense2.html.


Frequently Asked Questions

This section contains questions/answer to users and/or distributors of Modelica packages and/or documents under Modelica License 2. Note, the answers to the questions below are not a legal interpretation of the Modelica License 2. In case of a conflict, the language of the license shall prevail.

Using or Distributing a Modelica Package under the Modelica License 2

What are the main differences to the previous version of the Modelica License?

  1. Modelica License 1 is unclear whether the licensed Modelica package can be distributed under a different license. Version 2 explicitly allows that "Derivative Work" can be distributed under any license of Your choice, see examples in Section 1d) as to what qualifies as Derivative Work (so, version 2 is clearer).

  2. If You modify a Modelica package under Modelica License 2 (besides fixing of errors, adding vendor specific Modelica annotations, using a subset of the classes of a Modelica package, or using another representation, e.g., a binary representation), you must rename the root-level name of the package for your distribution. In version 1 you could keep the name (so, version 2 is more restrictive). The reason of this restriction is to reduce the risk that Modelica packages are available that have identical names, but different functionality.

  3. Modelica License 1 states that "It is not allowed to charge a fee for the original version or a modified version of the software, besides a reasonable fee for distribution and support". Version 2 has a similar intention for all Original Work under Modelica License 2 (to remain free of charge and open source) but states this more clearly as "No fee, neither as a copyright-license fee, nor as a selling fee for the copy as such may be charged". Contrary to version 1, Modelica License 2 has no restrictions on fees for Derivative Work that is provided under a different license (so, version 2 is clearer and has fewer restrictions).

  4. Modelica License 2 introduces several useful provisions for the licensee (articles 5, 6, 12), and for the licensor (articles 7, 12, 13, 14) that have no counter part in version 1.

  5. Modelica License 2 can be applied to all type of work, including documents, images and data files, contrary to version 1 that was dedicated for software only (so, version 2 is more general).

Can I distribute a Modelica package (under Modelica License 2) as part of my commercial Modelica modeling and simulation environment?

Yes, according to Section 2c). However, you are not allowed to charge a fee for this part of your environment. Of course, you can charge for your part of the environment.

Can I distribute a Modelica package (under Modelica License 2) under a different license?

No. The license of an unmodified Modelica package cannot be changed according to Sections 2c) and 2d). This means that you cannot sell copies of it, any distribution has to be free of charge.

Can I distribute a Modelica package (under Modelica License 2) under a different license when I first encrypt the package?

No. Merely encrypting a package does not qualify for Derivative Work and therefore the encrypted package has to stay under Modelica License 2.

Can I distribute a Modelica package (under Modelica License 2) under a different license when I first add classes to the package?

No. The package itself remains unmodified, i.e., it is Original Work, and therefore the license for this part must remain under Modelica License 2. The newly added classes can be, however, under a different license.

Can I copy a class out of a Modelica package (under Modelica License 2) and include it unmodified in a Modelica package under a commercial/proprietary license?

No, according to article 2c). However, you can include model, block, function, package, record and connector classes in your Modelica package under Modelica License 2. This means that your Modelica package could be under a commercial/proprietary license, but one or more classes of it are under Modelica License 2.
Note, a "type" class (e.g., type Angle = Real(unit="rad")) can be copied and included unmodified under a commercial/proprietary license (for details, see the next question).

Can I copy a type class or part of a model, block, function, record, connector class, out of a Modelica package (under Modelica License 2) and include it modified or unmodified in a Modelica package under a commercial/proprietary license?

Yes, according to article 2d), since this will in the end usually qualify as Derivative Work. The reasoning is the following: A type class or part of another class (e.g., an equation, a declaration, part of a class description) cannot be utilized "by its own". In order to make this "usable", you have to add additional code in order that the class can be utilized. This is therefore usually Derivative Work and Derivative Work can be provided under a different license. Note, this only holds, if the additional code introduced is sufficient to qualify for Derivative Work. Merely, just copying a class and changing, say, one character in the documentation of this class would be no Derivative Work and therefore the copied code would have to stay under Modelica License 2.

Can I copy a class out of a Modelica package (under Modelica License 2) and include it in modified form in a commercial/proprietary Modelica package?

Yes. If the modification can be seen as a "Derivative Work", you can place it under your commercial/proprietary license. If the modification does not qualify as "Derivative Work" (e.g., bug fixes, vendor specific annotations), it must remain under Modelica License 2. This means that your Modelica package could be under a commercial/proprietary license, but one or more parts of it are under Modelica License 2.

Can I distribute a "save total model" under my commercial/proprietary license, even if classes under Modelica License 2 are included?

Your classes of the "save total model" can be distributed under your commercial/proprietary license, but the classes under Modelica License 2 must remain under Modelica License 2. This means you can distribute a "save total model", but some parts might be under Modelica License 2.

Can I distribute a Modelica package (under Modelica License 2) in encrypted form?

Yes. Note, if the encryption does not allow "copying" of classes (in to unencrypted Modelica source code), you have to send the Modelica source code of this package to your customer, if he/she wishes it, according to article 6.

Can I distribute an executable under my commercial/proprietary license, if the model from which the executable is generated uses models from a Modelica package under Modelica License 2?

Yes, according to article 2d), since this is seen as Derivative Work. The reasoning is the following: An executable allows the simulation of a concrete model, whereas models from a Modelica package (without pre-processing, translation, tool run-time library) are not able to be simulated without tool support. By the processing of the tool and by its run-time libraries, significant new functionality is added (a model can be simulated whereas previously it could not be simulated) and functionality available in the package is removed (e.g., to build up a new model by dragging components of the package is no longer possible with the executable).

Is my modification to a Modelica package (under Modelica License 2) a Derivative Work?

It is not possible to give a general answer to it. To be regarded as "an original work of authorship", a derivative work must be different enough from the original or must contain a substantial amount of new material. Making minor changes or additions of little substance to a preexisting work will not qualify the work as a new version for such purposes.

Using or Distributing a Modelica Document under the Modelica License 2

This section is devoted especially for the following applications:

  1. A Modelica tool extracts information out of a Modelica package and presents the result in form of a "manual" for this package in, e.g., html, doc, or pdf format.

  2. The Modelica language specification is a document defining the Modelica language. It will be licensed under Modelica License 2.

  3. Someone writes a book about the Modelica language and/or Modelica packages and uses information which is available in the Modelica language specification and/or the corresponding Modelica package.

Can I sell a manual that was basically derived by extracting information automatically from a Modelica package under Modelica License 2 (e.g., a "reference guide" of the Modelica Standard Library)?

Yes. Extracting information from a Modelica package, and providing it in a human readable, suitable format, like html, doc or pdf format, where the content is significantly modified (e.g. tables with interface information are constructed from the declarations of the public variables) qualifies as Derivative Work and there are no restrictions to charge a fee for Derivative Work under alternative 2d).

Can I copy a text passage out of a Modelica document (under Modelica License 2) and use it unmodified in my document (e.g. the Modelica syntax description in the Modelica Specification)?

Yes. In case you distribute your document, the copied parts are still under Modelica License 2 and you are not allowed to charge a license fee for this part. You can, of course, charge a fee for the rest of your document.

Can I copy a text passage out of a Modelica document (under Modelica License 2) and use it in modified form in my document?

Yes, the creation of Derivative Works is allowed. In case the content is significantly modified this qualifies as Derivative Work and there are no restrictions to charge a fee for Derivative Work under alternative 2d).

Can I sell a printed version of a Modelica document (under Modelica License 2), e.g., the Modelica Language Specification?

No, if you are not the copyright-holder, since article 2c) does not allow a selling fee for a (in this case physical) copy. However, mere printing and shipping costs may be recovered.

")); end ModelicaLicense2; ================================================ FILE: omg_grid/UsersGuide/package.mo ================================================ within omg_grid; package UsersGuide "User's guide" extends Modelica.Icons.Information; annotation (DocumentationClass=true, Documentation(info="

This library aims at providing models the simulation of Microgrids.

")); end UsersGuide; ================================================ FILE: omg_grid/UsersGuide/package.order ================================================ Contact ModelicaLicense2 ================================================ FILE: omg_grid/create_fmu.mos ================================================ OpenModelica.Scripting.loadFile("grid.mo"); getErrorString(); setCommandLineOptions("-d=newInst"); getErrorString(); setCommandLineOptions("-d=initialization"); getErrorString(); setCommandLineOptions("--simCodeTarget=Cpp"); getErrorString(); setCommandLineOptions("-d=-disableDirectionalDerivatives"); getErrorString(); OpenModelica.Scripting.translateModelFMU(grid.network, version="2.0", fmuType = "me"); getErrorString(); ================================================ FILE: omg_grid/grid.mo ================================================ package grid import SI = Modelica.SIunits; package filter model pi parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Capacitance C4 = 0.00001; parameter SI.Capacitance C5 = 0.00001; parameter SI.Capacitance C6 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Resistance R1 = 0.01; parameter SI.Resistance R2 = 0.01; parameter SI.Resistance R3 = 0.01; parameter SI.Resistance R4 = 0.01; parameter SI.Resistance R5 = 0.01; parameter SI.Resistance R6 = 0.01; parameter SI.Resistance R7 = 0.01; parameter SI.Resistance R8 = 0.01; parameter SI.Resistance R9 = 0.01; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-14, 28}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-14, 52}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-14, 78}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-70, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {-48, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation( Placement(visible = true, transformation(origin = {26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation( Placement(visible = true, transformation(origin = {46, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation( Placement(visible = true, transformation(origin = {64, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-68, -6}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {-48, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {-26, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor4(R = R4) annotation( Placement(visible = true, transformation(origin = {10, 28}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor5(R = R5) annotation( Placement(visible = true, transformation(origin = {10, 52}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor6(R = R6) annotation( Placement(visible = true, transformation(origin = {10, 78}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor7(R = R7) annotation( Placement(visible = true, transformation(origin = {26, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor8(R = R8) annotation( Placement(visible = true, transformation(origin = {46, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor9(R = R9) annotation( Placement(visible = true, transformation(origin = {64, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(inductor1.n, resistor4.p) annotation( Line(points = {{-4, 28}, {0, 28}, {0, 28}, {0, 28}}, color = {0, 0, 255})); connect(inductor2.n, resistor5.p) annotation( Line(points = {{-4, 52}, {-4, 52}, {-4, 52}, {0, 52}}, color = {0, 0, 255})); connect(inductor3.n, resistor6.p) annotation( Line(points = {{-4, 78}, {0, 78}, {0, 78}, {0, 78}}, color = {0, 0, 255})); connect(resistor3.p, pin3) annotation( Line(points = {{-26, 2}, {-26, 2}, {-26, 18}, {-36, 18}, {-36, 78}, {-90, 78}, {-90, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255})); connect(resistor2.p, pin2) annotation( Line(points = {{-48, 2}, {-48, 2}, {-48, 52}, {-84, 52}, {-84, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255})); connect(resistor1.p, pin1) annotation( Line(points = {{-68, 4}, {-68, -39}, {-100, -39}, {-100, -60}}, color = {0, 0, 255})); connect(resistor1.n, capacitor1.p) annotation( Line(points = {{-68, -16}, {-75, -16}, {-75, -28}, {-70, -28}}, color = {0, 0, 255})); connect(resistor2.n, capacitor2.p) annotation( Line(points = {{-48, -18}, {-48, -18}, {-48, -28}, {-48, -28}}, color = {0, 0, 255})); connect(resistor3.n, capacitor3.p) annotation( Line(points = {{-26, -18}, {-26, -18}, {-26, -28}, {-26, -28}}, color = {0, 0, 255})); connect(resistor9.n, capacitor6.p) annotation( Line(points = {{64, -18}, {64, -18}, {64, -28}, {64, -28}}, color = {0, 0, 255})); connect(capacitor6.n, capacitor5.n) annotation( Line(points = {{64, -48}, {46, -48}}, color = {0, 0, 255})); connect(capacitor5.p, resistor8.n) annotation( Line(points = {{46, -28}, {46, -28}, {46, -18}, {46, -18}}, color = {0, 0, 255})); connect(resistor5.n, resistor8.p) annotation( Line(points = {{20, 52}, {46, 52}, {46, 2}}, color = {0, 0, 255})); connect(resistor7.n, capacitor4.p) annotation( Line(points = {{26, -18}, {26, -18}, {26, -28}, {26, -28}}, color = {0, 0, 255})); connect(resistor6.n, pin6) annotation( Line(points = {{20, 78}, {84, 78}, {84, 60}, {100, 60}}, color = {0, 0, 255})); connect(resistor5.n, pin5) annotation( Line(points = {{20, 52}, {84, 52}, {84, 0}, {100, 0}}, color = {0, 0, 255})); connect(resistor4.n, pin4) annotation( Line(points = {{20, 28}, {74, 28}, {74, 28}, {76, 28}, {76, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255})); connect(resistor6.n, resistor9.p) annotation( Line(points = {{20, 78}, {64, 78}, {64, 2}, {64, 2}}, color = {0, 0, 255})); connect(resistor4.n, resistor7.p) annotation( Line(points = {{20, 28}, {26, 28}, {26, 2}, {26, 2}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-80, -60}, {-80, 28}, {-24, 28}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-90, 60}, {-90, 78}, {-24, 78}}, color = {0, 0, 255})); connect(capacitor3.n, ground1.p) annotation( Line(points = {{-26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255})); connect(capacitor4.n, ground1.p) annotation( Line(points = {{26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255})); connect(inductor2.p, pin2) annotation( Line(points = {{-24, 52}, {-84, 52}, {-84, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor3.n) annotation( Line(points = {{-48, -48}, {-26, -48}, {-26, -48}, {-26, -48}}, color = {0, 0, 255})); connect(capacitor1.n, capacitor2.n) annotation( Line(points = {{-70, -48}, {-48, -48}, {-48, -48}, {-48, -48}}, color = {0, 0, 255})); connect(capacitor5.n, capacitor4.n) annotation( Line(points = {{46, -48}, {26, -48}, {26, -48}, {26, -48}}, color = {0, 0, 255})); end pi; model lcl parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Inductance L4 = 0.001; parameter SI.Inductance L5 = 0.001; parameter SI.Inductance L6 = 0.001; parameter SI.Resistance R1 = 0.01; parameter SI.Resistance R2 = 0.01; parameter SI.Resistance R3 = 0.01; parameter SI.Resistance R4 = 0.01; parameter SI.Resistance R5 = 0.01; parameter SI.Resistance R6 = 0.01; parameter SI.Resistance R7 = 0.01; parameter SI.Resistance R8 = 0.01; parameter SI.Resistance R9 = 0.01; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-64, 58}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-72, 86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {38, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-28, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation( Placement(visible = true, transformation(origin = {68, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation( Placement(visible = true, transformation(origin = {70, 40}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation( Placement(visible = true, transformation(origin = {74, 62}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor1 annotation( Placement(visible = true, transformation(origin = {-36, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor2 annotation( Placement(visible = true, transformation(origin = {-32, 48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor3 annotation( Placement(visible = true, transformation(origin = {-30, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor4 annotation( Placement(visible = true, transformation(origin = {42, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor5 annotation( Placement(visible = true, transformation(origin = {8, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor6 annotation( Placement(visible = true, transformation(origin = {-22, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor7 annotation( Placement(visible = true, transformation(origin = {32, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor8 annotation( Placement(visible = true, transformation(origin = {40, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor9 annotation( Placement(visible = true, transformation(origin = {34, 68}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(resistor2.n, resistor5.p) annotation( Line(points = {{-22, 48}, {8, 48}, {8, 0}}, color = {0, 0, 255})); connect(resistor8.p, resistor2.n) annotation( Line(points = {{30, 44}, {2, 44}, {2, 48}, {-22, 48}}, color = {0, 0, 255})); connect(resistor2.p, inductor2.n) annotation( Line(points = {{-42, 48}, {-50, 48}, {-50, 58}, {-54, 58}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 58}, {-74, 58}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 86}, {-82, 86}}, color = {0, 0, 255})); connect(resistor3.p, inductor3.n) annotation( Line(points = {{-40, 82}, {-47, 82}, {-47, 86}, {-62, 86}}, color = {0, 0, 255})); connect(resistor3.n, resistor9.p) annotation( Line(points = {{-20, 82}, {3, 82}, {3, 68}, {24, 68}}, color = {0, 0, 255})); connect(resistor6.p, resistor3.n) annotation( Line(points = {{-22, 2}, {-22, 41}, {-20, 41}, {-20, 82}}, color = {0, 0, 255})); connect(inductor6.n, pin6) annotation( Line(points = {{84, 62}, {84, 60}, {100, 60}}, color = {0, 0, 255})); connect(resistor9.n, inductor6.p) annotation( Line(points = {{44, 68}, {55, 68}, {55, 62}, {64, 62}}, color = {0, 0, 255})); connect(inductor5.n, pin5) annotation( Line(points = {{80, 40}, {88, 40}, {88, 0}, {100, 0}}, color = {0, 0, 255})); connect(resistor8.n, inductor5.p) annotation( Line(points = {{50, 44}, {55, 44}, {55, 40}, {60, 40}}, color = {0, 0, 255})); connect(resistor7.n, inductor4.p) annotation( Line(points = {{42, 30}, {54, 30}, {54, 6}, {58, 6}}, color = {0, 0, 255})); connect(resistor4.p, resistor7.p) annotation( Line(points = {{42, 0}, {42, 14.5}, {22, 14.5}, {22, 30}}, color = {0, 0, 255})); connect(resistor1.n, resistor7.p) annotation( Line(points = {{-26, 20}, {2, 20}, {2, 30}, {22, 30}}, color = {0, 0, 255})); connect(inductor4.n, pin4) annotation( Line(points = {{78, 6}, {80, 6}, {80, -60}, {100, -60}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor1.n) annotation( Line(points = {{12, -46}, {23, -46}, {23, -56}, {38, -56}}, color = {0, 0, 255})); connect(resistor4.n, capacitor1.p) annotation( Line(points = {{42, -20}, {42, -24}, {38, -24}, {38, -36}}, color = {0, 0, 255})); connect(resistor5.n, capacitor2.p) annotation( Line(points = {{8, -20}, {8, -24}, {12, -24}, {12, -26}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-28, -46}, {12, -46}}, color = {0, 0, 255})); connect(resistor6.n, capacitor3.p) annotation( Line(points = {{-22, -18}, {-22, -24}, {-28, -24}, {-28, -26}}, color = {0, 0, 255})); connect(resistor1.p, inductor1.n) annotation( Line(points = {{-46, 20}, {-50, 20}, {-50, 20}, {-50, 20}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255})); end lcl; model lc parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Resistance R1 = 0.01; parameter SI.Resistance R2 = 0.01; parameter SI.Resistance R3 = 0.01; parameter SI.Resistance R4 = 0.01; parameter SI.Resistance R5 = 0.01; parameter SI.Resistance R6 = 0.01; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {-34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {-26, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor4(R = R4) annotation( Placement(visible = true, transformation(origin = {32, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor5(R = R5) annotation( Placement(visible = true, transformation(origin = {12, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor6(R = R6) annotation( Placement(visible = true, transformation(origin = {-8, -8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(resistor3.n, resistor6.p) annotation( Line(points = {{-16, 70}, {-8, 70}, {-8, 2}}, color = {0, 0, 255})); connect(resistor3.n, pin6) annotation( Line(points = {{-16, 70}, {80, 70}, {80, 60}, {100, 60}}, color = {0, 0, 255})); connect(inductor3.n, resistor3.p) annotation( Line(points = {{-50, 70}, {-36, 70}}, color = {0, 0, 255})); connect(pin4, resistor1.n) annotation( Line(points = {{100, -60}, {62, -60}, {62, 20}, {-24, 20}, {-24, 20}}, color = {0, 0, 255})); connect(resistor4.n, capacitor1.p) annotation( Line(points = {{32, -18}, {32, -18}, {32, -26}, {32, -26}}, color = {0, 0, 255})); connect(resistor5.n, capacitor2.p) annotation( Line(points = {{12, -18}, {12, -18}, {12, -26}, {12, -26}}, color = {0, 0, 255})); connect(resistor6.n, capacitor3.p) annotation( Line(points = {{-8, -18}, {-8, -18}, {-8, -26}, {-8, -26}}, color = {0, 0, 255})); connect(pin5, resistor2.n) annotation( Line(points = {{100, 0}, {78, 0}, {78, 44}, {-24, 44}, {-24, 44}}, color = {0, 0, 255})); connect(resistor2.n, resistor5.p) annotation( Line(points = {{-24, 44}, {12, 44}, {12, 2}, {12, 2}}, color = {0, 0, 255})); connect(resistor1.n, resistor4.p) annotation( Line(points = {{-24, 20}, {32, 20}, {32, 2}, {32, 2}}, color = {0, 0, 255})); connect(inductor1.n, resistor1.p) annotation( Line(points = {{-50, 20}, {-44, 20}, {-44, 20}, {-44, 20}}, color = {0, 0, 255})); connect(inductor2.n, resistor2.p) annotation( Line(points = {{-50, 44}, {-44, 44}, {-44, 44}, {-44, 44}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor1.n) annotation( Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255})); end lc; model lclc parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Capacitance C4 = 0.00001; parameter SI.Capacitance C5 = 0.00001; parameter SI.Capacitance C6 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Inductance L4 = 0.001; parameter SI.Inductance L5 = 0.001; parameter SI.Inductance L6 = 0.001; parameter SI.Resistance R1 = 0.01; parameter SI.Resistance R2 = 0.01; parameter SI.Resistance R3 = 0.01; parameter SI.Resistance R4 = 0.01; parameter SI.Resistance R5 = 0.01; parameter SI.Resistance R6 = 0.01; parameter SI.Resistance R7 = 0.01; parameter SI.Resistance R8 = 0.01; parameter SI.Resistance R9 = 0.01; parameter SI.Resistance R10 = 0.01; parameter SI.Resistance R11 = 0.01; parameter SI.Resistance R12 = 0.01; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-82, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-82, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-84, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-2, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {-22, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-42, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {16, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation( Placement(visible = true, transformation(origin = {34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation( Placement(visible = true, transformation(origin = {34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation( Placement(visible = true, transformation(origin = {36, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation( Placement(visible = true, transformation(origin = {72, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation( Placement(visible = true, transformation(origin = {52, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation( Placement(visible = true, transformation(origin = {32, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-56, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {-56, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {-56, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor4(R = R4) annotation( Placement(visible = true, transformation(origin = {-2, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor5(R = R5) annotation( Placement(visible = true, transformation(origin = {-22, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor6(R = R6) annotation( Placement(visible = true, transformation(origin = {-42, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor7(R = R7) annotation( Placement(visible = true, transformation(origin = {10, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor8(R = R8) annotation( Placement(visible = true, transformation(origin = {10, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor9(R = R9) annotation( Placement(visible = true, transformation(origin = {10, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor10(R = R10) annotation( Placement(visible = true, transformation(origin = {72, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor11(R = R11) annotation( Placement(visible = true, transformation(origin = {52, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor12(R = R12) annotation( Placement(visible = true, transformation(origin = {32, -14}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(resistor10.p, inductor4.n) annotation( Line(points = {{72, -4}, {72, -4}, {72, 20}, {44, 20}, {44, 20}}, color = {0, 0, 255})); connect(inductor5.n, resistor11.p) annotation( Line(points = {{44, 44}, {54, 44}, {54, 44}, {52, 44}, {52, -4}, {52, -4}}, color = {0, 0, 255})); connect(resistor12.p, inductor6.n) annotation( Line(points = {{32, -4}, {32, -4}, {32, 10}, {58, 10}, {58, 70}, {46, 70}, {46, 70}}, color = {0, 0, 255})); connect(resistor11.n, capacitor5.p) annotation( Line(points = {{52, -24}, {52, -24}, {52, -28}, {52, -28}}, color = {0, 0, 255})); connect(resistor12.n, capacitor6.p) annotation( Line(points = {{32, -24}, {32, -24}, {32, -28}, {32, -28}}, color = {0, 0, 255})); connect(resistor1.n, resistor4.p) annotation( Line(points = {{-46, 20}, {-2, 20}, {-2, -4}, {-2, -4}, {-2, -4}}, color = {0, 0, 255})); connect(resistor2.n, resistor5.p) annotation( Line(points = {{-46, 44}, {-22, 44}, {-22, -4}, {-22, -4}, {-22, -4}}, color = {0, 0, 255})); connect(resistor6.p, resistor3.n) annotation( Line(points = {{-42, -4}, {-42, -4}, {-42, 70}, {-46, 70}, {-46, 70}}, color = {0, 0, 255})); connect(resistor1.n, resistor7.p) annotation( Line(points = {{-46, 20}, {-46, 20}, {-46, 20}, {0, 20}}, color = {0, 0, 255})); connect(resistor2.n, resistor8.p) annotation( Line(points = {{-46, 44}, {-46, 44}, {-46, 44}, {0, 44}}, color = {0, 0, 255})); connect(resistor3.n, resistor9.p) annotation( Line(points = {{-46, 70}, {0, 70}, {0, 70}, {0, 70}}, color = {0, 0, 255})); connect(resistor7.n, inductor4.p) annotation( Line(points = {{20, 20}, {24, 20}, {24, 20}, {24, 20}}, color = {0, 0, 255})); connect(resistor6.n, capacitor3.p) annotation( Line(points = {{-42, -24}, {-42, -24}, {-42, -28}, {-42, -28}}, color = {0, 0, 255})); connect(resistor5.n, capacitor2.p) annotation( Line(points = {{-22, -24}, {-22, -24}, {-22, -24}, {-22, -28}}, color = {0, 0, 255})); connect(resistor4.n, capacitor1.p) annotation( Line(points = {{-2, -24}, {-2, -24}, {-2, -28}, {-2, -28}}, color = {0, 0, 255})); connect(resistor10.n, capacitor4.p) annotation( Line(points = {{72, -24}, {72, -24}, {72, -28}, {72, -28}}, color = {0, 0, 255})); connect(resistor8.n, inductor5.p) annotation( Line(points = {{20, 44}, {24, 44}}, color = {0, 0, 255})); connect(resistor9.n, inductor6.p) annotation( Line(points = {{20, 70}, {26, 70}, {26, 70}, {26, 70}}, color = {0, 0, 255})); connect(inductor1.n, resistor1.p) annotation( Line(points = {{-72, 20}, {-66, 20}, {-66, 20}, {-66, 20}}, color = {0, 0, 255})); connect(inductor3.n, resistor3.p) annotation( Line(points = {{-74, 70}, {-74, 70}, {-74, 70}, {-66, 70}}, color = {0, 0, 255})); connect(inductor2.n, resistor2.p) annotation( Line(points = {{-72, 44}, {-66, 44}, {-66, 44}, {-66, 44}}, color = {0, 0, 255})); connect(inductor4.n, pin4) annotation( Line(points = {{44, 20}, {92, 20}, {92, -60}, {100, -60}}, color = {0, 0, 255})); connect(inductor6.n, pin6) annotation( Line(points = {{46, 70}, {76, 70}, {76, 60}, {100, 60}}, color = {0, 0, 255})); connect(capacitor6.n, ground1.p) annotation( Line(points = {{32, -48}, {16, -48}, {16, -60}}, color = {0, 0, 255})); connect(capacitor6.n, capacitor5.n) annotation( Line(points = {{32, -48}, {52, -48}, {52, -48}, {52, -48}}, color = {0, 0, 255})); connect(capacitor5.n, capacitor4.n) annotation( Line(points = {{52, -48}, {72, -48}, {72, -48}, {72, -48}}, color = {0, 0, 255})); connect(capacitor1.n, ground1.p) annotation( Line(points = {{-2, -48}, {16, -48}, {16, -60}, {16, -60}}, color = {0, 0, 255})); connect(inductor5.n, pin5) annotation( Line(points = {{44, 44}, {94, 44}, {94, 0}, {100, 0}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-42, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255})); connect(capacitor1.n, capacitor2.n) annotation( Line(points = {{-2, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-94, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-95, 0}, {-95, 44}, {-92, 44}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-93, -60}, {-93, 20}, {-92, 20}}, color = {0, 0, 255})); end lclc; model l parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Resistance R1 = 0.01; parameter SI.Resistance R2 = 0.01; parameter SI.Resistance R3 = 0.01; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); grid.components.resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-32, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {-32, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {-32, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(resistor1.n, pin4) annotation( Line(points = {{-22, 20}, {14, 20}, {14, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255})); connect(resistor2.n, pin5) annotation( Line(points = {{-22, 44}, {70, 44}, {70, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255})); connect(resistor3.n, pin6) annotation( Line(points = {{-22, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255})); connect(inductor1.n, resistor1.p) annotation( Line(points = {{-50, 20}, {-50, 20}, {-50, 20}, {-42, 20}}, color = {0, 0, 255})); connect(inductor2.n, resistor2.p) annotation( Line(points = {{-50, 44}, {-42, 44}, {-42, 44}, {-42, 44}}, color = {0, 0, 255})); connect(inductor3.n, resistor3.p) annotation( Line(points = {{-50, 70}, {-42, 70}, {-42, 70}, {-42, 70}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255})); end l; end filter; package loads model rc parameter SI.Resistance R1 = 20; parameter SI.Resistance R2 = 20; parameter SI.Resistance R3 = 20; parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-66, -48}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {-32, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {48, 0}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); grid.components.resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-50, -48}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {0, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {72, -2}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(resistor2.n, ground1.p) annotation( Line(points = {{0, -20}, {0, -20}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin2, resistor2.p) annotation( Line(points = {{-100, 0}, {0, 0}, {0, 0}, {0, 0}}, color = {0, 0, 255})); connect(resistor1.p, pin1) annotation( Line(points = {{-50, -38}, {-50, -38}, {-50, -22}, {-90, -22}, {-90, -60}, {-100, -60}, {-100, -60}}, color = {0, 0, 255})); connect(resistor1.n, ground1.p) annotation( Line(points = {{-50, -58}, {-50, -58}, {-50, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(resistor3.n, ground1.p) annotation( Line(points = {{72, -12}, {72, -12}, {72, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin3, resistor3.p) annotation( Line(points = {{-100, 60}, {68, 60}, {68, 60}, {72, 60}, {72, 8}, {72, 8}}, color = {0, 0, 255})); connect(capacitor1.p, pin1) annotation( Line(points = {{-66, -38}, {-66, -22}, {-90, -22}, {-90, -60}, {-100, -60}}, color = {0, 0, 255})); connect(capacitor3.p, pin3) annotation( Line(points = {{48, 10}, {48, 60}, {-100, 60}}, color = {0, 0, 255})); connect(pin3, capacitor3.p) annotation( Line(points = {{-100, 60}, {48, 60}, {48, 10}}, color = {0, 0, 255})); connect(capacitor3.n, ground1.p) annotation( Line(points = {{48, -10}, {48, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(capacitor2.p, pin2) annotation( Line(points = {{-32, 0}, {-100, 0}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{-32, -20}, {-32, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(capacitor1.n, ground1.p) annotation( Line(points = {{-66, -58}, {-66, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); end rc; model c parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-34, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {4, -10}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {40, 38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {4, -84}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(capacitor3.p, pin3) annotation( Line(points = {{40, 48}, {40, 60}, {-100, 60}}, color = {0, 0, 255})); connect(pin2, capacitor2.p) annotation( Line(points = {{-102, 0}, {4, 0}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{4, -20}, {4, -74}}, color = {0, 0, 255})); connect(pin1, capacitor1.p) annotation( Line(points = {{-100, -60}, {-67, -60}, {-67, -34}, {-34, -34}}, color = {0, 0, 255})); connect(capacitor3.n, ground1.p) annotation( Line(points = {{40, 28}, {40, -54}, {4, -54}, {4, -74}}, color = {0, 0, 255})); connect(capacitor1.n, ground1.p) annotation( Line(points = {{-34, -54}, {4, -54}, {4, -74}, {4, -74}}, color = {0, 0, 255})); end c; model r parameter SI.Resistance R1 = 20; parameter SI.Resistance R2 = 20; parameter SI.Resistance R3 = 20; Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); grid.components.resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-66, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor2(R = R1) annotation( Placement(visible = true, transformation(origin = {-66, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.components.resistor resistor3(R = R1) annotation( Placement(visible = true, transformation(origin = {-66, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(resistor3.n, ground1.p) annotation( Line(points = {{-56, 60}, {0, 60}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(resistor2.n, ground1.p) annotation( Line(points = {{-56, 0}, {-56, 0}, {-56, 0}, {0, 0}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(resistor1.n, ground1.p) annotation( Line(points = {{-56, -60}, {0, -60}, {0, -76}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin1, resistor1.p) annotation( Line(points = {{-100, -60}, {-76, -60}, {-76, -60}, {-76, -60}}, color = {0, 0, 255})); connect(pin2, resistor2.p) annotation( Line(points = {{-100, 0}, {-100, 0}, {-100, 0}, {-76, 0}}, color = {0, 0, 255})); connect(pin3, resistor3.p) annotation( Line(points = {{-100, 60}, {-76, 60}, {-76, 60}, {-76, 60}}, color = {0, 0, 255})); end r; model l parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-48, -50}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {0, -16}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {50, 50}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(inductor3.n, ground1.p) annotation( Line(points = {{50, 40}, {50, 40}, {50, -60}, {0, -60}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {50, 60}, {50, 60}, {50, 60}}, color = {0, 0, 255})); connect(inductor2.n, ground1.p) annotation( Line(points = {{0, -26}, {0, -26}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {0, 0}, {0, -6}}, color = {0, 0, 255})); connect(inductor1.n, ground1.p) annotation( Line(points = {{-48, -60}, {0, -60}, {0, -76}, {0, -76}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-78, -60}, {-78, -40}, {-48, -40}, {-48, -40}, {-48, -40}}, color = {0, 0, 255})); end l; model rlc parameter SI.Resistance R1 = 20; parameter SI.Resistance R2 = 20; parameter SI.Resistance R3 = 20; parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-74, -68}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {0, -30}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {74, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-74, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {0, 2}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {74, -4}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor1(R = R1) annotation( Placement(visible = true, transformation(origin = {-74, -20}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor2(R = R2) annotation( Placement(visible = true, transformation(origin = {0, 36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor3(R = R3) annotation( Placement(visible = true, transformation(origin = {74, 42}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(resistor1.p, pin1) annotation( Line(points = {{-74, -10}, {-100, -10}, {-100, -60}}, color = {0, 0, 255})); connect(pin2, resistor2.p) annotation( Line(points = {{-100, 0}, {-50, 0}, {-50, 46}, {0, 46}}, color = {0, 0, 255})); connect(resistor3.p, pin3) annotation( Line(points = {{74, 52}, {74, 52}, {74, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{0, -40}, {0, -76}}, color = {0, 0, 255})); connect(capacitor3.n, ground1.p) annotation( Line(points = {{74, -56}, {74, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(capacitor1.p, inductor1.n) annotation( Line(points = {{-74, -58}, {-74, -58}, {-74, -54}, {-74, -54}}, color = {0, 0, 255})); connect(resistor1.n, inductor1.p) annotation( Line(points = {{-74, -30}, {-74, -30}, {-74, -30}, {-74, -34}}, color = {0, 0, 255})); connect(resistor2.n, inductor2.p) annotation( Line(points = {{0, 26}, {0, 26}, {0, 12}, {0, 12}}, color = {0, 0, 255})); connect(inductor2.n, capacitor2.p) annotation( Line(points = {{0, -8}, {0, -8}, {0, -8}, {0, -20}}, color = {0, 0, 255})); connect(resistor3.n, inductor3.p) annotation( Line(points = {{74, 32}, {74, 32}, {74, 6}, {74, 6}}, color = {0, 0, 255})); connect(capacitor3.p, inductor3.n) annotation( Line(points = {{74, -36}, {74, -36}, {74, -14}, {74, -14}}, color = {0, 0, 255})); connect(capacitor1.n, ground1.p) annotation( Line(points = {{-74, -78}, {-46, -78}, {-46, -62}, {0, -62}, {0, -76}, {0, -76}}, color = {0, 0, 255})); end rlc; model lc parameter SI.Capacitance C1(start = 0.00001); parameter SI.Capacitance C2(start = 0.00001); parameter SI.Capacitance C3(start = 0.00001); parameter SI.Inductance L1(start = 0.001); parameter SI.Inductance L2(start = 0.001); parameter SI.Inductance L3(start = 0.001); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-56, -18}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {0, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {56, 42}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-56, -44}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {0, -24}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {56, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(capacitor3.n, inductor3.p) annotation( Line(points = {{56, 32}, {56, 32}, {56, 18}, {56, 18}}, color = {0, 0, 255})); connect(inductor3.n, ground1.p) annotation( Line(points = {{56, -2}, {56, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(capacitor1.n, inductor1.p) annotation( Line(points = {{-56, -28}, {-56, -28}, {-56, -34}, {-56, -34}}, color = {0, 0, 255})); connect(inductor1.n, ground1.p) annotation( Line(points = {{-56, -54}, {-56, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(capacitor1.p, pin1) annotation( Line(points = {{-56, -8}, {-56, 0}, {-88, 0}, {-88, -60}, {-100, -60}}, color = {0, 0, 255})); connect(capacitor2.p, pin2) annotation( Line(points = {{0, 18}, {0, 24}, {-94, 24}, {-94, 0}, {-100, 0}}, color = {0, 0, 255})); connect(capacitor2.n, inductor2.p) annotation( Line(points = {{0, -2}, {0, -2}, {0, -14}, {0, -14}}, color = {0, 0, 255})); connect(inductor2.n, ground1.p) annotation( Line(points = {{0, -34}, {0, -76}}, color = {0, 0, 255})); connect(pin3, capacitor3.p) annotation( Line(points = {{-100, 60}, {56, 60}, {56, 52}}, color = {0, 0, 255})); connect(capacitor3.p, pin3) annotation( Line(points = {{56, 52}, {56, 60}, {-100, 60}}, color = {0, 0, 255})); end lc; model rl parameter SI.Resistance R1 = 20; parameter SI.Resistance R2 = 20; parameter SI.Resistance R3 = 20; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -86}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-40, -46}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {0, -18}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {60, 8}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor1(R=R1) annotation( Placement(visible = true, transformation(origin = {-40, -20}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor2(R=R2) annotation( Placement(visible = true, transformation(origin = {0, 12}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.components.resistor resistor3(R=R3) annotation( Placement(visible = true, transformation(origin = {60, 34}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(resistor1.n, inductor1.p) annotation( Line(points = {{-40, -30}, {-40, -30}, {-40, -36}, {-40, -36}}, color = {0, 0, 255})); connect(pin3, resistor3.p) annotation( Line(points = {{-100, 60}, {60, 60}, {60, 44}, {60, 44}}, color = {0, 0, 255})); connect(resistor3.n, inductor3.p) annotation( Line(points = {{60, 24}, {60, 24}, {60, 24}, {60, 18}}, color = {0, 0, 255})); connect(resistor2.n, inductor2.p) annotation( Line(points = {{0, 2}, {0, 2}, {0, -8}, {0, -8}}, color = {0, 0, 255})); connect(pin2, resistor2.p) annotation( Line(points = {{-100, 0}, {-66, 0}, {-66, 22}, {0, 22}}, color = {0, 0, 255})); connect(pin1, resistor1.p) annotation( Line(points = {{-100, -60}, {-74, -60}, {-74, -10}, {-40, -10}, {-40, -10}}, color = {0, 0, 255})); connect(inductor3.n, ground1.p) annotation( Line(points = {{60, -2}, {60, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(inductor1.n, ground1.p) annotation( Line(points = {{-40, -56}, {-40, -62}, {0, -62}, {0, -76}}, color = {0, 0, 255})); connect(inductor2.n, ground1.p) annotation( Line(points = {{0, -28}, {0, -76}}, color = {0, 0, 255})); end rl; end loads; package inverters model inverter // input Real regler1; // input Real regler2; // input Real regler3; parameter Real v_DC = 1000; Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {-74, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage1 annotation( Placement(visible = true, transformation(origin = {-74, -42}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage2 annotation( Placement(visible = true, transformation(origin = {-74, 18}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage3 annotation( Placement(visible = true, transformation(origin = {-74, 78}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Blocks.Interfaces.RealInput u1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput u3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput u2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Blocks.Math.Gain gain3(k = v_DC/2) annotation( Placement(visible = true, transformation(origin = {-26, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Gain gain1(k = v_DC/2) annotation( Placement(visible = true, transformation(origin = {-26, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Gain gain2(k = v_DC/2) annotation( Placement(visible = true, transformation(origin = {-26, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(signalVoltage2.p, pin2) annotation( Line(points = {{-74, 28}, {80, 28}, {80, 0}, {100, 0}}, color = {0, 0, 255})); connect(signalVoltage3.p, pin3) annotation( Line(points = {{-74, 88}, {80, 88}, {80, 60}, {100, 60}}, color = {0, 0, 255})); connect(signalVoltage1.p, pin1) annotation( Line(points = {{-74, -32}, {80, -32}, {80, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255})); connect(signalVoltage3.n, ground1.p) annotation( Line(points = {{-74, 68}, {-74, 48}, {-82, 48}, {-82, -72}, {-74, -72}}, color = {0, 0, 255})); connect(signalVoltage2.n, ground1.p) annotation( Line(points = {{-74, 8}, {-82, 8}, {-82, -72}, {-74, -72}}, color = {0, 0, 255})); connect(signalVoltage1.n, ground1.p) annotation( Line(points = {{-74, -52}, {-74, -72}}, color = {0, 0, 255})); /* connect(signalVoltage1.v, regler1) annotation( Line); connect(signalVoltage2.v, regler2) annotation( Line); connect(signalVoltage3.v, regler3) annotation( Line); */ connect(u1, gain1.u) annotation( Line(points = {{-104, -60}, {-38, -60}, {-38, -60}, {-38, -60}}, color = {0, 0, 127})); connect(gain1.y, signalVoltage1.v) annotation( Line(points = {{-14, -60}, {-6, -60}, {-6, -42}, {-60, -42}, {-60, -42}, {-62, -42}}, color = {0, 0, 127})); connect(u2, gain2.u) annotation( Line(points = {{-104, 0}, {-38, 0}, {-38, 0}, {-38, 0}}, color = {0, 0, 127})); connect(gain2.y, signalVoltage2.v) annotation( Line(points = {{-14, 0}, {-6, 0}, {-6, 18}, {-62, 18}, {-62, 18}}, color = {0, 0, 127})); connect(u3, gain3.u) annotation( Line(points = {{-104, 60}, {-38, 60}, {-38, 60}, {-38, 60}}, color = {0, 0, 127})); connect(gain3.y, signalVoltage3.v) annotation( Line(points = {{-14, 60}, {-6, 60}, {-6, 78}, {-62, 78}, {-62, 78}}, color = {0, 0, 127})); annotation( uses(Modelica(version = "3.2.3"))); end inverter; end inverters; package ideal_filter model pi parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Capacitance C4 = 0.00001; parameter SI.Capacitance C5 = 0.00001; parameter SI.Capacitance C6 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {2, -16}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {2, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {0, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-70, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {-48, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {0, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation( Placement(visible = true, transformation(origin = {26, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation( Placement(visible = true, transformation(origin = {46, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation( Placement(visible = true, transformation(origin = {66, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(inductor3.n, capacitor6.p) annotation( Line(points = {{10, 60}, {66, 60}, {66, -28}, {66, -28}}, color = {0, 0, 255})); connect(capacitor3.p, pin3) annotation( Line(points = {{-26, -28}, {-26, -28}, {-26, 60}, {-100, 60}, {-100, 60}}, color = {0, 0, 255})); connect(inductor1.n, pin4) annotation( Line(points = {{12, -16}, {70, -16}, {70, -60}, {100, -60}}, color = {0, 0, 255})); connect(inductor1.n, capacitor4.p) annotation( Line(points = {{12, -16}, {26, -16}, {26, -28}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-80, -60}, {-80, -16}, {-8, -16}}, color = {0, 0, 255})); connect(inductor3.n, pin6) annotation( Line(points = {{10, 60}, {100, 60}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-10, 60}}, color = {0, 0, 255})); connect(inductor2.n, pin5) annotation( Line(points = {{12, 0}, {100, 0}}, color = {0, 0, 255})); connect(inductor2.n, capacitor5.p) annotation( Line(points = {{12, 0}, {46, 0}, {46, -28}}, color = {0, 0, 255})); connect(inductor2.p, pin2) annotation( Line(points = {{-8, 0}, {-100, 0}}, color = {0, 0, 255})); connect(capacitor1.p, pin1) annotation( Line(points = {{-70, -28}, {-80, -28}, {-80, -60}, {-100, -60}}, color = {0, 0, 255})); connect(capacitor2.p, pin2) annotation( Line(points = {{-48, -28}, {-48, -28}, {-48, 0}, {-100, 0}, {-100, 0}}, color = {0, 0, 255})); connect(capacitor3.n, ground1.p) annotation( Line(points = {{-26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255})); connect(capacitor4.n, ground1.p) annotation( Line(points = {{26, -48}, {0, -48}, {0, -60}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor3.n) annotation( Line(points = {{-48, -48}, {-26, -48}, {-26, -48}, {-26, -48}}, color = {0, 0, 255})); connect(capacitor1.n, capacitor2.n) annotation( Line(points = {{-70, -48}, {-48, -48}, {-48, -48}, {-48, -48}}, color = {0, 0, 255})); connect(capacitor5.n, capacitor4.n) annotation( Line(points = {{46, -48}, {26, -48}, {26, -48}, {26, -48}}, color = {0, 0, 255})); connect(capacitor6.n, capacitor5.n) annotation( Line(points = {{66, -48}, {46, -48}, {46, -48}, {46, -48}}, color = {0, 0, 255})); end pi; model lcl parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Inductance L4 = 0.001; parameter SI.Inductance L5 = 0.001; parameter SI.Inductance L6 = 0.001; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation( Placement(visible = true, transformation(origin = {68, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation( Placement(visible = true, transformation(origin = {74, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation( Placement(visible = true, transformation(origin = {64, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(inductor2.n, inductor5.p) annotation( Line(points = {{-50, 44}, {-50, 44}, {-50, 44}, {64, 44}}, color = {0, 0, 255})); connect(inductor2.n, capacitor2.p) annotation( Line(points = {{-50, 44}, {12, 44}, {12, -26}, {12, -26}}, color = {0, 0, 255})); connect(inductor1.n, inductor4.p) annotation( Line(points = {{-50, 20}, {-50, 20}, {-50, 20}, {58, 20}}, color = {0, 0, 255})); connect(inductor1.n, capacitor1.p) annotation( Line(points = {{-50, 20}, {32, 20}, {32, -26}, {32, -26}}, color = {0, 0, 255})); connect(inductor3.n, capacitor3.p) annotation( Line(points = {{-50, 70}, {-8, 70}, {-8, -26}, {-8, -26}}, color = {0, 0, 255})); connect(inductor3.n, inductor6.p) annotation( Line(points = {{-50, 70}, {54, 70}, {54, 70}, {54, 70}}, color = {0, 0, 255})); connect(inductor4.n, pin4) annotation( Line(points = {{78, 20}, {80, 20}, {80, -60}, {100, -60}}, color = {0, 0, 255})); connect(inductor6.n, pin6) annotation( Line(points = {{74, 70}, {84, 70}, {84, 60}, {100, 60}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255})); connect(inductor5.n, pin5) annotation( Line(points = {{84, 44}, {88, 44}, {88, 0}, {100, 0}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor1.n) annotation( Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255})); end lcl; model lc parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {32, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {12, -68}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {12, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-8, -36}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(inductor1.n, pin4) annotation( Line(points = {{-50, 20}, {54, 20}, {54, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255})); connect(inductor2.n, pin5) annotation( Line(points = {{-50, 44}, {68, 44}, {68, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255})); connect(inductor3.n, pin6) annotation( Line(points = {{-50, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255})); connect(inductor3.n, capacitor3.p) annotation( Line(points = {{-50, 70}, {-8, 70}, {-8, -26}, {-8, -26}}, color = {0, 0, 255})); connect(inductor2.n, capacitor2.p) annotation( Line(points = {{-50, 44}, {12, 44}, {12, -26}, {12, -26}}, color = {0, 0, 255})); connect(inductor1.n, capacitor1.p) annotation( Line(points = {{-50, 20}, {32, 20}, {32, -26}, {32, -26}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-8, -46}, {12, -46}, {12, -46}, {12, -46}}, color = {0, 0, 255})); connect(capacitor2.n, ground1.p) annotation( Line(points = {{12, -46}, {12, -46}, {12, -58}, {12, -58}}, color = {0, 0, 255})); connect(capacitor2.n, capacitor1.n) annotation( Line(points = {{12, -46}, {32, -46}, {32, -46}, {32, -46}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255})); end lc; model lclc parameter SI.Capacitance C1 = 0.00001; parameter SI.Capacitance C2 = 0.00001; parameter SI.Capacitance C3 = 0.00001; parameter SI.Capacitance C4 = 0.00001; parameter SI.Capacitance C5 = 0.00001; parameter SI.Capacitance C6 = 0.00001; parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; parameter SI.Inductance L4 = 0.001; parameter SI.Inductance L5 = 0.001; parameter SI.Inductance L6 = 0.001; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-82, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-82, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-84, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Basic.Capacitor capacitor1(C = C1) annotation( Placement(visible = true, transformation(origin = {-2, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor2(C = C2) annotation( Placement(visible = true, transformation(origin = {-22, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor3(C = C3) annotation( Placement(visible = true, transformation(origin = {-42, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Ground ground1 annotation( Placement(visible = true, transformation(origin = {16, -70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); Modelica.Electrical.Analog.Basic.Inductor inductor4(L = L4) annotation( Placement(visible = true, transformation(origin = {34, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor5(L = L5) annotation( Placement(visible = true, transformation(origin = {34, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor6(L = L6) annotation( Placement(visible = true, transformation(origin = {36, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Capacitor capacitor4(C = C4) annotation( Placement(visible = true, transformation(origin = {72, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor5(C = C5) annotation( Placement(visible = true, transformation(origin = {52, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); Modelica.Electrical.Analog.Basic.Capacitor capacitor6(C = C6) annotation( Placement(visible = true, transformation(origin = {32, -38}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); equation connect(inductor4.n, capacitor4.p) annotation( Line(points = {{44, 20}, {72, 20}, {72, -28}, {72, -28}}, color = {0, 0, 255})); connect(inductor6.n, capacitor6.p) annotation( Line(points = {{46, 70}, {64, 70}, {64, 4}, {32, 4}, {32, -28}}, color = {0, 0, 255})); connect(capacitor3.p, inductor3.n) annotation( Line(points = {{-42, -28}, {-42, -28}, {-42, 70}, {-74, 70}, {-74, 70}}, color = {0, 0, 255})); connect(capacitor2.p, inductor2.n) annotation( Line(points = {{-22, -28}, {-22, -28}, {-22, 44}, {-72, 44}, {-72, 44}}, color = {0, 0, 255})); connect(inductor5.n, capacitor5.p) annotation( Line(points = {{44, 44}, {52, 44}, {52, -28}, {52, -28}}, color = {0, 0, 255})); connect(inductor3.n, inductor6.p) annotation( Line(points = {{-74, 70}, {26, 70}, {26, 70}, {26, 70}}, color = {0, 0, 255})); connect(inductor2.n, inductor5.p) annotation( Line(points = {{-72, 44}, {-72, 44}, {-72, 44}, {24, 44}}, color = {0, 0, 255})); connect(inductor1.n, inductor4.p) annotation( Line(points = {{-72, 20}, {24, 20}, {24, 20}, {24, 20}}, color = {0, 0, 255})); connect(inductor1.n, capacitor1.p) annotation( Line(points = {{-72, 20}, {-2, 20}, {-2, -28}, {-2, -28}}, color = {0, 0, 255})); connect(inductor4.n, pin4) annotation( Line(points = {{44, 20}, {92, 20}, {92, -60}, {100, -60}}, color = {0, 0, 255})); connect(inductor6.n, pin6) annotation( Line(points = {{46, 70}, {76, 70}, {76, 60}, {100, 60}}, color = {0, 0, 255})); connect(capacitor6.n, ground1.p) annotation( Line(points = {{32, -48}, {16, -48}, {16, -60}}, color = {0, 0, 255})); connect(capacitor6.n, capacitor5.n) annotation( Line(points = {{32, -48}, {52, -48}, {52, -48}, {52, -48}}, color = {0, 0, 255})); connect(capacitor5.n, capacitor4.n) annotation( Line(points = {{52, -48}, {72, -48}, {72, -48}, {72, -48}}, color = {0, 0, 255})); connect(capacitor1.n, ground1.p) annotation( Line(points = {{-2, -48}, {16, -48}, {16, -60}, {16, -60}}, color = {0, 0, 255})); connect(inductor5.n, pin5) annotation( Line(points = {{44, 44}, {94, 44}, {94, 0}, {100, 0}}, color = {0, 0, 255})); connect(capacitor3.n, capacitor2.n) annotation( Line(points = {{-42, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255})); connect(capacitor1.n, capacitor2.n) annotation( Line(points = {{-2, -48}, {-22, -48}, {-22, -48}, {-22, -48}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-94, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-95, 0}, {-95, 44}, {-92, 44}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-93, -60}, {-93, 20}, {-92, 20}}, color = {0, 0, 255})); end lclc; model l parameter SI.Inductance L1 = 0.001; parameter SI.Inductance L2 = 0.001; parameter SI.Inductance L3 = 0.001; Modelica.Electrical.Analog.Basic.Inductor inductor1(L = L1) annotation( Placement(visible = true, transformation(origin = {-60, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor2(L = L2) annotation( Placement(visible = true, transformation(origin = {-60, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Inductor inductor3(L = L3) annotation( Placement(visible = true, transformation(origin = {-60, 70}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Interfaces.Pin pin3 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin1 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin2 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin6 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin4 annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin pin5 annotation( 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))); equation connect(inductor3.n, pin6) annotation( Line(points = {{-50, 70}, {80, 70}, {80, 60}, {100, 60}, {100, 60}}, color = {0, 0, 255})); connect(inductor2.n, pin5) annotation( Line(points = {{-50, 44}, {78, 44}, {78, 0}, {100, 0}, {100, 0}}, color = {0, 0, 255})); connect(inductor1.n, pin4) annotation( Line(points = {{-50, 20}, {60, 20}, {60, -60}, {100, -60}, {100, -60}}, color = {0, 0, 255})); connect(pin1, inductor1.p) annotation( Line(points = {{-100, -60}, {-85, -60}, {-85, 20}, {-70, 20}}, color = {0, 0, 255})); connect(pin3, inductor3.p) annotation( Line(points = {{-100, 60}, {-93, 60}, {-93, 70}, {-70, 70}}, color = {0, 0, 255})); connect(pin2, inductor2.p) annotation( Line(points = {{-100, 0}, {-91, 0}, {-91, 44}, {-70, 44}}, color = {0, 0, 255})); end l; end ideal_filter; package components model resistor parameter SI.Resistance R(start = 1); extends Modelica.Electrical.Analog.Interfaces.OnePort; equation v = R * i; annotation( Documentation(info = "

The linear resistor connects the branch voltage v with the branch current i by i*R = v. The Resistance R is allowed to be positive, zero, or negative.

", revisions = "
  • August 07, 2009 by Anton Haumer
    temperature dependency of resistance added
  • March 11, 2009 by Christoph Clauss
    conditional heat port added
  • 1998 by Christoph Clauss
    initially implemented
"), 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})})); end resistor; end components; package plls model pll Modelica.Electrical.Analog.Interfaces.Pin a annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin b annotation( 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))); Modelica.Electrical.Analog.Interfaces.Pin c annotation( 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))); Modelica.Electrical.Analog.Basic.Ground ground annotation( Placement(visible = true, transformation(origin = {-86, 62}, extent = {{-6, -6}, {6, 6}}, rotation = 180))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_c annotation( Placement(visible = true, transformation(origin = {-88, -8}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_a annotation( Placement(visible = true, transformation(origin = {-86, 50}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); Modelica.Electrical.Analog.Sensors.VoltageSensor voltageSensor_b annotation( Placement(visible = true, transformation(origin = {-88, 22}, extent = {{-6, -6}, {6, 6}}, rotation = 90))); grid.transforms.abc2AlphaBeta abc2AlphaBeta annotation( Placement(visible = true, transformation(origin = {-62, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Sin sin annotation( Placement(visible = true, transformation(origin = {-10, -6}, extent = {{-4, -4}, {4, 4}}, rotation = 180))); Modelica.Blocks.Math.Cos cos annotation( Placement(visible = true, transformation(origin = {-10, -18}, extent = {{-4, -4}, {4, 4}}, rotation = 180))); Modelica.Blocks.Math.Gain Norm_U_ref_alpha(k = 1 / (230 * 1.414)) annotation( Placement(visible = true, transformation(origin = {-33, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Gain Norm_U_ref_beta(k = 1 / (230 * 1.414)) annotation( Placement(visible = true, transformation(origin = {-33, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Product alphaSin annotation( Placement(visible = true, transformation(origin = {-7, 29}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Product betaCos annotation( Placement(visible = true, transformation(origin = {-9, 15}, extent = {{-3, -3}, {3, 3}}, rotation = 0))); Modelica.Blocks.Math.Add add(k1 = -1, k2 = +1) annotation( Placement(visible = true, transformation(origin = {12, 24}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Continuous.PI pi(T = 0.2, k = 15) annotation( Placement(visible = true, transformation(origin = {26, 24}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Add add_freq_nom_delta_f(k1 = +1, k2 = +1) annotation( Placement(visible = true, transformation(origin = {48, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Sources.Constant f_nom(k = 50) annotation( Placement(visible = true, transformation(origin = {28, 4}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Continuous.Integrator f2theta(y_start = 0) annotation( Placement(visible = true, transformation(origin = {64, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); Modelica.Blocks.Math.Gain deg2rad(k = 2 * 3.1416) annotation( Placement(visible = true, transformation(origin = {78, 22}, extent = {{-4, -4}, {4, 4}}, rotation = 0))); equation connect(a, voltageSensor_a.p) annotation( Line(points = {{-100, 44}, {-86, 44}}, color = {0, 0, 255})); connect(b, voltageSensor_b.p) annotation( Line(points = {{-100, 16}, {-88, 16}}, color = {0, 0, 255})); connect(c, voltageSensor_c.p) annotation( Line(points = {{-100, -14}, {-88, -14}}, color = {0, 0, 255})); connect(voltageSensor_a.n, ground.p) annotation( Line(points = {{-86, 56}, {-86, 56}}, color = {0, 0, 255})); connect(voltageSensor_b.n, ground.p) annotation( Line(points = {{-88, 28}, {-88, 42}, {-86, 42}, {-86, 56}}, color = {0, 0, 255})); connect(voltageSensor_c.n, ground.p) annotation( Line(points = {{-88, -2}, {-88, 27}, {-86, 27}, {-86, 56}}, color = {0, 0, 255})); connect(abc2AlphaBeta.b, voltageSensor_b.v) annotation( Line(points = {{-72, 21}, {-74, 21}, {-74, 22}, {-82, 22}}, color = {0, 0, 127})); connect(abc2AlphaBeta.a, voltageSensor_a.v) annotation( Line(points = {{-72, 24}, {-76, 24}, {-76, 50}, {-80, 50}}, color = {0, 0, 127})); connect(abc2AlphaBeta.c, voltageSensor_c.v) annotation( Line(points = {{-72, 18}, {-76, 18}, {-76, -8}, {-82, -8}}, color = {0, 0, 127})); connect(Norm_U_ref_alpha.u, abc2AlphaBeta.alpha) annotation( Line(points = {{-37, 29}, {-40, 29}, {-40, 26}, {-52, 26}}, color = {0, 0, 127})); connect(Norm_U_ref_beta.u, abc2AlphaBeta.beta) annotation( Line(points = {{-37, 15}, {-42, 15}, {-42, 17}, {-52, 17}}, color = {0, 0, 127})); connect(Norm_U_ref_alpha.y, alphaSin.u1) annotation( Line(points = {{-30, 30}, {-11, 30}, {-11, 31}}, color = {0, 0, 127})); connect(Norm_U_ref_beta.y, betaCos.u1) annotation( Line(points = {{-30, 16}, {-13, 16}, {-13, 17}}, color = {0, 0, 127})); connect(sin.y, alphaSin.u2) annotation( Line(points = {{-14, -6}, {-22, -6}, {-22, 27}, {-11, 27}}, color = {0, 0, 127})); connect(cos.y, betaCos.u2) annotation( Line(points = {{-14, -18}, {-18, -18}, {-18, 13}, {-13, 13}}, color = {0, 0, 127})); connect(add.u1, alphaSin.y) annotation( Line(points = {{7, 26}, {3.5, 26}, {3.5, 30}, {-4, 30}}, color = {0, 0, 127})); connect(betaCos.y, add.u2) annotation( Line(points = {{-6, 16}, {4, 16}, {4, 22}, {7, 22}}, color = {0, 0, 127})); connect(pi.u, add.y) annotation( Line(points = {{19, 24}, {16, 24}}, color = {0, 0, 127})); connect(add_freq_nom_delta_f.u1, pi.y) annotation( Line(points = {{43, 24}, {33, 24}}, color = {0, 0, 127})); connect(f_nom.y, add_freq_nom_delta_f.u2) annotation( Line(points = {{32, 4}, {36, 4}, {36, 20}, {43, 20}}, color = {0, 0, 127})); connect(f2theta.u, add_freq_nom_delta_f.y) annotation( Line(points = {{59, 22}, {52, 22}}, color = {0, 0, 127})); connect(deg2rad.u, f2theta.y) annotation( Line(points = {{74, 22}, {68, 22}, {68, 22}, {68, 22}}, color = {0, 0, 127})); connect(deg2rad.y, sin.u) annotation( Line(points = {{82, 22}, {92, 22}, {92, -6}, {-6, -6}, {-6, -6}}, color = {0, 0, 127})); connect(cos.u, deg2rad.y) annotation( Line(points = {{-6, -18}, {4, -18}, {4, -6}, {92, -6}, {92, 22}, {82, 22}, {82, 22}, {82, 22}}, color = {0, 0, 127})); end pll; end plls; package transforms model abc2AlphaBeta Modelica.Blocks.Interfaces.RealInput a annotation( 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))); Modelica.Blocks.Interfaces.RealInput b annotation( 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))); Modelica.Blocks.Interfaces.RealInput c annotation( 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))); Modelica.Blocks.Math.Gain gain(k = 2 / 3) annotation( Placement(visible = true, transformation(origin = {-40, 78}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); Modelica.Blocks.Math.Gain gain1(k = -1 / 3) annotation( Placement(visible = true, transformation(origin = {-39, 55}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.Gain gain2(k = -1 / 3) annotation( Placement(visible = true, transformation(origin = {-39, 29}, extent = {{-7, -7}, {7, 7}}, rotation = 0))); Modelica.Blocks.Math.MultiSum multiSum(nu = 3) annotation( Placement(visible = true, transformation(origin = {58, 66}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Math.Gain gain3(k = -1 / sqrt(3)) annotation( Placement(visible = true, transformation(origin = {-32, -18}, extent = {{-14, -14}, {14, 14}}, rotation = 0))); Modelica.Blocks.Math.Gain gain4(k = 1 / sqrt(3)) annotation( Placement(visible = true, transformation(origin = {-32, -64}, extent = {{-14, -14}, {14, 14}}, rotation = 0))); Modelica.Blocks.Math.MultiSum multiSum1(nu = 2) annotation( Placement(visible = true, transformation(origin = {48, -34}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealOutput alpha annotation( 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))); Modelica.Blocks.Interfaces.RealOutput beta annotation( 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))); equation connect(a, gain.u) annotation( Line(points = {{-104, 40}, {-77, 40}, {-77, 78}, {-48, 78}}, color = {0, 0, 127})); connect(gain1.u, b) annotation( Line(points = {{-48, 56}, {-71, 56}, {-71, 12}, {-104, 12}}, color = {0, 0, 127})); connect(c, gain2.u) annotation( Line(points = {{-104, -18}, {-62, -18}, {-62, 30}, {-48, 30}}, color = {0, 0, 127})); connect(gain.y, multiSum.u[1]) annotation( Line(points = {{-34, 78}, {48, 78}, {48, 66}}, color = {0, 0, 127})); connect(gain1.y, multiSum.u[2]) annotation( Line(points = {{-32, 56}, {48, 56}, {48, 66}, {48, 66}}, color = {0, 0, 127})); connect(gain2.y, multiSum.u[3]) annotation( Line(points = {{-32, 30}, {48, 30}, {48, 66}, {48, 66}}, color = {0, 0, 127})); connect(gain4.u, b) annotation( Line(points = {{-48, -64}, {-73, -64}, {-73, -62}, {-72, -62}, {-72, 12}, {-104, 12}}, color = {0, 0, 127})); connect(gain3.u, c) annotation( Line(points = {{-48, -18}, {-96, -18}, {-96, -18}, {-104, -18}}, color = {0, 0, 127})); connect(gain3.y, multiSum1.u[1]) annotation( Line(points = {{-16, -18}, {38, -18}, {38, -34}, {38, -34}}, color = {0, 0, 127})); connect(gain4.y, multiSum1.u[2]) annotation( Line(points = {{-16, -64}, {38, -64}, {38, -34}, {38, -34}}, color = {0, 0, 127})); connect(multiSum.y, alpha) annotation( Line(points = {{70, 66}, {96, 66}, {96, 64}, {102, 64}}, color = {0, 0, 127})); connect(multiSum1.y, beta) annotation( Line(points = {{60, -34}, {102, -34}}, color = {0, 0, 127})); end abc2AlphaBeta; end transforms; model network grid.inverters.inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.ideal_filter.lc lc1 annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.inverters.inverter inverter2 annotation( Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); ideal_filter.lcl lcl1 annotation( Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.loads.rl rl1 annotation( Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); ideal_filter.lc lc2 annotation( Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(inverter1.pin3, lc1.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, lc1.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, lc1.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255})); connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(i2p3, inverter2.u3) annotation( Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127})); connect(i2p2, inverter2.u2) annotation( Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127})); connect(i2p1, inverter2.u1) annotation( Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127})); connect(inverter2.pin3, lcl1.pin3) annotation( Line(points = {{-60, -24}, {-42, -24}, {-42, -24}, {-42, -24}}, color = {0, 0, 255})); connect(inverter2.pin2, lcl1.pin2) annotation( Line(points = {{-60, -30}, {-42, -30}, {-42, -30}, {-42, -30}}, color = {0, 0, 255})); connect(inverter2.pin1, lcl1.pin1) annotation( Line(points = {{-60, -36}, {-42, -36}, {-42, -36}, {-42, -36}}, color = {0, 0, 255})); connect(lc2.pin4, rl1.pin1) annotation( Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255})); connect(lc2.pin5, rl1.pin2) annotation( Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255})); connect(lc2.pin6, rl1.pin3) annotation( Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255})); connect(lc1.pin6, lc2.pin3) annotation( Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255})); connect(lc1.pin5, lc2.pin2) annotation( Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255})); connect(lc1.pin4, lc2.pin1) annotation( Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255})); connect(lcl1.pin4, lc2.pin1) annotation( Line(points = {{-22, -36}, {6, -36}, {6, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255})); connect(lcl1.pin5, lc2.pin2) annotation( Line(points = {{-22, -30}, {0, -30}, {0, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255})); connect(lcl1.pin6, lc2.pin3) annotation( Line(points = {{-22, -24}, {-6, -24}, {-6, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255})); annotation( Diagram); end network; model pll_network grid.inverters.inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.ideal_filter.lc lc1 annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.loads.rc rc1 annotation( Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.inverters.inverter inverter2 annotation( Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.ideal_filter.lc lc2 annotation( Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); ideal_filter.lcl lcl1 annotation( Placement(visible = true, transformation(origin = {-30, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.plls.pll pll annotation( Placement(visible = true, transformation(origin = {20, -62}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(lc2.pin4, rc1.pin1) annotation( Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255})); connect(lc2.pin5, rc1.pin2) annotation( Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255})); connect(lc2.pin6, rc1.pin3) annotation( Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255})); connect(lc1.pin6, lc2.pin3) annotation( Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255})); connect(lc1.pin5, lc2.pin2) annotation( Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255})); connect(lc1.pin4, lc2.pin1) annotation( Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255})); connect(inverter1.pin3, lc1.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, lc1.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, lc1.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255})); connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(i2p3, inverter2.u3) annotation( Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127})); connect(i2p2, inverter2.u2) annotation( Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127})); connect(i2p1, inverter2.u1) annotation( Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127})); connect(inverter2.pin3, lcl1.pin3) annotation( Line(points = {{-60, -24}, {-40, -24}, {-40, -24}, {-40, -24}}, color = {0, 0, 255})); connect(inverter2.pin2, lcl1.pin2) annotation( Line(points = {{-60, -30}, {-40, -30}, {-40, -30}, {-40, -30}}, color = {0, 0, 255})); connect(inverter2.pin1, lcl1.pin1) annotation( Line(points = {{-60, -36}, {-40, -36}, {-40, -36}, {-40, -36}}, color = {0, 0, 255})); connect(lcl1.pin6, lc2.pin3) annotation( Line(points = {{-20, -24}, {-4, -24}, {-4, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255})); connect(lcl1.pin5, lc2.pin2) annotation( Line(points = {{-20, -30}, {0, -30}, {0, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255})); connect(lcl1.pin4, lc2.pin1) annotation( Line(points = {{-20, -36}, {6, -36}, {6, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255})); connect(pll.a, lcl1.pin6) annotation( Line(points = {{10, -58}, {-14, -58}, {-14, -24}, {-20, -24}}, color = {0, 0, 255})); connect(pll.b, lcl1.pin5) annotation( Line(points = {{10, -60}, {-16, -60}, {-16, -30}, {-20, -30}}, color = {0, 0, 255})); connect(pll.c, lcl1.pin4) annotation( Line(points = {{10, -63}, {-18, -63}, {-18, -36}, {-20, -36}}, color = {0, 0, 255})); annotation( Diagram); end pll_network; model rlc_network grid.inverters.inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.ideal_filter.lc lc1 annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.inverters.inverter inverter2 annotation( Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.ideal_filter.lc lc2 annotation( Placement(visible = true, transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); ideal_filter.lcl lcl1 annotation( Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.loads.rlc rlc1 annotation( Placement(visible = true, transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(lc1.pin6, lc2.pin3) annotation( Line(points = {{-20, 36}, {20, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255})); connect(lc1.pin5, lc2.pin2) annotation( Line(points = {{-20, 30}, {20, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255})); connect(lc1.pin4, lc2.pin1) annotation( Line(points = {{-20, 24}, {20, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255})); connect(inverter1.pin3, lc1.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, lc1.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, lc1.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255})); connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(i2p3, inverter2.u3) annotation( Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127})); connect(i2p2, inverter2.u2) annotation( Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127})); connect(i2p1, inverter2.u1) annotation( Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127})); connect(inverter2.pin3, lcl1.pin3) annotation( Line(points = {{-60, -24}, {-42, -24}, {-42, -24}, {-42, -24}}, color = {0, 0, 255})); connect(inverter2.pin2, lcl1.pin2) annotation( Line(points = {{-60, -30}, {-42, -30}, {-42, -30}, {-42, -30}}, color = {0, 0, 255})); connect(inverter2.pin1, lcl1.pin1) annotation( Line(points = {{-60, -36}, {-42, -36}, {-42, -36}, {-42, -36}}, color = {0, 0, 255})); connect(lcl1.pin6, lc2.pin3) annotation( Line(points = {{-22, -24}, {-6, -24}, {-6, 36}, {20, 36}, {20, 36}}, color = {0, 0, 255})); connect(lcl1.pin5, lc2.pin2) annotation( Line(points = {{-22, -30}, {0, -30}, {0, 30}, {20, 30}, {20, 30}}, color = {0, 0, 255})); connect(lcl1.pin4, lc2.pin1) annotation( Line(points = {{-22, -36}, {6, -36}, {6, 24}, {20, 24}, {20, 24}}, color = {0, 0, 255})); connect(lc2.pin4, rlc1.pin1) annotation( Line(points = {{40, 24}, {60, 24}, {60, 24}, {60, 24}}, color = {0, 0, 255})); connect(lc2.pin5, rlc1.pin2) annotation( Line(points = {{40, 30}, {60, 30}, {60, 30}, {60, 30}}, color = {0, 0, 255})); connect(lc2.pin6, rlc1.pin3) annotation( Line(points = {{40, 36}, {60, 36}, {60, 36}, {60, 36}}, color = {0, 0, 255})); annotation( Diagram); end rlc_network; model pll_Test transforms.abc2AlphaBeta abc2AlphaBeta annotation( Placement(visible = true, transformation(origin = {-14, 4}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine(amplitude = 230 * 1.414, freqHz = 50) annotation( Placement(visible = true, transformation(origin = {-90, 34}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine1(amplitude = 230 * 1.414, freqHz = 50, phase = -2.0944) annotation( Placement(visible = true, transformation(origin = {-88, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine2(amplitude = 230 * 1.414, freqHz = 50, phase = -4.18879) annotation( Placement(visible = true, transformation(origin = {-88, -26}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.inverters.inverter inverter annotation( Placement(visible = true, transformation(origin = {-14, 58}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.plls.pll pll annotation( Placement(visible = true, transformation(origin = {28, 56}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(abc2AlphaBeta.a, sine.y) annotation( Line(points = {{-24, 8}, {-44, 8}, {-44, 34}, {-78, 34}, {-78, 34}}, color = {0, 0, 127})); connect(abc2AlphaBeta.b, sine1.y) annotation( Line(points = {{-24, 6}, {-77, 6}}, color = {0, 0, 127})); connect(sine2.y, abc2AlphaBeta.c) annotation( Line(points = {{-76, -26}, {-44, -26}, {-44, 2}, {-24, 2}, {-24, 2}}, color = {0, 0, 127})); connect(inverter.u3, sine.y) annotation( Line(points = {{-24, 64}, {-70, 64}, {-70, 34}, {-78, 34}}, color = {0, 0, 127})); connect(inverter.u2, sine1.y) annotation( Line(points = {{-24, 58}, {-60, 58}, {-60, 6}, {-76, 6}}, color = {0, 0, 127})); connect(inverter.u1, sine2.y) annotation( Line(points = {{-24, 52}, {-54, 52}, {-54, -26}, {-76, -26}}, color = {0, 0, 127})); connect(pll.a, inverter.pin3) annotation( Line(points = {{18, 60}, {4, 60}, {4, 64}, {-4, 64}, {-4, 64}}, color = {0, 0, 255})); connect(pll.b, inverter.pin2) annotation( Line(points = {{18, 58}, {-4, 58}, {-4, 58}, {-4, 58}}, color = {0, 0, 255})); connect(pll.c, inverter.pin1) annotation( Line(points = {{18, 54}, {4, 54}, {4, 52}, {-4, 52}, {-4, 52}}, color = {0, 0, 255})); end pll_Test; model sine_Test Modelica.Blocks.Sources.Sine sine(amplitude = 230, freqHz = 50) annotation( Placement(visible = true, transformation(origin = {-76, -82}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine1(amplitude = 230, freqHz = 50, phase = 2.0944) annotation( Placement(visible = true, transformation(origin = {-76, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Sine sine2(amplitude = 230, freqHz = 50, phase = 4.18879) annotation( Placement(visible = true, transformation(origin = {-76, -16}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.loads.rl rl(L1 = 0.002, L2 = 0.002, L3 = 0.002, R1 = 1, R2 = 1, R3 = 1) annotation( Placement(visible = true, transformation(origin = {64, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 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( Placement(visible = true, transformation(origin = {14, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.inverters.inverter inverter1(v_DC = 60) annotation( Placement(visible = true, transformation(origin = {-24, -48}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(lc.pin5, rl.pin2) annotation( Line(points = {{24, -48}, {54, -48}, {54, -48}, {54, -48}}, color = {0, 0, 255})); connect(lc.pin4, rl.pin1) annotation( Line(points = {{24, -54}, {54, -54}, {54, -54}, {54, -54}}, color = {0, 0, 255})); connect(lc.pin3, inverter1.pin3) annotation( Line(points = {{4, -42}, {-14, -42}, {-14, -42}, {-14, -42}}, color = {0, 0, 255})); connect(lc.pin6, rl.pin3) annotation( Line(points = {{24, -42}, {52, -42}, {52, -42}, {54, -42}}, color = {0, 0, 255})); connect(lc.pin2, inverter1.pin2) annotation( Line(points = {{4, -48}, {-14, -48}, {-14, -48}, {-14, -48}}, color = {0, 0, 255})); connect(lc.pin1, inverter1.pin1) annotation( Line(points = {{4, -54}, {-14, -54}, {-14, -54}, {-14, -54}}, color = {0, 0, 255})); connect(inverter1.u2, sine1.y) annotation( Line(points = {{-34, -48}, {-64, -48}, {-64, -48}, {-64, -48}}, color = {0, 0, 127})); connect(inverter1.u3, sine2.y) annotation( Line(points = {{-34, -42}, {-48, -42}, {-48, -16}, {-64, -16}, {-64, -16}}, color = {0, 0, 127})); connect(inverter1.u1, sine.y) annotation( Line(points = {{-34, -54}, {-48, -54}, {-48, -82}, {-64, -82}, {-64, -82}}, color = {0, 0, 127})); end sine_Test; model network_singleInverter grid.inverters.inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.ideal_filter.lc lc1(C1 = 0.00002, C2 = 0.00002, C3 = 0.00002, L1 = 0.002, L2 = 0.002, L3 = 0.002) annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); grid.loads.rl rl1 annotation( Placement(visible = true, transformation(origin = {24, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(inverter1.pin3, lc1.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, lc1.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, lc1.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255})); connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(lc1.pin6, rl1.pin3) annotation( Line(points = {{-20, 36}, {14, 36}}, color = {0, 0, 255})); connect(lc1.pin5, rl1.pin2) annotation( Line(points = {{-20, 30}, {14, 30}}, color = {0, 0, 255})); connect(lc1.pin4, rl1.pin1) annotation( Line(points = {{-20, 24}, {14, 24}}, color = {0, 0, 255})); annotation( Diagram); end network_singleInverter; model singleModel grid.inverters.inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.inverters.inverter inverter2 annotation( Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); grid.loads.r r(R1 = 100, R2 = 100, R3 = 100) annotation( Placement(visible = true, transformation(origin = {8, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.ideal_filter.l l annotation( Placement(visible = true, transformation(origin = {-40, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(i2p3, inverter2.u3) annotation( Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127})); connect(i2p2, inverter2.u2) annotation( Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127})); connect(i2p1, inverter2.u1) annotation( Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127})); connect(inverter1.pin3, r.pin3) annotation( Line(points = {{-60, 36}, {-2, 36}, {-2, 36}, {-2, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, r.pin2) annotation( Line(points = {{-60, 30}, {-2, 30}, {-2, 30}, {-2, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, r.pin1) annotation( Line(points = {{-60, 24}, {-2, 24}, {-2, 24}, {-2, 24}}, color = {0, 0, 255})); connect(inverter2.pin3, l.pin3) annotation( Line(points = {{-60, -24}, {-50, -24}}, color = {0, 0, 255})); connect(inverter2.pin2, l.pin2) annotation( Line(points = {{-60, -30}, {-50, -30}}, color = {0, 0, 255})); connect(inverter2.pin1, l.pin1) annotation( Line(points = {{-60, -36}, {-50, -36}}, color = {0, 0, 255})); connect(l.pin6, r.pin3) annotation( Line(points = {{-30, -24}, {-20, -24}, {-20, 36}, {-2, 36}, {-2, 36}, {-2, 36}}, color = {0, 0, 255})); connect(l.pin5, r.pin2) annotation( Line(points = {{-30, -30}, {-14, -30}, {-14, 30}, {-2, 30}, {-2, 30}}, color = {0, 0, 255})); connect(l.pin4, r.pin1) annotation( Line(points = {{-30, -36}, {-8, -36}, {-8, 24}, {-2, 24}, {-2, 24}, {-2, 24}}, color = {0, 0, 255})); annotation( Diagram); end singleModel; model microgrid grid.inverters.inverter inverter1 annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.ideal_filter.lc lc1 annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.inverters.inverter inverter2 annotation( Placement(visible = true, transformation(origin = {-70, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i2p3 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); ideal_filter.lcl lcl1 annotation( Placement(visible = true, transformation(origin = {-32, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.loads.rl rl1 annotation( Placement(visible = true, transformation(origin = {92, 2}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.ideal_filter.l l12 annotation( Placement(visible = true, transformation(origin = {2, 0}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); grid.ideal_filter.l l13 annotation( Placement(visible = true, transformation(origin = {46, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.ideal_filter.l l23 annotation( Placement(visible = true, transformation(origin = {48, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(inverter1.pin3, lc1.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, lc1.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, lc1.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}}, color = {0, 0, 255})); connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(i2p3, inverter2.u3) annotation( Line(points = {{-104, -18}, {-88, -18}, {-88, -24}, {-80, -24}, {-80, -24}}, color = {0, 0, 127})); connect(i2p2, inverter2.u2) annotation( Line(points = {{-104, -30}, {-80, -30}, {-80, -30}, {-80, -30}}, color = {0, 0, 127})); connect(i2p1, inverter2.u1) annotation( Line(points = {{-104, -42}, {-90, -42}, {-90, -36}, {-80, -36}, {-80, -36}}, color = {0, 0, 127})); connect(inverter2.pin3, lcl1.pin3) annotation( Line(points = {{-60, -24}, {-42, -24}, {-42, -24}, {-42, -24}}, color = {0, 0, 255})); connect(inverter2.pin2, lcl1.pin2) annotation( Line(points = {{-60, -30}, {-42, -30}, {-42, -30}, {-42, -30}}, color = {0, 0, 255})); connect(inverter2.pin1, lcl1.pin1) annotation( Line(points = {{-60, -36}, {-42, -36}, {-42, -36}, {-42, -36}}, color = {0, 0, 255})); connect(lc1.pin6, l12.pin3) annotation( Line(points = {{-20, 36}, {8, 36}, {8, 10}}, color = {0, 0, 255})); connect(lc1.pin5, l12.pin2) annotation( Line(points = {{-20, 30}, {2, 30}, {2, 10}}, color = {0, 0, 255})); connect(lc1.pin4, l12.pin1) annotation( Line(points = {{-20, 24}, {-4, 24}, {-4, 10}}, color = {0, 0, 255})); connect(l12.pin6, lcl1.pin6) annotation( Line(points = {{8, -10}, {8, -24}, {-22, -24}}, color = {0, 0, 255})); connect(l12.pin5, lcl1.pin5) annotation( Line(points = {{2, -10}, {2, -30}, {-22, -30}}, color = {0, 0, 255})); connect(l12.pin4, lcl1.pin4) annotation( Line(points = {{-4, -10}, {-4, -36}, {-22, -36}}, color = {0, 0, 255})); connect(l13.pin3, lc1.pin6) annotation( Line(points = {{36, 36}, {-20, 36}}, color = {0, 0, 255})); connect(l13.pin2, lc1.pin5) annotation( Line(points = {{36, 30}, {-20, 30}, {-20, 30}, {-20, 30}, {-20, 30}}, color = {0, 0, 255})); connect(l13.pin1, lc1.pin4) annotation( Line(points = {{36, 24}, {-20, 24}, {-20, 24}, {-20, 24}}, color = {0, 0, 255})); connect(l23.pin3, lcl1.pin6) annotation( Line(points = {{38, -24}, {-22, -24}, {-22, -24}, {-22, -24}}, color = {0, 0, 255})); connect(l23.pin2, lcl1.pin5) annotation( Line(points = {{38, -30}, {-22, -30}, {-22, -30}, {-22, -30}}, color = {0, 0, 255})); connect(l23.pin1, lcl1.pin4) annotation( Line(points = {{38, -36}, {-22, -36}, {-22, -36}, {-22, -36}}, color = {0, 0, 255})); connect(l13.pin6, rl1.pin3) annotation( Line(points = {{56, 36}, {72, 36}, {72, 8}, {82, 8}, {82, 8}}, color = {0, 0, 255})); connect(l13.pin5, rl1.pin2) annotation( Line(points = {{56, 30}, {66, 30}, {66, 2}, {82, 2}, {82, 2}}, color = {0, 0, 255})); connect(l13.pin4, rl1.pin1) annotation( Line(points = {{56, 24}, {62, 24}, {62, -4}, {82, -4}, {82, -4}}, color = {0, 0, 255})); connect(l23.pin5, rl1.pin2) annotation( Line(points = {{58, -30}, {66, -30}, {66, 2}, {82, 2}, {82, 2}}, color = {0, 0, 255})); connect(l23.pin6, rl1.pin3) annotation( Line(points = {{58, -24}, {72, -24}, {72, 8}, {82, 8}, {82, 8}}, color = {0, 0, 255})); connect(l23.pin4, rl1.pin1) annotation( Line(points = {{58, -36}, {62, -36}, {62, -4}, {82, -4}, {82, -4}}, color = {0, 0, 255})); annotation( Diagram); end microgrid; model testbench_SC2 grid.inverters.inverter inverter1(v_DC = 60) annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); grid.filter.l rl(L1 = 0.0023, L2 = 0.0023, L3 = 0.0023, R1 = 0.170, R2 = 0.170, R3 = 0.170) annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(inverter1.pin3, rl.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}, {-40, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, rl.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}, {-40, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, rl.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}, {-40, 24}, {-40, 24}}, color = {0, 0, 255})); connect(rl.pin6, rl.pin4) annotation( Line(points = {{-20, 36}, {-14, 36}, {-14, 24}, {-20, 24}, {-20, 24}}, color = {0, 0, 255})); connect(rl.pin5, rl.pin6) annotation( Line(points = {{-20, 30}, {-14, 30}, {-14, 36}, {-20, 36}, {-20, 36}}, color = {0, 0, 255})); connect(rl.pin4, rl.pin5) annotation( Line(points = {{-20, 24}, {-14, 24}, {-14, 30}, {-20, 30}, {-20, 30}}, color = {0, 0, 255})); annotation( Diagram); end testbench_SC2; model paper grid.inverters.inverter inverter1(v_DC = 60) annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); 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( Placement(visible = true, transformation(origin = {-32, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Ground ground5 annotation( Placement(visible = true, transformation(origin = {6, 14}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(lc.pin3, inverter1.pin3) annotation( Line(points = {{-42, 36}, {-60, 36}, {-60, 36}, {-60, 36}}, color = {0, 0, 255})); connect(lc.pin2, inverter1.pin2) annotation( Line(points = {{-42, 30}, {-60, 30}, {-60, 30}, {-60, 30}}, color = {0, 0, 255})); connect(lc.pin1, inverter1.pin1) annotation( Line(points = {{-42, 24}, {-60, 24}, {-60, 24}, {-60, 24}}, color = {0, 0, 255})); connect(lc.pin6, ground5.p) annotation( Line(points = {{-22, 36}, {6, 36}, {6, 24}, {6, 24}}, color = {0, 0, 255})); connect(lc.pin5, ground5.p) annotation( Line(points = {{-22, 30}, {6, 30}, {6, 24}, {6, 24}}, color = {0, 0, 255})); connect(lc.pin4, ground5.p) annotation( Line(points = {{-22, 24}, {6, 24}, {6, 24}, {6, 24}}, color = {0, 0, 255})); annotation( Diagram); end paper; model paper_loadstep grid.inverters.inverter inverter1(v_DC = 60) annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); 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( Placement(visible = true, transformation(origin = {-32, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.loads.r r_load(R1 = 7.15, R2 = 7.15, R3 = 7.15) annotation( Placement(visible = true, transformation(origin = {10, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(lc.pin3, inverter1.pin3) annotation( Line(points = {{-42, 36}, {-60, 36}, {-60, 36}, {-60, 36}}, color = {0, 0, 255})); connect(lc.pin2, inverter1.pin2) annotation( Line(points = {{-42, 30}, {-60, 30}, {-60, 30}, {-60, 30}}, color = {0, 0, 255})); connect(lc.pin1, inverter1.pin1) annotation( Line(points = {{-42, 24}, {-60, 24}, {-60, 24}, {-60, 24}}, color = {0, 0, 255})); connect(r_load.pin3, lc.pin6) annotation( Line(points = {{0, 36}, {-22, 36}, {-22, 36}, {-22, 36}}, color = {0, 0, 255})); connect(r_load.pin2, lc.pin5) annotation( Line(points = {{0, 30}, {-22, 30}, {-22, 30}, {-22, 30}}, color = {0, 0, 255})); connect(r_load.pin1, lc.pin4) annotation( Line(points = {{0, 24}, {-22, 24}, {-22, 24}, {-22, 24}}, color = {0, 0, 255})); annotation( Diagram); end paper_loadstep; model paper_stepfuncion grid.inverters.inverter inverter1(v_DC = 60) annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 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( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Electrical.Analog.Basic.Ground ground5 annotation( Placement(visible = true, transformation(origin = {6, 14}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Step step annotation( Placement(visible = true, transformation(origin = {-122, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(lc.pin3, inverter1.pin3) annotation( Line(points = {{-40, 36}, {-60, 36}}, color = {0, 0, 255})); connect(lc.pin2, inverter1.pin2) annotation( Line(points = {{-40, 30}, {-60, 30}}, color = {0, 0, 255})); connect(lc.pin1, inverter1.pin1) annotation( Line(points = {{-40, 24}, {-60, 24}}, color = {0, 0, 255})); connect(lc.pin6, ground5.p) annotation( Line(points = {{-20, 36}, {-7, 36}, {-7, 24}, {6, 24}}, color = {0, 0, 255})); connect(lc.pin5, ground5.p) annotation( Line(points = {{-20, 30}, {-5, 30}, {-5, 24}, {6, 24}}, color = {0, 0, 255})); connect(lc.pin4, ground5.p) annotation( Line(points = {{-20, 24}, {6, 24}}, color = {0, 0, 255})); connect(step.y, inverter1.u2) annotation( Line(points = {{-110, 44}, {-94, 44}, {-94, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(inverter1.u3, step.y) annotation( Line(points = {{-80, 36}, {-108, 36}, {-108, 44}, {-110, 44}, {-110, 44}}, color = {0, 0, 127})); connect(inverter1.u1, step.y) annotation( Line(points = {{-80, 24}, {-104, 24}, {-104, 44}, {-112, 44}, {-112, 44}, {-110, 44}}, color = {0, 0, 127})); annotation( Diagram); end paper_stepfuncion; model step Modelica.Blocks.Sources.Step step annotation( Placement(visible = true, transformation(origin = {-22, 34}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation annotation( Diagram); end step; model paper_loadstep_stepfu grid.inverters.inverter inverter1(v_DC = 60) annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 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( Placement(visible = true, transformation(origin = {-32, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Sources.Step step annotation( Placement(visible = true, transformation(origin = {-104, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); loads.r r_load(R1 = 7.15, R2 = 7.15, R3 = 7.15) annotation( Placement(visible = true, transformation(origin = {10, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(lc.pin3, inverter1.pin3) annotation( Line(points = {{-42, 36}, {-60, 36}, {-60, 36}, {-60, 36}}, color = {0, 0, 255})); connect(lc.pin2, inverter1.pin2) annotation( Line(points = {{-42, 30}, {-60, 30}, {-60, 30}, {-60, 30}}, color = {0, 0, 255})); connect(lc.pin1, inverter1.pin1) annotation( Line(points = {{-42, 24}, {-60, 24}, {-60, 24}, {-60, 24}}, color = {0, 0, 255})); connect(step.y, inverter1.u3) annotation( Line(points = {{-92, 44}, {-86, 44}, {-86, 36}, {-80, 36}, {-80, 36}}, color = {0, 0, 127})); connect(inverter1.u2, step.y) annotation( Line(points = {{-80, 30}, {-86, 30}, {-86, 44}, {-92, 44}, {-92, 44}}, color = {0, 0, 127})); connect(inverter1.u1, step.y) annotation( Line(points = {{-80, 24}, {-86, 24}, {-86, 44}, {-92, 44}, {-92, 44}, {-92, 44}}, color = {0, 0, 127})); connect(r_load.pin1, lc.pin4) annotation( Line(points = {{0, 24}, {-22, 24}, {-22, 24}, {-22, 24}}, color = {0, 0, 255})); connect(r_load.pin2, lc.pin5) annotation( Line(points = {{0, 30}, {-22, 30}, {-22, 30}, {-22, 30}}, color = {0, 0, 255})); connect(r_load.pin3, lc.pin6) annotation( Line(points = {{0, 36}, {-22, 36}, {-22, 36}, {-22, 36}}, color = {0, 0, 255})); annotation( Diagram); end paper_loadstep_stepfu; model testbench_SC_load grid.inverters.inverter inverter1(v_DC = 60) annotation( Placement(visible = true, transformation(origin = {-70, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Blocks.Interfaces.RealInput i1p1 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p2 annotation( 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))); Modelica.Blocks.Interfaces.RealInput i1p3 annotation( 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))); grid.filter.l rl(L1 = 0.0023, L2 = 0.0023, L3 = 0.0023, R1 = 0.170, R2 = 0.170, R3 = 0.170) annotation( Placement(visible = true, transformation(origin = {-30, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); grid.loads.r r(R1 = 20, R2 = 20, R3 = 20) annotation( Placement(visible = true, transformation(origin = {2, 30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); equation connect(i1p1, inverter1.u1) annotation( Line(points = {{-104, 18}, {-86, 18}, {-86, 24}, {-80, 24}, {-80, 24}}, color = {0, 0, 127})); connect(i1p2, inverter1.u2) annotation( Line(points = {{-104, 30}, {-80, 30}, {-80, 30}, {-80, 30}}, color = {0, 0, 127})); connect(i1p3, inverter1.u3) annotation( Line(points = {{-104, 42}, {-86, 42}, {-86, 36}, {-80, 36}}, color = {0, 0, 127})); connect(inverter1.pin3, rl.pin3) annotation( Line(points = {{-60, 36}, {-40, 36}, {-40, 36}, {-40, 36}}, color = {0, 0, 255})); connect(inverter1.pin2, rl.pin2) annotation( Line(points = {{-60, 30}, {-40, 30}, {-40, 30}, {-40, 30}}, color = {0, 0, 255})); connect(inverter1.pin1, rl.pin1) annotation( Line(points = {{-60, 24}, {-40, 24}, {-40, 24}, {-40, 24}}, color = {0, 0, 255})); connect(rl.pin6, r.pin3) annotation( Line(points = {{-20, 36}, {-8, 36}}, color = {0, 0, 255})); connect(rl.pin5, r.pin2) annotation( Line(points = {{-20, 30}, {-8, 30}}, color = {0, 0, 255})); connect(rl.pin4, r.pin1) annotation( Line(points = {{-20, 24}, {-8, 24}}, color = {0, 0, 255})); annotation( Diagram); end testbench_SC_load; annotation( uses(Modelica(version = "3.2.3"))); end grid; ================================================ FILE: omg_grid/merge_fmus.py ================================================ from os import listdir, walk, chdir from os.path import join, basename, isdir from shutil import copytree from tempfile import TemporaryDirectory from tkinter.filedialog import askopenfilenames, Tk, asksaveasfilename from zipfile import ZipFile import xml.etree.ElementTree as ET import re from more_itertools import collapse def get_fmu_key(path): """some identifier to derive a unique identifier from the fmu""" root = ET.parse(join(path, "modelDescription.xml")).getroot() s = '' # root.attrib['guid'] s += ET.tostring(root.find('ModelVariables')).decode('utf-8') s += ET.tostring(root.find('ModelStructure')).decode('utf-8') # replace all floats s = re.sub('"[-+]?(\d+([.,]\d*)?|[.,]\d+)([eE][-+]?\d+)?"', lambda f: str(float(f[0][1:-1])), s) return s def copy_binaries(wd, fmus, binaries): primary = basename(fmus[0]) for fmu, binaries_ in zip(fmus[1:], binaries[1:]): for binary in binaries_: copytree(join(wd, basename(fmu), 'binaries', binary), join(wd, primary, 'binaries', binary)) if __name__ == '__main__': tk = Tk() tk.withdraw() fmus = askopenfilenames(title='Select FMUs to merge', filetypes=['Functional\u00A0Mockup\u00A0Unit {*.fmu}']) if len(fmus) < 2: raise ValueError('Please select multiple FMUs') # create tmp directory with TemporaryDirectory() as dir: # unpack fmus to tmp directory binaries = [] for fmu in fmus: with ZipFile(fmu, 'r') as z: z.extractall(join(dir, basename(fmu))) # add all subdirectories of "binaries" in the zip files binaries.append(list(filter(lambda f: not isdir(f), listdir(join(dir, basename(fmu), "binaries"))))) # check if all fmus provide different binaries (binary/*) if len(set(collapse(binaries))) != len(list(collapse(binaries))): raise ValueError(f'The provided binary folders are not unique: {list(zip(fmus, binaries))}') # check if their xmls are sufficiently similar if len(set([get_fmu_key(join(dir, basename(fmu))) for fmu in fmus])) > 1: raise ValueError('The FMUs appear to be generated from different model files.' + str([get_fmu_key(join(dir, basename(fmu))) for fmu in fmus])) # copy content of binary folder from secondary fmus to the folder of the primary copy_binaries(dir, fmus, binaries) # change the working directory into the primary fmu folder which we want to package chdir(join(dir, basename(fmus[0]))) # zip the primary folder, delete tempfolder with ZipFile(asksaveasfilename(filetypes=['Functional\u00A0Mockup\u00A0Unit {*.fmu}']), 'w') as zipObj: # Iterate over all the files in directory for folderName, subfolders, filenames in walk('.'): for filename in filenames: # Add file to zip zipObj.write(join(folderName, filename)) ================================================ FILE: omg_grid/package.mo ================================================ within ; package omg_grid extends Modelica.Icons.Package; // Import mathematical constants and functions import SI = Modelica.SIunits; import Modelica.Constants.pi; import Modelica.Math; import Modelica.ComplexMath.'abs'; import Modelica.ComplexMath.arg; import Modelica.ComplexMath.fromPolar; import Modelica.ComplexMath.real; import Modelica.ComplexMath.imag; import Modelica.ComplexMath.conj; import Modelica.ComplexMath.j; annotation ( preferredView="info", uses(Modelica(version="3.2.3"), Complex(version="3.2.3"), ModelicaServices(version = "3.2.3")), Documentation(info="

omg_grid is a free package that is developed with the Modelica® language from the Modelica Association, see https://www.Modelica.org.

It was designed in the scope of the OpenModelica Microgrid Gym (OMG) project see https://github.com/upb-lea/openmodelica-microgrid-gym

It provides model components for microgrids.

Licensed under the Modelica License 2
Copyright © 2020, chair of Power Electronics and Electrical Drives, Paderborn University.

This Modelica package is free software and the use is completely at your own risk; it can be redistributed and/or modified under the terms of the Modelica License 2. For license conditions (including the disclaimer of warranty) see Modelica.UsersGuide.ModelicaLicense2 or visit https://www.modelica.org/licenses/ModelicaLicense2.

")); end omg_grid; ================================================ FILE: omg_grid/package.order ================================================ UsersGuide Filter Loads ActiveLoads Inverters PLLs Transformations Components Grids Examples ================================================ FILE: openmodelica_microgrid_gym/__init__.py ================================================ import logging from gym.envs.registration import register from openmodelica_microgrid_gym.agents import Agent from openmodelica_microgrid_gym.env import ModelicaEnv from openmodelica_microgrid_gym.execution import Runner __all__ = ['Agent', 'Runner'] __version__ = '0.4.0' register( id='ModelicaEnv_test-v1', entry_point='openmodelica_microgrid_gym.env:ModelicaEnv', kwargs=dict(log_level=logging.DEBUG, max_episode_steps=500, viz_mode='episode', is_normalized=False) ) register( id='ModelicaEnv-v1', entry_point='openmodelica_microgrid_gym.env:ModelicaEnv', kwargs=dict(is_normalized=False) ) ================================================ FILE: openmodelica_microgrid_gym/agents/__init__.py ================================================ from openmodelica_microgrid_gym.agents.agent import Agent from openmodelica_microgrid_gym.agents.safeopt import SafeOptAgent from openmodelica_microgrid_gym.agents.staticctrl import StaticControlAgent __all__ = ['Agent', 'SafeOptAgent', 'StaticControlAgent'] ================================================ FILE: openmodelica_microgrid_gym/agents/agent.py ================================================ from typing import List, Union import numpy as np from matplotlib.figure import Figure from openmodelica_microgrid_gym.env import ModelicaEnv from openmodelica_microgrid_gym.util import EmptyHistory class Agent: def __init__(self, obs_varnames: List[str] = None, history: EmptyHistory = None, env: ModelicaEnv = None): """ Abstract base class for all Agents. The agent can act on the environment and observe its result. This class is aims to wrap the whole learning process into a class to simplify the implementation. This class is strongly inspired by the tensorforce library. :param obs_varnames: list of variable names that match the values of the observations passed in the act function. Will be automatically set by the Runner class :param history: used to store agent internal data for monitoring :param env: reference to the environment (only needed when used in internal act function) """ self.env = env self.history = history or EmptyHistory() self.obs_varnames = obs_varnames def reset(self): """ Resets all agent buffers and discards unfinished episodes. """ self.history.reset() self.prepare_episode() def act(self, obs: np.ndarray) -> np.ndarray: """ Select an action with respect to the state this might update the internal state with respect to the history. :param obs: Observation :return: Actions the agent takes with respect to the observation """ pass def observe(self, reward: float, terminated: bool): """ The observe function is might be called after the act function. It might trigger the learning in some implementations. :param reward: reward from the environment after the last action :param terminated: whether the episode is finished """ pass @property def measurement_cols(self) -> List[Union[List, str]]: """ Structured columns of the measurement. Used in the Runner to setup the history columns of the Environment. :return: structured columns of measurement """ return [] def measure(self, obs: np.ndarray) -> np.ndarray: """ Measurements the agent takes on the environment. This data is passed to the environment. The values returned by this property should be fully determined by the environment. This is a workaround to provide data measurement like PLL controllers in the environment even though they are functionally part of the Agent. :return: current measurement """ return np.empty(0) def render(self) -> Figure: """ Visualisation of the agent, e.g. its learning state or similar """ pass def prepare_episode(self): """ Prepares the next episode; resets all controllers and filters (initial value of integrators...) """ pass @property def has_improved(self) -> bool: """ Defines if the performance increased or stays constant Does not learn, can never improve """ return False @property def has_worsened(self) -> bool: """ Defines if the performance decreased or stays constant Does not learn, can never improve """ return False ================================================ FILE: openmodelica_microgrid_gym/agents/episodic.py ================================================ from openmodelica_microgrid_gym.agents import Agent class EpisodicLearnerAgent(Agent): def observe(self, reward: float, terminated: bool): """ Observes current reward. The idea of the episodic learner is to only learn after termination, therefore update_params() must not be called during the episode but only at the end. """ if terminated: self.update_params() def update_params(self): pass @property def performance(self): return None @performance.setter def performance(self, val): pass ================================================ FILE: openmodelica_microgrid_gym/agents/safeopt.py ================================================ import importlib import logging from typing import Dict, Union, Any, List, Mapping import GPy import matplotlib.pyplot as plt import numpy as np from GPy.kern import Kern from matplotlib.figure import Figure from safeopt import SafeOptSwarm from openmodelica_microgrid_gym.agents.episodic import EpisodicLearnerAgent from openmodelica_microgrid_gym.agents.staticctrl import StaticControlAgent from openmodelica_microgrid_gym.agents.util import MutableParams from openmodelica_microgrid_gym.aux_ctl import Controller logger = logging.getLogger(__name__) class SafeOptAgent(StaticControlAgent, EpisodicLearnerAgent): def __init__(self, mutable_params: Union[dict, list], abort_reward: int, min_performance: float, kernel: Kern, gp_params: Dict[str, Any], ctrls: List[Controller], obs_template: Mapping[str, List[Union[List[str], np.ndarray]]], obs_varnames: List[str] = None, **kwargs): """ Agent to execute safeopt algorithm (https://arxiv.org/abs/1509.01066) to control the environment by using auxiliary controllers and Gaussian process to adopt the controller parameters (mutable_params) to safely increase the performance. :param mutable_params: safe inital controller parameters to adopt :param abort_reward: factor to multiply with the initial reward to give back an abort_reward-times higher negative reward in case of limit exceeded :param kernel: kernel for the Gaussian process unsing GPy :param gp_params: kernel parameters like bounds and lengthscale :param ctrls: Controllers that are feed with the observations and exert actions on the environment :param obs_template: Template describing how the observation array should be transformed and passed to the internal controllers. The key must match the name field of an internal controller. The values are a list of: - list of strings - matching variable names of the state - must match self.obs_varnames - will be substituted by the values on runtime - will be passed as an np.array of floats to the controller - np.ndarray of floats (to be passed statically to the controller) - a mixture of static and dynamic values in one parameter is not supported for performance reasons. The values will be passed as parameters to the controllers step function. :param obs_varnames: list of variable names that match the values of the observations passed in the act function. Will be automatically set by the Runner class :param min_performance: Minial allowed performance to define parameters as safe """ self.params = MutableParams( list(mutable_params.values()) if isinstance(mutable_params, dict) else mutable_params) self.kernel = kernel self.bounds = gp_params['bounds'] self.noise_var = gp_params['noise_var'] self.prior_mean = gp_params['prior_mean'] self.safe_threshold = gp_params['safe_threshold'] self.explore_threshold = gp_params['explore_threshold'] self.abort_reward = abort_reward self.episode_return = None self.optimizer = None self._performance = None self._min_performance = min_performance self.initial_performance = min_performance / 2 self.last_best_performance = 0 # set to 0 due to in MC otherwise we do not get the best/worst before the # first update_params after the MC loop self.last_worst_performance = 0 self.unsafe = False self._iterations = 0 super().__init__(ctrls, obs_template, obs_varnames, **kwargs) self.history.cols = ['J', 'Params'] def reset(self): """ Resets the kernel, episodic reward and the optimizer """ # reinstantiate kernel kernel_params = self.kernel.to_dict() cls_name = kernel_params['class'] mod = importlib.import_module('.'.join(cls_name.split('.')[:-1])) cls = getattr(mod, cls_name.split('.')[-1]) remaining_params = {k: v for k, v in kernel_params.items() if k not in {'class', 'useGPU'}} self.kernel = cls(**remaining_params) self.params.reset() self.optimizer = None self.episode_return = 0 self.initial_performance = 1 self._performance = None self.last_best_performance = 0 self.last_worst_performance = 0 self.unsafe = False self._iterations = 0 return super().reset() def observe(self, reward, terminated): """ Makes an observation of the enviroment. If terminated, then caclulates the performance and the next values for the parameters using safeopt :param reward: reward of the simulation step :param terminated: True if episode is over or aborted :return: """ self._iterations += 1 self.episode_return += reward or 0 if terminated: if self.optimizer is None: self.initial_performance = self.episode_return # self.performance = self._iterations / (self.episode_return * self.initial_performance) self.performance = (self.episode_return - self._min_performance) / ( self.initial_performance - self.min_performance) # safeopt update step self.update_params() # reset for new episode self.prepare_episode() # on other steps we don't need to do anything def update_params(self): """ Sets up the Gaussian process in the first episodes, updates the parameters in the following. """ if self.optimizer is None: # First Iteration self.last_best_performance = self.performance self.last_worst_performance = self.performance # Define Mean "Offset": Like BK: Assume Mean = Threshold (BK = 0, now = 20% below first (safe) J: means: if # new Performance is 20 % lower than the inital we assume as unsafe) mf = GPy.core.Mapping(len(self.bounds), 1) mf.f = lambda x: self.prior_mean * self.performance mf.update_gradients = lambda a, b: 0 mf.gradients_X = lambda a, b: 0 gp = GPy.models.GPRegression(np.array([self.params[:]]), # noqa np.array([[self.performance]]), self.kernel, noise_var=self.noise_var) self.optimizer = SafeOptSwarm(gp, self.safe_threshold * self.performance, bounds=self.bounds, threshold=self.explore_threshold * self.performance) else: self.optimizer.add_new_data_point(self.params[:], self.performance) if self.performance < self.safe_threshold: # Due to nromalization tp 1 safe_threshold directly enough self.unsafe = True self.history.append([self.performance, self.params[:]]) self.params[:] = self.optimizer.optimize() if self.has_improved: # if performance has improved store the current last index of the df self.best_episode = self.history.df.shape[0] - 1 self.last_best_performance = self.performance if self.has_worsened: # if performance has improved store the current last index of the df self.worst_episode = self.history.df.shape[0] - 1 self.last_worst_performance = self.performance def render(self) -> Figure: """ Renders the results for the performance """ figure, ax = plt.subplots() if self.optimizer.x.shape[1] > 3: # check if the dimensionality is less then 4 dimension logger.info('Plotting of GP landscape not possible for then 3 dimensions') return figure self.optimizer.plot(1000, figure=figure, ms=1) # mark best performance in green y, x = self.history.df.loc[self.best_episode, ['J', 'Params']] y0, x0 = self.history.df.loc[0, ['J', 'Params']] if len(x) == 1: ax.scatter([x], [y], s=20, marker='x', linewidths=3, color='g') ax.scatter([x0], [y0], s=20, marker='x', linewidths=3, color='m') # ax.scatter([x], [y], s=20 * 10, marker='x', linewidths=3, color='g') elif len(x) == 2: ax.plot(x[0], x[1], 'og') ax.plot(x0[0], x0[1], 'om') else: logger.warning('Choose appropriate number of control parameters') plt.show() return figure def prepare_episode(self): """ Prepares the next episode; reset iteration counting variable and call superclass to reset controllers """ self._iterations = 0 super().prepare_episode() @property def has_improved(self) -> bool: """ Defines if the performance increased or stays constant :return: True, if performance was increased or equal, else False """ return self.performance >= self.last_best_performance @property def has_worsened(self) -> bool: """ Defines if the performance increased or stays constant :return: True, if performance was increased or equal, else False """ return self.performance <= self.last_worst_performance @property def performance(self): if np.isnan(self.episode_return): # toDo: set reward to -inf and stop agent? # warning mit logger logger.warning('UNSAFE! Limit exceeded, epsiode abort, give a reward of {} times the ' 'initial reward'.format(self.abort_reward)) # set r to doubled (negative!) initial reward self._performance = self.abort_reward """print('Reward exceeded bad bound! Continue?(Type Y/N)') variable = input('Does this spike make sence?! (Type Y/N): ') if variable == 'N': self.episode_return = -300 elif variable == 'Y': logger.warning('UNSAFE! Limit exceeded, epsiode abort, give a reward of {} times the ' 'initial reward'.format(self.abort_reward)) # set r to doubled (negative!) initial reward self._performance = self.abort_reward else: print('Choose appropiate answer! Programm will run with J =1') self.episode_return = -300 """ if self._performance is None: # Performance = inverse average return (return/iterations)^⁻1 normalized by initial performance # self._performance = self._iterations / (self.episode_return * self.initial_performance) self._performance = (self.episode_return - self._min_performance) / ( self.initial_performance - self.min_performance) return self._performance @performance.setter def performance(self, new_performance): self._performance = new_performance # self.episode_return = self._iterations / (new_performance*self.initial_performance) @property def min_performance(self): if self._min_performance is None: self._min_performance = 0.95 * self.initial_performance return self._min_performance ================================================ FILE: openmodelica_microgrid_gym/agents/staticctrl.py ================================================ from itertools import chain from typing import List, Mapping, Union import numpy as np from openmodelica_microgrid_gym.agents import Agent from openmodelica_microgrid_gym.agents.util import MutableParams from openmodelica_microgrid_gym.aux_ctl import Controller from openmodelica_microgrid_gym.util import ObsTempl class StaticControlAgent(Agent): def __init__(self, ctrls: List[Controller], obs_template: Mapping[str, List[Union[List[str], np.ndarray]]], obs_varnames: List[str] = None, **kwargs): """ Simple agent that controls the environment by using auxiliary controllers that are fully configured. The Agent does not learn anything. :param ctrls: Controllers that are feed with the observations and exert actions on the environment :param obs_template: Template describing how the observation array should be transformed and passed to the internal controllers. The key must match the name field of an internal controller. The values are a list of: - list of strings - matching variable names of the state - must match self.obs_varnames - will be substituted by the values on runtime - will be passed as an np.array of floats to the controller - np.array of floats (to be passed statically to the controller) - a mixture of static and dynamic values in one parameter is not supported for performance reasons. The values will be passed as parameters to the controllers step function. :param obs_varnames: list of variable names that match the values of the observations passed in the act function. Will be automatically set by the Runner class """ super().__init__(obs_varnames, **kwargs) self.episode_return = 0 self.controllers = {ctrl.name: ctrl for ctrl in ctrls} self.obs_template_param = obs_template self._obs_template = None @property def obs_template(self): """ lazy fill convert the observation template as the runner will set the :return: """ if self._obs_template is None: self._obs_template = {ctrl: ObsTempl(self.obs_varnames, tmpl) for ctrl, tmpl in self.obs_template_param.items()} return self._obs_template def act(self, state: np.ndarray): """ Executes the actions with the observations as parameters. :param state: the agent itself is stateless. the state is stored in the controllers. Therefore we simply pass the observation from the environment into the controllers. """ controls = [self.controllers[key].step() for key in self.obs_template.keys()] return np.concatenate(controls) def observe(self, reward: float, terminated: bool): """ The observe function is might be called after the act function. It might trigger the learning in some implementations. :param reward: reward from the environment after the last action :param terminated: whether the episode is finished """ self.episode_return += reward or 0 if terminated: # reset episode reward self.prepare_episode() # on other steps we don't need to do anything @property def measurement_cols(self) -> List[Union[List, str]]: """ Structured columns of the measurement. Used in the Runner to setup the history columns of the Environment. :return: structured columns of measurement """ return [ctrl.history.structured_cols(None) for ctrl in self.controllers.values()] def measure(self, state) -> np.ndarray: """ Measurements the agent takes on the environment. This data is passed to the environment. The values returned by this property should be fully determined by the environment. This is a workaround to provide data measurement like PLL controllers in the environment even though they are functionally part of the Agent. :return: current measurement """ for key, tmpl in self.obs_template.items(): params = tmpl.fill(state) self.controllers[key].prepare(*params) return np.array(list(chain.from_iterable([ctrl.history.last() for ctrl in self.controllers.values()]))) def prepare_episode(self): """ Prepares the next episode; resets all controllers and filters (initial value of integrators...) """ for ctrl in self.controllers.values(): ctrl.reset() self.episode_return = 0 @property def has_improved(self) -> bool: """ Defines if the performance increased or stays constant Does not learn, can never improve :return: False """ return False ================================================ FILE: openmodelica_microgrid_gym/agents/util.py ================================================ from typing import Sequence class MutableFloat: def __init__(self, f: float): """ Wrapper object to store a float and change/modify it in a call-by-reference manner :param f: float as initial data """ self._f = f def __float__(self): return float(self.val) def __repr__(self): return f'{self.__class__.__name__}({float(self)})' @property def val(self): """ retrieve or update internal float variable :getter: retrieve internal variable :setter: substitute internal variable """ return self._f @val.setter def val(self, v: float): self._f = v class MutableParams: def __init__(self, params: Sequence[MutableFloat]): """ Wrapper object to access, modify and reset a sequence of float values in a call-by-reference manner Supports slicing for getting and setting :param params: initial values """ self.vars = params self.defaults = [float(v) for v in params] def reset(self): """ Restore initial value of all variables """ for var, default in zip(self.vars, self.defaults): var.val = default def __setitem__(self, key, value): if isinstance(key, slice): for var, val in zip(self.vars[key], value): var.val = val else: self.vars[key].val = value def __getitem__(self, item): if isinstance(item, slice): return [float(v) for v in self.vars[item]] return float(self.vars[item]) def __repr__(self): return str(list(self.vars)) ================================================ FILE: openmodelica_microgrid_gym/aux_ctl/__init__.py ================================================ from openmodelica_microgrid_gym.aux_ctl.inverter_controllers import * from openmodelica_microgrid_gym.aux_ctl.params import * __all__ = ['PI_params', 'PLLParams', 'DroopParams', 'InverseDroopParams', 'Controller', 'MultiPhaseABCPIPIController', 'MultiPhaseDQCurrentController', 'MultiPhaseDQ0PIPIController', 'MultiPhaseDQCurrentSourcingController'] ================================================ FILE: openmodelica_microgrid_gym/aux_ctl/base.py ================================================ from typing import Tuple import numpy as np from openmodelica_microgrid_gym.aux_ctl.params import PLLParams from openmodelica_microgrid_gym.aux_ctl.pi_controllers import PIController from openmodelica_microgrid_gym.util import abc_to_alpha_beta, cos_sin, normalise_abc, Fastqueue class DDS: """ Implements a basic Direct Digital Synthesizer (DDS) controller. Basically is a resetting integrator to provide a theta reference at a given frequency. """ def __init__(self, ts: float, dds_max: float = 1, theta_0: float = 0): """ :param ts: Sample time :param dds_max: The value at which the DDS resets the integrator :param theta_0: The initial value of the DDS upon initialisation (not reset) """ self._integralSum = 0 self._ts = ts self._max = dds_max self._integralSum = theta_0 def reset(self): """ Resets the DDS integrator to 0 """ self._integralSum = 0 def step(self, freq: float): """ Advances the Oscilator :param freq: Absolute frequency to oscillate at for the next time step :return theta: The angle in RADIANS [0:2pi] """ self._integralSum += self._ts * freq # reset oscilator one phase if self._integralSum > self._max: self._integralSum -= self._max return self._integralSum * 2 * np.pi class LimitLoadIntegral: def __init__(self, dt, freq, i_lim, i_nom=None): """ :param dt: time resolution in s :param freq: nominal frequency, used to calculate timewindow of interest :param i_lim: maximum energy allowed over one half frequency time in J :param i_nom: nominal energy allowed over one half frequency time in J """ self.dt = dt # number of samples for a half freq self._buffer = Fastqueue(int(1 / freq / 2 / dt)) self.integral = 0 self.lim_integral = len(self._buffer) * self.dt * i_lim ** 2 if i_nom: self.nom_integral = len(self._buffer) * self.dt * i_nom ** 2 else: self.nom_integral = len(self._buffer) * self.dt * (i_lim * .9) ** 2 def reset(self): self.integral = 0 self._buffer.clear() def step(self, value): self.integral += self.dt * (value ** 2 - self._buffer.shift(value) ** 2) def risk(self): return np.clip((self.integral - self.nom_integral) / (self.lim_integral - self.nom_integral), 0, 1) class PLL: """ Implements a basic PI controller based PLL to track the angle of a three-phase ABC voltage """ def __init__(self, params: PLLParams, ts: float): """ :param params: PI Params for controller (kP, kI, limits, kB, f_nom, theta_0) :param ts: absolute sampling time for the controller """ self._params = params self._controller = PIController(params, ts) # Uses a DDS oscillator to keep track of the internal angle self._dds = DDS(ts=ts, theta_0=params.theta_0) self._prev_cossin = cos_sin(params.theta_0) self._sqrt2 = np.sqrt(2) def step(self, v_abc: np.ndarray) -> Tuple[np.ndarray, float, float]: """ Performs a discrete set of calculations for the PLL :param v_abc: Voltages in the abc frame to track :return _prev_cossin: The internal cos-sin of the current phase angle :return freq: The frequency of the internal oscillator :return theta: The internal phase angle """ v_abc = normalise_abc(v_abc) cossin_x = abc_to_alpha_beta(v_abc) dphi = self.__phase_comp(cossin_x, self._prev_cossin) freq = self._controller.step(dphi) + self._params.f_nom theta = self._dds.step(freq) self._prev_cossin = cos_sin(theta) return self._prev_cossin, freq, theta def reset(self): self._dds.reset() @staticmethod def __phase_comp(cos_sin_x: np.ndarray, cos_sin_i: np.ndarray): """ The phase comparison calculation uses sin(A-B)= sin(A)cos(B)-cos(A)sin(B) = A-B (approximates for small A-B) :param cos_sin_x: Alpha-beta components of the external angle to track, should be normalised [-1,1] :param cos_sin_i: Alpha-beta components of the internal angle to compare to, should be normalised [-1,1] :return dphi: The approximate error between the two phases """ dphi = (cos_sin_x[1] * cos_sin_i[0]) - (cos_sin_x[0] * cos_sin_i[1]) return dphi ================================================ FILE: openmodelica_microgrid_gym/aux_ctl/droop_controllers.py ================================================ from openmodelica_microgrid_gym.aux_ctl.filter import PT1Filter from openmodelica_microgrid_gym.aux_ctl.params import InverseDroopParams class DroopController(PT1Filter): """ Implements a basic first order filter with gain and time constant. Uses the PT1 to implement the droop but modifies the gains and outputs as required to implement inverter droop Ignores the first order element if gain is set to 0, providing a linear gain """ def __init__(self, DroopParams, ts): """ :type DroopParams: DroopParams :param DroopParams: The droop parameters (gain, tau, nom_value) :type ts: float :param ts: Sample time """ self._droopParams = DroopParams super().__init__(DroopParams, ts) def step(self, val_in): """ Implements a first order response on the input, using the initialised params :type val_in: float :param val_in: Input - instantaneous power/reactive power :return f/V: frequency or voltage, depending on the load and nominal value """ return super().step(val_in) + self._droopParams.nom_val class InverseDroopController(DroopController): """ Implements an inverse Droop controller. For the use in grid following inverters as opposed to grid forming inverters Uses the frequency to determine the power output. Contains a derivative elements and an input filter. Ignores the first order element if gain is set to 0, providing a linear gain """ def __init__(self, DroopParams: InverseDroopParams, ts: float): """ :param DroopParams: The InverseDroopControllerParams for the droop controller :param ts: Sample step size """ super().__init__(DroopParams, ts) self._params = DroopParams self._prev_val = 0 self._ts = ts self._droop_filt = PT1Filter(DroopParams.derivativeFiltParams, ts) def step(self, val_in: float): """ Implements a inverse of the first order system :param val_in: The result of a first order response to be reversed :return f/V: frequency or voltage, depending on the load and nominal value """ val_in = self._droop_filt.step(val_in - self._params.nom_val) derivative = (val_in - self._prev_val) / self._ts derivative = derivative * self._params.tau self._prev_val = val_in if self._params.gain != 0: output = (val_in / self._params.gain + derivative) # print("Inverse val: {}, nom: {}, output: {}".format(val_in,self._params.gain, output)) return output else: return 0 ================================================ FILE: openmodelica_microgrid_gym/aux_ctl/filter.py ================================================ from openmodelica_microgrid_gym.aux_ctl.params import DroopParams class Filter: """ An empty Filter defining a base interface for any inherenting classes """ def step(self, value): pass class PT1Filter(Filter): """ A PT1 Filter implementation """ def __init__(self, filtParams: DroopParams, ts: float): """ :param filtParams: The filter params :param ts: Sample time """ self._params = filtParams self._integral = 0 self._ts = ts def reset(self): """ Resets the filter Integrator """ self._integral = 0 def step(self, val_in): """ Implements a first order PT1 filter on the input :type val_in: float :param val_in: Filter input :return: Filtered output """ output = val_in * self._params.gain - self._integral if self._params.tau != 0: intIn = output / self._params.tau self._integral = (self._integral + intIn * self._ts) output = self._integral elif self._params.gain != 0: self._integral = 0 else: output = 0 return output ================================================ FILE: openmodelica_microgrid_gym/aux_ctl/inverter_controllers.py ================================================ import logging from typing import List import numpy as np from openmodelica_microgrid_gym.util import SingleHistory, EmptyHistory, nested_map, inst_power, inst_reactive, \ dq0_to_abc, abc_to_dq0, abc_to_dq0_cos_sin, inst_rms, dq0_to_abc_cos_sin from .base import DDS, PLL from .droop_controllers import DroopController, InverseDroopController from .observers import Observer from .params import * from .pi_controllers import MultiPhasePIController N_PHASE = 3 logger = logging.getLogger(__name__) class Controller: """ Base class for all voltage and current controllers """ def __init__(self, IPIParams: PI_params, ts_sim: float, ts_ctrl: Optional[float] = None, history: EmptyHistory = None, name=''): """ :param IPIParams: PI parameters for the current control loop :param ts_sim: positive float. absolute time resolution of the env :param ts_ctrl: positive float. absolute sampling time for the controller :param history: Dataframe to store internal data """ self.history = history or SingleHistory() self._last_meas = [] self._ts = ts_ctrl if ts_ctrl is None: self._ts = ts_sim self._undersample = 1 else: self._ts = ts_ctrl self._undersample = ts_ctrl / ts_sim if abs(self._undersample - round(self._undersample)) > 1e-6 or self._undersample < 1: raise ValueError('Controller time resolution must be a integer multiple of simulation time resolution') elif abs(self._undersample - round(self._undersample)) > 0: logger.warning('Controller time resolution is almost a integer multiple of simulation time resolution,' f' rounded undersampling is of {int(round(self._undersample))} is used.' ' The timeresolution will be undersampling wrt. to simulation time.') self._undersample = int(round(self._undersample)) self._currentPI = MultiPhasePIController(IPIParams, self._ts) # defining memory variables self._undersampling_count = None self._stored_control = None self.name = name def _set_hist_cols(self, cols): """ prefixes column names with controller name and append a entry for the clipped values :param cols: :return: """ self.history.cols = nested_map(lambda col: '.'.join([self.name, col]), cols + [f'm{s}_clipped' for s in 'abc']) def reset(self): """ Resets the controller to initialization state. Must be called before usage. """ self.history.reset() # enforce the first step call to calculate the set point self._undersampling_count = 0 self._stored_control = np.zeros(N_PHASE) self._currentPI.reset() def step(self): """ Will use precalculated action and handle undersampling. The function will replay the last control action for the duration of the undersampling. :return: most up to date control action """ return self._stored_control def prepare(self, *args, **kwargs): """ Performs the calculations for a discrete step of the controller and stores control response """ if self._undersampling_count == 0: abc_action = self.control(*args, **kwargs) abc_action = np.clip(abc_action, -1, 1) self._last_meas.extend(abc_action) self.history.append(self._last_meas) self._stored_control = abc_action self._undersampling_count = (self._undersampling_count + 1) % self._undersample def control(self, *args, **kwargs) -> np.ndarray: """ Performs the calculations for a discrete step of the controller :param currentCV: 1d-array with 3 entries, one for each phase. The feedback values for current :param voltageCV: 1d-array with 3 entries, one for each phase. The feedback values for voltage :param idq0SP: :return: The controller output for the current calculation in the ABC frame """ pass class VoltageCtl(Controller): def __init__(self, VPIParams: PI_params, IPIParams: PI_params, Pdroop_param: DroopParams, Qdroop_param: DroopParams, ts_sim: float, ts_ctrl: Optional[float] = None, *args, **kwargs): """ Defines the controller for a voltage forming inverter (Master) :param VPIParams: PI parameters for the voltage control loop :param IPIParams: PI_parameter for the current control loop :param Pdroop_param: Droop parameters for P-droop :param Qdroop_param: Droop parameters for Q-droop :param ts_sim: positive float. absolute time resolution of the env :param ts_ctrl: positive float. absolute sampling time for the controller """ super().__init__(IPIParams, ts_sim=ts_sim, ts_ctrl=ts_ctrl, *args, **kwargs) self._integralSum = 0 self._PdroopController = DroopController(Pdroop_param, self._ts) self._QdroopController = DroopController(Qdroop_param, self._ts) self._voltagePI = MultiPhasePIController(VPIParams, self._ts) self._phaseDDS = DDS(self._ts) def reset(self): super().reset() self._voltagePI.reset() self._phaseDDS.reset() self._PdroopController.reset() self._QdroopController.reset() class CurrentCtl(Controller): def __init__(self, IPIParams: PI_params, pllPIParams: PLLParams, i_limit: float, Pdroop_param: InverseDroopParams, Qdroop_param: InverseDroopParams, ts_sim: float, ts_ctrl: Optional[float] = None, *args, **kwargs): """ Defines the controller for a current sourcing inverter (Slave) :param IPIParams: PI_parameter for the current control loop :param pllPIParams: PI parameters for the PLL controller :param i_limit: Current limit :param Pdroop_param: Droop parameters for P-droop :param Qdroop_param: Droop parameters for Q-droop :param ts_sim: positive float. absolute time resolution of the env :param ts_ctrl: positive float. absolute sampling time for the controller """ super().__init__(IPIParams, ts_sim=ts_sim, ts_ctrl=ts_ctrl, *args, **kwargs) self._i_limit = i_limit self._PdroopController = InverseDroopController(Pdroop_param, self._ts) self._QdroopController = InverseDroopController(Qdroop_param, self._ts) # Three controllers for each axis (d,q,0) self._pll = PLL(pllPIParams, self._ts) def reset(self): super().reset() self._pll.reset() self._PdroopController.reset() self._QdroopController.reset() class MultiPhaseABCPIPIController(VoltageCtl): """ Implements a discrete multiphase PI-PI voltage forming control with current limiting in ABC. Has its own internal oscillator to keep track of the internal angle Controls each phase individually in the abc axis. """ def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, **kwargs): instPow = -inst_power(voltageCV, currentCV) freq = self._PdroopController.step(instPow) # Get the next phase rotation angle to implement phase = self._phaseDDS.step(freq) instQ = -inst_reactive(voltageCV, currentCV) voltage = self._QdroopController.step(instQ) VSP = voltage * 1.732050807568877 # Voltage SP in dq0 (static for the moment) SPVdq0 = np.array([VSP, 0, 0]) # Get the voltage SPs in abc vector # print("SPVdq0: {}, phase: {}".format(SPVdq0,phase)) SPV = dq0_to_abc(SPVdq0, phase) # print("QInst: {}, Volt {}".format(instQ,VSP)) SPI = self._voltagePI.step(SPV, voltageCV) # Average voltages from modulation indices created by current controller return self._currentPI.step(SPI, currentCV) class MultiPhaseDQ0PIPIController(VoltageCtl): """ Implements a discrete multiphase PI-PI voltage forming control with current limiting. Has its own internal oscillator to keep track of the internal angle. Controls each phase individualy in the dq0 axis. """ def __init__(self, VPIParams: PI_params, IPIParams: PI_params, Pdroop_param: DroopParams, Qdroop_param: DroopParams, ts_sim: float, ts_ctrl: Optional[float] = None, observer: Optional[List[Observer]] = None, *args, **kwargs): """ :param VPIParams: PI parameters for the voltage control loop :param IPIParams: PI_parameter for the current control loop :param Pdroop_param: Droop parameters for P-droop :param Qdroop_param: Droop parameters for Q-droop :param ts_sim: positive float. absolute time resolution of the env :param ts_ctrl: positive float. absolute sampling time for the controller :param observer: list of Observers """ super().__init__(VPIParams, IPIParams, ts_sim=ts_sim, ts_ctrl=ts_ctrl, Pdroop_param=Pdroop_param, Qdroop_param=Qdroop_param, *args, **kwargs) self._set_hist_cols(['phase', 'instPow', 'instQ', 'freq', [f'SPV{s}' for s in 'dq0'], [f'SPV{s}' for s in 'abc'], [f'SPI{s}' for s in 'dq0'], [f'SPI{s}' for s in 'abc'], [f'CVV{s}' for s in 'dq0'], [f'CVi{s}' for s in 'dq0'], [f'I_hat{s}' for s in 'dq0'], [f'I_hat{s}' for s in 'abc'], [f'm{s}' for s in 'dq0'], [f'm{s}' for s in 'abc']]) self._prev_CV = np.zeros(N_PHASE) self._lastMabc = np.zeros(N_PHASE) self._observer = observer def reset(self): self._prev_CV = np.zeros(N_PHASE) self._lastMabc = np.zeros(N_PHASE) super().reset() def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, **kwargs): """ Performs the calculations for a discrete step of the controller :param currentCV: 1d-array with 3 entries, one for each phase in abc. The feedback values for current :param voltageCV: 1d-array with 3 entries, one for each phase in abc. The feedback values for voltage :return: Modulation index for the inverter in abc """ instPow = -inst_power(voltageCV, currentCV) freq = self._PdroopController.step(instPow) # Get the next phase rotation angle to implement self.phase = phase = self._phaseDDS.step(freq) instQ = -inst_reactive(voltageCV, currentCV) voltage = self._QdroopController.step(instQ) # Transform the feedback to the dq0 frame CVIdq0 = abc_to_dq0(currentCV, phase) CVVdq0 = abc_to_dq0(voltageCV, phase) # If available, calulate load current using observer iabc_out_etimate = np.empty(N_PHASE) if self._observer is None: iabc_out_etimate = np.zeros(N_PHASE) else: for j in range(N_PHASE): iabc_out_etimate[j] = self._observer[j].cal_estimate(y=[currentCV[j], voltageCV[j]], u=self._lastMabc[j]) i_dq0_out_estimat = abc_to_dq0(iabc_out_etimate, phase) # Voltage controller calculations VSP = voltage # Voltage SP in dq0 (static for the moment) SPVdq0 = np.array([VSP, 0, 0]) SPVabc = dq0_to_abc(SPVdq0, phase) SPIdq0 = self._voltagePI.step(SPVdq0, CVVdq0, i_dq0_out_estimat) SPIabc = dq0_to_abc(SPIdq0, phase) # Current controller calculations MVdq0 = self._currentPI.step(SPIdq0, CVIdq0) MVabc = dq0_to_abc(MVdq0, phase) # Transform the MVs back to the abc frame self._lastMabc = MVabc # Add intern measurment self._last_meas = [phase, instPow, instQ, freq, *SPVdq0, *SPVabc, *SPIdq0, *SPIabc, *CVVdq0, *CVIdq0, *i_dq0_out_estimat, *iabc_out_etimate, *MVdq0, *MVabc] return MVabc class MultiPhaseDQCurrentController(CurrentCtl): """ Implements a discrete 3-phase current sourcing inverter, using a PLL to keep track of the external phase angle Controls the currents dq0 axis, aligned to the external voltage vector, d-axis is aligned with the A phase. Rotating frame aligned with A axis at # t = 0, that is, at t = 0, the d-axis is aligned with the a-axis. DOES NOT wait for PLL lock before activating """ def __init__(self, IPIParams: PI_params, pllPIParams: PLLParams, i_limit: float, Pdroop_param: InverseDroopParams, Qdroop_param: InverseDroopParams, ts_sim: float, ts_ctrl: Optional[float] = None, lower_droop_voltage_threshold: float = 150., *args, **kwargs): """ :param IPIParams: PI_parameter for the current control loop :param pllPIParams: PI parameters for the PLL controller :param i_limit: Current limit :param Pdroop_param: Droop parameters for P-droop :param Qdroop_param: Droop parameters for Q-droop :param lower_droop_voltage_threshold: Grid voltage threshold from where the controller starts to react on the voltage and frequency in the grid :param ts_sim: positive float. absolute time resolution of the env :param ts_ctrl: positive float. absolute sampling time for the controller :param history: Dataframe to store internal data """ super().__init__(IPIParams, pllPIParams, ts_sim=ts_sim, ts_ctrl=ts_ctrl, i_limit=i_limit, Pdroop_param=Pdroop_param, Qdroop_param=Qdroop_param, *args, **kwargs) self._set_hist_cols(['phase', 'instPow', 'instQ', 'freq', [f'CVI{s}' for s in 'dq0'], [f'SPI{s}' for s in 'dq0'], [f'm{s}' for s in 'dq0'], [f'm{s}' for s in 'abc']]) # Populate the previous values with 0's self._prev_cossine = np.zeros(2) self._lastIDQ = np.zeros(N_PHASE) self._prev_theta = 0 self._prev_freq = 0 self.lower_droop_voltage_threshold = lower_droop_voltage_threshold def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, idq0SP: np.ndarray = np.zeros(3), **kwargs): """ Performs the calculations for a discrete step of the controller Droop-control is started if Vgrid_rms exceeds 200 V, to avoid oscillation with the grid forming inverter :param currentCV: 1d-array with 3 entries, one for each phase. The feedback values for current :param voltageCV: 1d-array with 3 entries, one for each phase. The feedback values for voltage :param idq0SP: The peak current setpoints in the dq0 frame (Additional power output to droop, if == 0, than only droop is applied :return: Modulation indices for the current sourcing inverter in ABC """ # Calulate P&Q for slave instPow = -inst_power(voltageCV, currentCV) instQ = -inst_reactive(voltageCV, currentCV) Vinst = inst_rms(voltageCV) # Get current phase information from the voltage measurement self._prev_cossine, self._prev_freq, self._prev_theta = self._pll.step(voltageCV) # Transform the current feedback to the DQ0 frame self._lastIDQ = abc_to_dq0_cos_sin(currentCV, *self._prev_cossine) droop = np.zeros(2) if Vinst > self.lower_droop_voltage_threshold: # Determine the droop power setpoints droopPI = (self._PdroopController.step(self._prev_freq) / inst_rms(voltageCV)) # Determine the droop reactive power set points droopQI = (self._QdroopController.step(Vinst) / Vinst) droop = np.array([droopPI, droopQI]) / 3 # devide by 3 due to here dq, but we want to set the e.g. # d-setpoint in the sum of the phases abc droop = droop * 1.4142135623730951 # RMS to Peak droop = np.clip(droop, -self._i_limit, self._i_limit) idq0SP = idq0SP + np.array([-droop[0], +droop[1], 0]) # Calculate the control applied to the DQ0 currents # action space is limited to [-1,1] MVdq0 = self._currentPI.step(idq0SP, self._lastIDQ) # Transform the outputs from the controllers (dq0) to abc # also divide by SQRT(2) to ensure the transform is limited to [-1,1] MVabc = dq0_to_abc_cos_sin(MVdq0, *self._prev_cossine) self._last_meas = [self._prev_theta, instPow, instQ, self._prev_freq, *self._lastIDQ, *idq0SP, *MVdq0, *MVabc] return MVabc class MultiPhaseDQCurrentSourcingController(Controller): """ Implements a discrete multiphase PI current controller to supply an amount of current to a load / the grid which can be chosen via state"extension" using idq0SP at a frequency of f_nom. Has its own internal oscillator to keep track of the internal angle. Controls each phase individually in the dq0 axis. """ def __init__(self, IPIParams: PI_params, ts_sim: float, ts_ctrl: Optional[float] = None, f_nom: float = 50.0, *args, **kwargs): """ :param IPIParams: PI_parameter for the current control loop :param ts_sim: positive float. absolute time resolution of the env :param ts_ctrl: positive float. absolute sampling time for the controller :param f_nom: nominal frequency for current sourcing ctrl """ super().__init__(IPIParams, ts_sim=ts_sim, ts_ctrl=ts_ctrl, *args, **kwargs) self._phaseDDS = DDS(self._ts) self.f_nom = f_nom self._set_hist_cols(['phase', [f'CVI{s}' for s in 'dq0'], [f'SPI{s}' for s in 'dq0'], [f'SPI{s}' for s in 'abc'], [f'm{s}' for s in 'dq0'], [f'm{s}' for s in 'abc']]) self._prev_CV = np.zeros(N_PHASE) def reset(self): super().reset() self._phaseDDS.reset() def control(self, currentCV: np.ndarray, idq0SP: np.ndarray = np.zeros(3), **kwargs): """ Performs the calculations for a discrete step of the controller :param currentCV: 1d-array with 3 entries, one for each phase in abc. The feedback values for current :param idq0SP: 1d-array with 3 entries, one for each phase in dq0. Current set points for current sourcing ctrl :return: Modulation index for the inverter in abc """ # Get the next phase rotation angle to implement phase = self._phaseDDS.step(self.f_nom) # Transform the feedback to the dq0 frame CVIdq0 = abc_to_dq0(currentCV, phase) # current setpoint SPIdq0 = np.array(idq0SP[:]) SPIabc = dq0_to_abc(SPIdq0, phase) # Current controller calculations MVdq0 = self._currentPI.step(SPIdq0, CVIdq0) MVabc = dq0_to_abc(MVdq0, phase) # Add intern measurment self._last_meas = [phase, *CVIdq0, *SPIdq0, *SPIabc, *MVdq0, *MVabc] # Transform the MVs back to the abc frame return MVabc ================================================ FILE: openmodelica_microgrid_gym/aux_ctl/observers.py ================================================ import numpy as np N_PHASE = 3 class Observer(): def __init__(self): pass def cal_estimate(self, y, u): """ Estimtates output values depending on measured outputs y and intputs u Our exemplary application: x_hat = [iL_abc, vC_abc, iLoad_abc] y = [iL_abc_mess, vC_abc_mess, 0] C = [[1, 0, 0], [0, 1, 0], [0, 0, 0]] -> y_hat = [iL_hat, vC_hat, 0] """ pass class Lueneberger(Observer): def __init__(self, A, B, C, L, ts, vDC): super().__init__() self.A = A self.B = B self.C = C self.L = L self._ts = ts self.vDC = vDC self.last_x_estimate = np.zeros([3, 1]) def cal_estimate(self, y, u): """ Estimtates output values depending on measured outputs y and intputs u Our exemplary application: x_hat = [iL_abc, vC_abc, iLoad_abc] y = [iL_abc_mess, vC_abc_mess, 0] C = [[1, 0, 0], [0, 1, 0], [0, 0, 0]] -> y_hat = [iL_hat, vC_hat, 0] """ y_estimate = self.C @ self.last_x_estimate uB = self.L @ (np.array(y).reshape([2, 1]) - y_estimate) # times vCD because vDC was shifted to the OM-env model_input = self.B * u * self.vDC dx_estimate = model_input + uB + self.A @ self.last_x_estimate self.last_x_estimate = self.last_x_estimate + dx_estimate * self._ts return self.last_x_estimate[-1] ================================================ FILE: openmodelica_microgrid_gym/aux_ctl/params.py ================================================ """ The parameter classes wrap controller parameters. The fields are wrapped into properties in order to allow transparent usage of the MutableFloat wrapper """ from typing import Tuple, Union, Optional from openmodelica_microgrid_gym.agents.util import MutableFloat class FilterParams: def __init__(self, gain: Union[MutableFloat, float], tau: Union[MutableFloat, float]): """ Defines Filter Parameters :param gain: Filter gain :param tau: Filter time constant """ self._gain = gain self._tau = tau @property def gain(self): return float(self._gain) @property def tau(self): return float(self._tau) class DroopParams(FilterParams): """ Defines droop parameters needed for the droop-controller for a voltage forming inverter """ def __init__(self, gain: Union[MutableFloat, float], tau: Union[MutableFloat, float], nom_value: float = 0): """ e.g. for a P-f droop controller (for voltage forming inverter) Inverter of 10 kW, droop of 10% , tau of 1 sec, 50 Hz Droop = gain = 1000 [W/Hz] tau = 1 nomValue = 50 [Hz] :param gain: The droop gain [W/Hz or VA/V], gets inverted :param tau: The first order time constant [s] :param nom_value: An offset to add to the output of the droop (e.g. f = 50 Hz) """ super().__init__(gain, tau) self.nom_val = nom_value @property def gain(self): if float(self._gain) != 0: return 1 / float(self._gain) return 0 class InverseDroopParams(DroopParams): """ Defines droop parameters needed for the droop-controller for a current sourcing inverter """ def __init__(self, gain: Union[MutableFloat, float], tau: Union[MutableFloat, float], nom_value: float = 0, tau_filt: Union[MutableFloat, float] = 0): """ e.g. for a f-P droop controller (for current sourcing inverter) Inverter of 10 kW, droop of 10% , tau of 1 sec, 50 Hz Droop = gain = 1000 [W/Hz] tau = 1 nomValue = 50 [Hz] :param gain: The droop gain [W/Hz or VA/V] - Defines the power output due to the frequency/voltage change from nom_val :param tau: The first order time constant [s] :param nom_value: An offset to add to the output of the droop (e.g. f = 50 Hz) :param tau_filt: timeresolution for filter """ super().__init__(gain, tau, nom_value) self.derivativeFiltParams = FilterParams(1, tau_filt) class PI_params: """ The params for a basic PI Controller All fields are represented by properties to allow passing MutableFloats """ def __init__(self, kP: Union[MutableFloat, float], kI: Union[MutableFloat, float], limits: Union[Tuple[MutableFloat, MutableFloat], Tuple[float, float]], kB: float = 1): """ :param kP: Proportional gain :param kI: Intergral gain :param limits: Controller limits :param kB: Anti-windup (back calculation) """ self._kP = kP self._kI = kI self._limits = limits self._kB = kB @property def kP(self): return float(self._kP) @property def kI(self): return float(self._kI) @property def limits(self): if self._limits is None: return [-float('inf'),float('inf')] return [float(limit) for limit in self._limits] @property def kB(self): return float(self._kB) class PLLParams(PI_params): """ The params for a Phase Lock Loop (PLL) to measure the frequency """ def __init__(self, kP: Union[MutableFloat, float], kI: Union[MutableFloat, float], limits: Optional[Union[Tuple[MutableFloat, MutableFloat], Tuple[float, float]]] = None, kB: Union[MutableFloat, float] = 1, f_nom: float = 50, theta_0: float = 0): """ :param kP: Proportional gain :param kI: Intergral gain :param limits: Controller limits :param kB: Anti-windup (back calculation) :param f_nom: Nominal grid frequency to track (e.g. 50 Hz) :param theta_0: Inital angle """ super().__init__(kP, kI, limits, kB) self._f_nom = f_nom self._theta_0 = theta_0 @property def f_nom(self): return float(self._f_nom) @property def theta_0(self): return float(self._theta_0) ================================================ FILE: openmodelica_microgrid_gym/aux_ctl/pi_controllers.py ================================================ import logging from typing import Optional import numpy as np from openmodelica_microgrid_gym.aux_ctl.params import PI_params N_phase = 3 class PIController: """ Implements a basic, discrete PI controller. Uses back calculation for anti-windup. """ def __init__(self, PI_param: PI_params, ts: float): """ :param PI_param: The PI_Parameters object with the PI controller parameters (kP, kI, kB for the gains of the proportional, integral and anti-windup part and the limits of the output) :param ts: Sample time """ self._params = PI_param self.integralSum = 0 self.windup_compensation = 0 self._ts = ts def reset(self): """ Resets the Integrator """ self.integralSum = 0 def step(self, error: float, feedforward: float = 0) -> float: """ Implements a step of a basic PI controller with anti-windup by back-calculation :param error: Control error to act on :param feedforward: feed forward term :return: The calculated PI controller response to the error, using the PI_Parameters provided during initialisation, clipped due to the defined limits """ self.integralSum += (self._params.kI * error + self.windup_compensation) * self._ts output = self._params.kP * error + self.integralSum clipped = np.clip(output + feedforward, *self._params.limits) self.windup_compensation = (output + feedforward - clipped) * self._params.kB return clipped.squeeze() class MultiPhasePIController: """ Implements a number of PI controllers for use in multiphase systems Number of phases is set to N_phase = 3 """ def __init__(self, PI_param: PI_params, ts: float): """ :param PI_param: The PI_Parameters object with the PI controller parameters (kP, kI, kB for the gains of the proportional, integral and anti-windup part and the limits of the output) :param ts: Sample time """ self.controllers = [PIController(PI_param, ts) for _ in range(N_phase)] def reset(self): """ Resets all controllers """ for ctl in self.controllers: ctl.reset() def step(self, SP: np.ndarray, CV: np.ndarray, feedforward: Optional[np.ndarray] = None) -> np.ndarray: """ Performs a controller step calculating the error itself using the array of Setpoints (SP) and Controlled Variables (CV, feedback) :param SP: Floats of setpoints :param CV: Floats of system state to be controlled (feedback) :return output: An array of the controller outputs. """ if feedforward is None: feedforward = np.zeros(len(SP)) error = SP - CV if len(error) != len(self.controllers): message = f'List of values for error inputs should be of the length {len(self.controllers)}, ' f'equal to the number of model inputs. Actual length {len(error)}' logging.error(message) raise ValueError(message) # perform all the steps for each phase return np.array([ctl.step(error[i], feedforward[i]) for i, ctl in enumerate(self.controllers)]) ================================================ FILE: openmodelica_microgrid_gym/env/__init__.py ================================================ from .modelica import ModelicaEnv from .plot import PlotTmpl __all__ = ['ModelicaEnv', 'PlotTmpl'] ================================================ FILE: openmodelica_microgrid_gym/env/modelica.py ================================================ import logging import re from fnmatch import translate from functools import partial from typing import Sequence, Callable, List, Union, Tuple, Optional, Mapping, Dict, Any import gym import matplotlib.pyplot as plt import numpy as np import scipy from matplotlib.figure import Figure from scipy import integrate from openmodelica_microgrid_gym.env.plot import PlotTmpl from openmodelica_microgrid_gym.env.pyfmi import PyFMI_Wrapper from openmodelica_microgrid_gym.net.base import Network from openmodelica_microgrid_gym.util import FullHistory, EmptyHistory, Fastqueue, ObsTempl logger = logging.getLogger(__name__) class ModelicaEnv(gym.Env): """ OpenAI gym Environment encapsulating an FMU model. """ viz_modes = {'episode', 'step', None} """Set of all valid visualisation modes""" def __init__(self, net: Union[str, Network], time_start: float = 0, reward_fun: Callable[[List[str], np.ndarray, float], float] = lambda cols, obs, risk: 1, abort_reward: Optional[float] = -np.inf, is_normalized=True, log_level: int = logging.WARNING, solver_method: str = 'LSODA', max_episode_steps: Optional[int] = 200, model_params: Optional[Dict[str, Union[Callable[[float], Optional[float]], float]]] = None, model_path: str = '../omg_grid/grid.network.fmu', viz_mode: Optional[str] = 'episode', viz_cols: Optional[Union[str, List[Union[str, PlotTmpl]]]] = None, history: EmptyHistory = FullHistory(), action_time_delay: int = 0, on_episode_reset_callback: Optional[Callable[[], None]] = None, obs_output: List[str] = None): """ Initialize the Environment. The environment can only be used after reset() is called. :param time_start: offset of the time in seconds :param reward_fun: The function receives as a list of variable names and a np.ndarray of the values of the current observation as well as a risk value between 0 and 1. The separation is mainly for performance reasons, such that the resolution of data indices can be cached. It must return the reward of this timestep as float. It should return np.nan or -np.inf or None in case of a failiure. It should have no side-effects :param abort_reward: reward returned on episode abort :param log_level: logging granularity. see logging in stdlib :param solver_method: solver of the scipy.integrate.solve_ivp function :param max_episode_steps: maximum number of episode steps. After one episodes there are max_episode_steps+1 states available (one additionally caused by the reset) and max_episode_steps actions, so the env.step() is executed max_episode_steps-times The end time of the episode is calculated by the time resolution and the number of steps. If set to None, the environment will never finish because of step sizes, but it might still stop because of system failure (-inf reward) :param model_params: parameters of the FMU. dictionary of variable names and scalars or callables. If a callable is provided it is called on reset with t==-1 and in every time step with the current time. This callable must return a float or None The float is passed to the fmu, None values are discarded. This allows setting initial values using like:: model_params={'lc1.capacitor1.v': lambda t: -10 if t==-1 else None} as well as continuosly changing values using like:: model_params={'lc1.capacitor1.v': lambda t: np.random.random()} :param net: Path to the network configuration file passed to the net.Network.load() function :param model_path: Path to the FMU :param viz_mode: specifies how and if to render - 'episode': render after the episode is finished - 'step': render after each time step - None: disable visualization :param viz_cols: enables specific columns while plotting - None: all columns will be used for visualization (default) - string: will be interpret as regex. all fully matched columns names will be enabled - list of strings: Each string might be a unix-shell style wildcard like "*.i" to match all data series ending with ".i". - list of PlotTmpl: Each template will result in a plot :param history: history to store observations and measurement (from the agent) after each step :param action_time_delay: Defines how many time steps the controller needs before the action is applied; action is buffered in an array :param on_episode_reset_callback: Callable which is executed during the environment reset. Can be used for example to reset external processes like random process used in model params and to synchronize their reset to the env reset :param obs_output: List of strings of observations given to the agent. obs_output is compared to history.cols ( variable names have to fit) """ if viz_mode not in self.viz_modes: raise ValueError(f'Please select one of the following viz_modes: {self.viz_modes}') self.viz_mode = viz_mode self._register_render = False logger.setLevel(log_level) self.solver_method = solver_method # load model from fmu self.model = PyFMI_Wrapper.load(model_path) # if you reward policy is different from just reward/penalty - implement custom step method self.reward = reward_fun self.abort_reward = abort_reward self._failed = False # Parameters required by this implementation self.max_episode_steps = max_episode_steps if time_start < 0: raise RuntimeError('Starttime must not negative! -1 is used for initialization of model params functions!' ' See ModelicaEnv.__init__() parameter "model_params".') self.time_start = time_start if isinstance(net, Network): self.net = net else: self.net = Network.load(net) self.time_step_size = self.net.ts self.time_end = np.inf if max_episode_steps is None \ else self.time_start + (max_episode_steps + 1) * self.time_step_size # + 1 to execute env.step() # max_episode_step-times # if there are parameters, we will convert all scalars to constant functions. model_params = model_params or dict() # the "partial" is needed because of some absurd python behaviour https://stackoverflow.com/a/34021333/13310191 self.model_parameters = {var: (val if callable(val) else partial(lambda t, val_: val_, val_=val)) for var, val in model_params.items()} self.sim_time_interval = None self._state = np.empty(0) self.measure = lambda obs: np.empty(0) # type : Callable([np.ndarray],np.ndarray) self.record_states = viz_mode == 'episode' self.history = history self.is_normalized = is_normalized # also add the augmented values to the history self.history.cols = self.net.out_vars(True, False) self.model_input_names = self.net.in_vars() # variable names are flattened to a list if they have specified in the nested dict manner) self.model_output_names = self.net.out_vars(False, True) self.viz_col_tmpls = [] if viz_cols is None: logger.info('Provide the option "viz_cols" if you wish to select only specific plots. ' 'The default behaviour is to plot all data series') self.viz_col_regex = '.*' elif isinstance(viz_cols, list): # strings are glob patterns that can be used in the regex patterns, tmpls = [], [] for elem in viz_cols: if isinstance(elem, str): patterns.append(translate(elem)) elif isinstance(elem, PlotTmpl): tmpls.append(elem) else: raise ValueError('"viz_cols" list must contain only strings or PlotTmpl objects not' f' {type(viz_cols)}') self.viz_col_regex = '|'.join(patterns) self.viz_col_tmpls = tmpls elif isinstance(viz_cols, str): # is directly interpret as regex self.viz_col_regex = viz_cols else: raise ValueError('"viz_cols" must be one type Optional[Union[str, List[Union[str, PlotTmpl]]]]' f'and not {type(viz_cols)}') # OpenAI Gym requirements d_i, d_o = len(self.model_input_names), len(obs_output or self.history.cols) self.action_space = gym.spaces.Box(low=np.full(d_i, -1), high=np.full(d_i, 1)) self.observation_space = gym.spaces.Box(low=np.full(d_o, -np.inf), high=np.full(d_o, np.inf)) self.used_action = np.zeros(self.action_space.shape) self.action_time_delay = action_time_delay if self.action_time_delay == 0: self.delay_buffer = None else: self.delay_buffer = Fastqueue(self.action_time_delay, self.action_space.shape[0]) if on_episode_reset_callback is None: self.on_episode_reset_callback = lambda: None else: self.on_episode_reset_callback = on_episode_reset_callback if obs_output is None: # if not defined all hist.cols are used as observations self._out_obs_tmpl = ObsTempl(self.history.cols, None) else: self._out_obs_tmpl = ObsTempl(self.history.cols, [obs_output]) def _calc_jac(self, t, x) -> np.ndarray: # noqa """ Compose Jacobian matrix from the directional derivatives of the FMU model. This function will be called by the scipy.integrate.solve_ivp solver, therefore we have to obey the expected signature. :param t: time (ignored) :param x: state (ignored) :return: the Jacobian matrix """ return self.model.jacc() def _get_deriv(self, t: float, x: np.ndarray) -> np.ndarray: """ Retrieve derivatives at given time and with given state from the FMU model :param t: time :param x: 1d float array of continuous states :return: 1d float array of derivatives """ self.model.time = t self.model.states = x.copy(order='C') # Compute the derivative dx = self.model.deriv return dx def _simulate(self) -> np.ndarray: """ Executes simulation by FMU in the time interval [start_time; stop_time] currently saved in the environment. :return: resulting state of the environment """ logger.debug(f'Simulation started for time interval {self.sim_time_interval[0]}-{self.sim_time_interval[1]}') # Advance x_0 = self.model.states # Get the output from a step of the solver sol_out = scipy.integrate.solve_ivp( self._get_deriv, self.sim_time_interval, x_0, method=self.solver_method, jac=self._calc_jac) # get the last solution of the solver self.model.states = sol_out.y[:, -1] # noqa obs = self.model.obs return obs @property def is_done(self) -> bool: """ Checks if the experiment is finished using a time limit :return: True if simulation time exceeded """ if self._failed: logger.info(f'risk level exceeded') return True logger.debug(f't: {self.sim_time_interval[1]}, ') return abs(self.sim_time_interval[1]) > self.time_end def reset(self) -> np.ndarray: """ OpenAI Gym API. Restarts environment and sets it ready for experiments. In particular, does the following: * resets model * sets simulation start time to 0 * sets initial parameters of the model - Using the parameters defined in self.model_parameters * initializes the model :return: state of the environment after resetting. If self.ob_output is not None, the there defined outputs are returned. """ self.net.reset() logger.debug("Experiment reset was called. Resetting the model.") self.on_episode_reset_callback() self.sim_time_interval = np.array([self.time_start, self.time_start + self.time_step_size]) self.model.setup(self.time_start, self.model_output_names, self.model_parameters) self.history.reset() self._failed = False self._register_render = False self.used_action = np.zeros(self.action_space.shape) if self.delay_buffer is not None: self.delay_buffer.clear() outputs = self._create_state(is_init=True) return self._out_obs_tmpl.fill(outputs)[0] def step(self, action: Sequence) -> Tuple[np.ndarray, float, bool, Mapping]: """ OpenAI Gym API. Determines how one simulation step is performed for the environment. Simulation step is execution of the given action in a current state of the environment. The state also contains the measurement. :param action: action to be executed. :return: state, reward, is done, info """ logger.debug("Experiment next step was called.") if self.is_done: logger.warning( """You are calling 'step()' even though this environment has already returned done = True. You should always call 'reset()' once you receive 'done = True' -- any further steps are undefined behavior.""") return self._state, -np.inf, True, {} # check if action is a list. If not - create list of length 1 try: iter(action) except TypeError: action = [action] logger.warning("Model input values (action) should be passed as a list") # Check if number of model inputs equals number of values passed if len(action) != len(list(self.model_input_names)): message = f'List of values for model inputs should be of the length {len(list(self.model_input_names))},' f'equal to the number of model inputs. Actual length {len(action)}' logger.error(message) raise ValueError(message) action = np.clip(action, self.action_space.low, self.action_space.high) # enqueue action and get delayed/last action if self.delay_buffer is not None: self.used_action = self.delay_buffer.shift(action) else: self.used_action = action # Set input values of the model logger.debug('model input: %s, values: %s', self.model_input_names, self.used_action) self.model.set(**dict(zip(self.model_input_names, self.used_action))) if self.model_parameters: values = {var: f(self.sim_time_interval[0]) for var, f in self.model_parameters.items()} else: values = {} params = {**values, **self.net.params(self.used_action)} # delete None values to make model initialization possible (take care in model_params definition!) params = {k: v for k, v in params.items() if v is not None} if params: self.model.set_params(**params) risk = self.net.risk() # Simulate and observe result state outputs = self._create_state() logger.debug("model output: %s, values: %s", self.model_output_names, self._state) # Check if experiment has finished # Move simulation time interval if experiment continues if not self.is_done: logger.debug("Experiment step done, experiment continues.") self.sim_time_interval += self.time_step_size else: logger.debug("Experiment step done, experiment done.") reward = self.reward(self.history.cols, outputs, risk) self._failed = risk >= 1 or reward is None or np.isnan(reward) or (np.isinf(reward) and reward < 0) if self._failed: reward = self.abort_reward # only return the state, the agent does not need the measurement # if self.obs_output is defined, obs_tmpl is used to filter out wanted observations, otherwise all states are # passed return self._out_obs_tmpl.fill(outputs)[0], reward, self.is_done, dict(risk=risk) def _create_state(self, is_init: bool = False): # Simulate and observe result state if is_init: self._state = self.model.obs raw, normalized = self.net.augment(self._state, -1) else: self._state = self._simulate() raw, normalized = self.net.augment(self._state, self.sim_time_interval[0]) outputs = normalized if self.is_normalized else raw measurements = self.measure(outputs) raw = np.hstack([raw, measurements]) self.history.append(raw) outputs = np.hstack([outputs, measurements]) return outputs def render(self, mode: str = 'human', close: bool = False) -> List[Figure]: """ OpenAI Gym API. Determines how current environment state should be rendered. Does nothing at the moment :param mode: (ignored) rendering mode. Read more in Gym docs. :param close: flag if rendering procedure should be finished and resources cleaned. Used, when environment is closed. """ if self.viz_mode is None: return [] elif self.viz_mode == 'step': if close: # TODO close plot pass pass elif self.viz_mode == 'episode': # TODO update plot if not close: self._register_render = True elif self._register_render: figs = [] # plot cols by theirs structure filtered by the vis_cols param for cols in self.history.structured_cols(): if not isinstance(cols, list): cols = [cols] cols = [col for col in cols if re.fullmatch(self.viz_col_regex, col)] if not cols: continue df = self.history.df[cols].copy() df.index = self.history.df.index * self.time_step_size + self.time_start fig, ax = plt.subplots() df.plot(legend=True, figure=fig, ax=ax) plt.show() figs.append(fig) # plot all templates for tmpl in self.viz_col_tmpls: fig, ax = plt.subplots() for series, kwargs in tmpl: ser = self.history.df[series].copy() ser.index = self.history.df.index * self.time_step_size + self.time_start ser.plot(figure=fig, ax=ax, **kwargs) tmpl.callback(fig) figs.append(fig) return figs def close(self) -> Tuple[bool, Any]: """ OpenAI Gym API. Closes environment and all related resources. Closes rendering. :return: True on success """ figs = self.render(close=True) return True, figs ================================================ FILE: openmodelica_microgrid_gym/env/plot.py ================================================ from typing import List, Union, Callable, Optional from matplotlib.figure import Figure import matplotlib.pyplot as plt from more_itertools import collapse from openmodelica_microgrid_gym.util import flatten_together class PlotTmpl: def __init__(self, variables: List[Union[List, str]], callback: Optional[Callable[[Figure], None]] = None, **kwargs): """ Provides an iterable of variables and plot parameters like ('induction1', {'color':'green', 'style': '--'}). It contains logic to automatically match up the variables and provided kwargs to allow for a simple syntax. e.g. when called with [['a','b'],['c','d']] and style = [['.', None],'--'] it will detect the grouping and apply the dotted style to 'a' and the dashed style to 'c' and 'd' :param vars: nested list of strings. Each string represents a variable of the FMU or a measurement that should be plotted by the environment. :param callback: if provided, it is executed after the plot is finished. Will get the generated figure as parameter to allow further modifications. :param kwargs: those arguments are merged (see omg.util.flatten_together) with the variables and than provided to the pd.DataFrame.plot(·) function """ self.vars = list(collapse(variables)) self._callback = callback # set colors None if not provided colorkey = ({'c', 'color'} & set(kwargs.keys())) if not colorkey: kwargs['c'] = None colorkey = 'c' elif len(colorkey) > 1: raise ValueError(f'Multiple color parameters provided "{colorkey}"') else: colorkey = colorkey.pop() args = dict() for k, v in dict(kwargs).items(): args[k] = flatten_together(variables, v) # apply to a group only if all color values are none inside that group if colorkey: # if all elements in the variables are lists and they are all of equal length lengths = set([isinstance(l, list) and len(l) for l in variables]) if len(lengths) == 1: # set contains either the length of all lists or false if all values where non-list values length = lengths.pop() if length: for groups in range(len(variables)): for i in range(length): if args[colorkey][length * groups + i] is None: args[colorkey][length * groups + i] = 'C' + str(i + 1) else: # all elements are single values for i, c in enumerate(args[colorkey]): if c is None: args[colorkey][i] = 'C' + str(i + 1) # merge parameters to the variables for indexing access self.kwargs = [] for i, _ in enumerate(self.vars): args_ = dict() for k, arg in args.items(): v = arg[i] if v is not None: args_[k] = v self.kwargs.append(args_) def callback(self, fig: Figure): """ Will be called in the ModelicaEnv.render() once all plotting is finished. This function enables the user to specify more modifications to apply to the figure. The function will call the callable passed in the constructor. Additionally the figure is plotted by this function. :param fig: Finished figure that is one might want to modify. :return: """ if self._callback is not None: self._callback(fig) else: plt.show() def __iter__(self): self.i = -1 return self def __next__(self): try: self.i += 1 return self.vars[self.i], self.kwargs[self.i] except IndexError: raise StopIteration def __getitem__(self, item): return self.vars[item], self.kwargs[item] ================================================ FILE: openmodelica_microgrid_gym/env/plotmanager.py ================================================ import os.path as p import matplotlib.pyplot as plt from openmodelica_microgrid_gym.agents import SafeOptAgent class PlotManager: def __init__(self, used_agent: SafeOptAgent, save_results: bool = False, save_folder: str = 'test_folder', show_plots: bool = True): """ Class for plot configuration, knows agent, so can include agent params to title (e.g. performance) If more plots should be stored in save_folder, extend the corresponding label function with save command :param used_agent: agent used for experiment :param save_results: if True, saves results to save_folder :param save_folder: folder name :param show_plots: if True, shows plots after each run """ self.agent = used_agent self.save_results = save_results self.save_folder = save_folder self.show_plots = show_plots def xylables_v_abc(self, fig): self.update_axes(fig, ylabel='$v_{\mathrm{abc}}\,/\,\mathrm{V}$', filename=f'{self.agent.history.df.shape[0]}_J_{self.agent.performance}_v_abc0', legend=dict(handle_slice=slice(None, None, 3), labels=('Measurement', 'Setpoint'), loc='best')) def xylables_v_dq0(self, fig): self.update_axes(fig, ylabel='$v_{\mathrm{dq0}}\,/\,\mathrm{V}$', filename=f'{self.agent.history.df.shape[0]}_J_{self.agent.performance}_v_dq0', legend=dict(handle_slice=slice(None, None, 3), labels=('Measurement', 'Setpoint'), loc='best')) def xylables_i_abc(self, fig): self.update_axes(fig, ylabel='$i_{\mathrm{abc}}\,/\,\mathrm{A}$', filename=f'{self.agent.history.df.shape[0]}_J_{self.agent.performance}_i_abc', legend=dict(handle_slice=slice(None, None, 3), labels=('Measurement', 'Setpoint'), loc='best')) def xylables_i_dq0(self, fig): self.update_axes(fig, ylabel='$i_{\mathrm{dq0}}\,/\,\mathrm{A}$', filename=f'{self.agent.history.df.shape[0]}_J_{self.agent.performance}_i_dq0', legend=dict(handle_slice=slice(None, None, 3), labels=('Measurement', 'Setpoint'), loc='best')) def update_axes(self, fig, title=None, xlabel=r'$t\,/\,\mathrm{s}$', ylabel=None, legend=None, filename=None): """ General function to handle most of the standard modifications :param fig: figure to change :param title: optional title :param xlabel: optional label, time in seconds is default :param ylabel: optional ylabel :param legend: optional legend, can have optional key "handle_slice" which is used to slice the line handles for legend and set labels accordingly. :param filename: optional filename """ ax = fig.gca() if title is not None: plt.title(title) if xlabel is not None: ax.set_xlabel(xlabel) if ylabel is not None: ax.set_ylabel(ylabel) ax.grid(which='both') if legend is not None: if 'handle_slice' in legend: _slice = legend['handle_slice'] del legend['handle_slice'] plt.legend(handles=ax.lines[_slice], **legend) else: plt.legend(**legend) if self.save_results and filename is not None: for filetype in ['pgf', 'pdf']: fig.savefig(p.join(self.save_folder, f'{filename}.{filetype}')) if self.show_plots: plt.show() else: plt.close(fig) ================================================ FILE: openmodelica_microgrid_gym/env/pyfmi.py ================================================ import logging from datetime import datetime from os.path import basename from typing import Dict, Callable import numpy as np from pyfmi import load_fmu logger = logging.getLogger(__name__) class PyFMI_Wrapper: """ convenience class""" def __init__(self, model): self.model = model @classmethod def load(cls, path): model_name = basename(path) logger.debug('Loading model "%s"', model_name) model = cls(load_fmu(path, log_file_name=datetime.now().strftime(f'%Y-%m-%d_{model_name}.txt'))) logger.debug('Successfully loaded model "%s"', model_name) return model def setup(self, time_start, output_names, model_params: Dict[str, Callable]): self.model.reset() self.model.setup_experiment(start_time=time_start) # This is needed, because otherwise setting new values seems not to work self.model.initialize() if model_params: # set to -1 for initial evaluation of params. See documentation of ModelicaEnv.__init__(). values = {var: f(-1) for var, f in model_params.items()} # values = {var: f(time_start) for var, f in model_params.items()} # list of keys and list of values self.set_params(**values) e_info = self.model.get_event_info() e_info.newDiscreteStatesNeeded = True # Event iteration while e_info.newDiscreteStatesNeeded: self.model.enter_event_mode() self.model.event_update() e_info = self.model.get_event_info() self.model.enter_continuous_time_mode() # precalculating indices for more efficient lookup self.model_output_idx = np.array([self.model.get_variable_valueref(k) for k in output_names]) @property def obs(self): return self.model.get_real(self.model_output_idx) @property def states(self): return self.model.continuous_states @states.setter def states(self, val): self.model.continuous_states = val @property def deriv(self): return self.model.get_derivatives() @property def time(self): return self.model.time @time.setter def time(self, val): self.model.time = val def jacc(self): # get state and derivative value reference lists refs = [[s.value_reference for s in getattr(self.model, attr)().values()] for attr in ['get_states_list', 'get_derivatives_list']] jacobian = np.identity(len(refs[1])) np.apply_along_axis(lambda col: self.model.get_directional_derivative(*refs, col), 0, jacobian) return jacobian def set(self, **kwargs): self.model.set(*zip(*kwargs.items())) def set_params(self, **kwargs): # replacing enter and exit mode -> works to set parameters during simulation AND model get outputs self.model.initialize() self.model.set(*zip(*kwargs.items())) ================================================ FILE: openmodelica_microgrid_gym/execution/__init__.py ================================================ from openmodelica_microgrid_gym.execution.callbacks import Callback from openmodelica_microgrid_gym.execution.runner import Runner __all__ = ['Runner', 'Callback'] ================================================ FILE: openmodelica_microgrid_gym/execution/callbacks.py ================================================ from abc import ABC class Callback(ABC): def reset(self): pass def __call__(self, *args, **kwargs): pass ================================================ FILE: openmodelica_microgrid_gym/execution/runner.py ================================================ from typing import Dict, Any, Optional from tqdm import tqdm from openmodelica_microgrid_gym.agents import Agent from openmodelica_microgrid_gym.env import ModelicaEnv from openmodelica_microgrid_gym.execution.callbacks import Callback class Runner: """ This class will execute an agent on the environment. It handles communication between agent and environment and handles the execution of multiple epochs """ def __init__(self, agent: Agent, env: ModelicaEnv, callback: Optional[Callback] = None): """ :param agent: Agent that acts on the environment :param env: Environment tha Agent acts on """ self.env = env self.agent = agent self.agent.env = env self.run_data = dict() # type: Dict[str,Any] self.callback = callback """ Dictionary storing information about the experiment. - "best_env_plt": environment best plots - "best_episode_idx": index of best episode - "agent_plt": last agent plot """ def run(self, n_episodes: int = 10, visualise: bool = False): """ Trains/executes the agent on the environment for a number of epochs :param n_episodes: number of epochs to play :param visualise: turns on visualization of the environment """ self.agent.reset() self.agent.obs_varnames = self.env.history.cols self.env.history.cols = self.env.history.structured_cols(None) + self.agent.measurement_cols self.env.measure=self.agent.measure agent_fig = None for i in tqdm(range(n_episodes), desc='episodes', unit='epoch'): obs = self.env.reset() if self.callback is not None: self.callback.reset() done, r = False, None for _ in tqdm(range(self.env.max_episode_steps), desc='steps', unit='step', leave=False): self.agent.observe(r, done) act = self.agent.act(obs) obs, r, done, info = self.env.step(act) if self.callback is not None: self.callback(self.env.history.cols, self.env.history.last()) if visualise: self.env.render() if done: break # close env before calling final agent observe to see plots even if agent crashes _, env_fig = self.env.close() self.agent.observe(r, done) if visualise: agent_fig = self.agent.render() self.run_data['last_agent_plt'] = agent_fig if i == 0 or self.agent.has_improved: self.run_data['best_env_plt'] = env_fig self.run_data['best_episode_idx'] = i if i == 0 or self.agent.has_worsened: self.run_data['worst_env_plt'] = env_fig self.run_data['worst_episode_idx'] = i ================================================ FILE: openmodelica_microgrid_gym/net/__init__.py ================================================ from .components import MasterInverter, SlaveInverter, MasterInverterCurrentSourcing from .base import Network __all__ = ['Network', 'MasterInverter', 'SlaveInverter', 'MasterInverterCurrentSourcing'] ================================================ FILE: openmodelica_microgrid_gym/net/base.py ================================================ from importlib import import_module from typing import List, Dict, Optional, Union, Tuple import numexpr as ne import numpy as np import yaml from more_itertools import collapse, flatten class Component: def __init__(self, net: 'Network', id=None, in_vars=None, out_vars=None, out_calc=None): """ :param net: Network to which component belongs to :param id: Component ID :param in_vars: Input variables to component :param out_vars: Output variables from component :param out_calc: (mapping from attr name to dimension of data vector) Adds values (e.g. references,...) to output """ self.net = net self.id = id self.in_vars = in_vars self.in_idx = None # type: Optional[dict] self.out_calc = out_calc or {} self.out_vars = out_vars self.out_idx = None # type: Optional[dict] # has to be set via the network class # net['inverter2'].post_calculate_hook = callable_func # type: Callable[[Component, float], dict] # gets the component object and the current timestep self.post_calculate_hook = None def reset(self): pass def risk(self) -> float: return 0 def params(self, actions): """ Calculate additional environment parameters :param actions: :return: mapping """ return {} def get_in_vars(self): """ list of input variable names of this component """ if self.in_vars: return [[self._prefix_var(val) for val in vals] for attr, vals in self.in_vars.items()] return [] def get_out_vars(self, with_aug=False): r = [] if self.out_vars: r = [[self._prefix_var(val) for val in vals] for attr, vals in self.out_vars.items()] if not with_aug: return r else: return r + [[self._prefix_var([self.id, attr, str(i)]) for i in range(n)] for attr, n in self.out_calc.items()] def fill_tmpl(self, state: np.ndarray): if self.out_idx is None: raise ValueError('call set_tmplidx before fill_tmpl. the keys must be converted to indices for efficiency') for attr, idxs in self.out_idx.items(): # set object variables to the respective state variables setattr(self, attr, state[idxs]) def set_outidx(self, keys): # This pre-calculation is done mainly for performance reasons keyidx = {val: idx for idx, val in enumerate(keys)} self.out_idx = {} try: for var, keys in self.out_vars.items(): # lookup index in the whole state keys self.out_idx[var] = [keyidx[self._prefix_var(key)] for key in keys] except KeyError as e: raise KeyError(f'the output variable {e!s} is not provided by your state keys') def _prefix_var(self, strs): if isinstance(strs, str): strs = [strs] if strs[0].startswith('.'): # first string minus its prefix '.' and the remaining strs strs[0] = strs[0][1:] if self.id is not None: # this is a complete identifier like 'lc1.inductor1.i' that should not be modified: strs = [self.id] + strs return '.'.join(strs) def calculate(self) -> Dict[str, np.ndarray]: """ Will modify object variables (like current i of an inductor) it is called after all internal variables are set. Therefore the function has side-effects. The return value must be a dictionary whose keys match the keys of self.out_calc and whose values are of the length of out_calcs values. The returned values are hence additional values (like reference current i_ref). :: set(self.out_calc.keys()) == set(return) all([len(v) == self.out_calc[k] for k,v in return.items()]) :return: """ pass def normalize(self, calc_data): """ Will modify object variables it is called after all internal variables are set. Therefore the function has side-effects, similarly to calculate(). """ pass def augment(self, state: np.ndarray, t: float): """ Stateful function that calculates additional values given the state of the environment. :param t: timestamp from the environment :param state: state array to augment and normalize :return: augmented state as tuple of raw and normalized """ self.fill_tmpl(state) calc_data = self.calculate() if callable(self.post_calculate_hook): calc_data = {**calc_data, **self.post_calculate_hook(self, t)} raw_data = self.extract_data(calc_data) self.normalize(calc_data) norm_data = self.extract_data(calc_data) return raw_data, norm_data def extract_data(self, calc_data): """ merge data from field variables (out_idx) and additional values (out_calc) :param calc_data: :return: """ attr = '' try: # concatenate internal values (like voltage) and newly calculated ones (like ref) out_vals = [getattr(self, attr) for attr in self.out_idx.keys()] # prepare newly calculated values new_vals = [[calc_data[attr][i] for i in range(n)] for attr, n in self.out_calc.items()] return np.hstack(out_vals + new_vals) except KeyError as e: raise ValueError( f'{self.__class__} missing return key: {e!s}. did you forget to set it in the calculate method?') except IndexError as e: raise ValueError(f'{self.__class__}.calculate()[{attr}] has the wrong number of values') class Network: """ This class has two main functions: - :code:`load()`: load yaml files to instantiate an object structure of electronic components - :code:`augment()`: traverses all components and uses the data from the simulation and augments or modifies it. """ def __init__(self, ts: float, v_nom: Union[int, str], freq_nom: float = 50): self.ts = float(ts) self.v_nom = ne.evaluate(str(v_nom)) self.freq_nom = freq_nom @staticmethod def _validate_load_data(data): # validate that inputs are disjoined components_with_inputs = [component['in'].values() for component in data['components'].values() if 'in' in component] inputs = list(flatten(components_with_inputs)) if sum(map(len, inputs)) != len(set().union(*inputs)): # all inputs are pairwise disjoint if the total number of inputs is the same as the number of elements in the union raise ValueError('The inputs of the components should be disjoined') return True @classmethod def load(cls, configurl='net.yaml'): """ Initialize object from config file Structure of yaml-file: .. code-block:: text conf:: *net_params* *components* net_params:: components:: components: *component* ... *component* component:: : *component_params* component_params:: cls: in: : out: : All 'in' and 'out' variable names together define the interaction with the environment, expected cardinality and order of the vector provided to the augment(). :param configurl: :return: """ data = yaml.safe_load(open(configurl)) if not cls._validate_load_data(data): raise ValueError(f'loading {configurl} failed due to validation') components = data['components'] del data['components'] self = cls(**data) components_obj = [] for name, component in components.items(): # resolve class from 'cls' argument comp_cls = component['cls'] del component['cls'] comp_cls = getattr(import_module('..components', __name__), comp_cls) # rename keys (because 'in' is a reserved keyword) if 'in' in component: component['in_vars'] = component.pop('in') if 'out' in component: component['out_vars'] = component.pop('out') # instantiate component class try: components_obj.append(comp_cls(net=self, **component)) except AttributeError as e: raise AttributeError(f'{e!s}, please validate {configurl}') self.components = components_obj return self def risk(self) -> float: return np.max([comp.risk() for comp in self.components]) def reset(self): for comp in self.components: comp.reset() def params(self, actions): """ Allows the network to add additional parameters like changing loads to the simulation :param actions: :return: mapping of additional actions and list of actions. """ d = {} for comp in self.components: params = comp.params(actions) d.update(params) return d def augment(self, state: np.ndarray, t: float) -> Tuple[np.ndarray]: """ Allows the network to provide additional output variables in order to provide measurements and reference information the RL agent needs to understand its rewards. The function is stateful! :param t: timestamp from the environment :param state: raw state as recieved form the environment. must match the expected shape specified by :code:`in_vars()` :return: augmented state in raw and normalized """ return tuple([np.hstack(data) for data in zip(*[comp.augment(state, t) for comp in self.components])]) def in_vars(self): return list(collapse([comp.get_in_vars() for comp in self.components])) def out_vars(self, with_aug=True, flattened=True): r = [comp.get_out_vars(with_aug) for comp in self.components] if flattened: return list(collapse(r)) return r @property def components(self) -> List[Component]: return self._components @components.setter def components(self, val: List[Component]): self._components = val keys = self.out_vars(with_aug=False, flattened=True) for comp in self.components: comp.set_outidx(keys) def __getitem__(self, item): """ get component by id :param item: name of the component :return: """ for component in self.components: if component.id == item: return component raise ValueError(f'no such component named "{item}"') ================================================ FILE: openmodelica_microgrid_gym/net/components.py ================================================ from functools import partial from typing import Optional import numpy as np from openmodelica_microgrid_gym.aux_ctl import DDS, DroopController, DroopParams, InverseDroopController, \ InverseDroopParams, PLLParams, PLL from openmodelica_microgrid_gym.aux_ctl.base import LimitLoadIntegral from openmodelica_microgrid_gym.net.base import Component from openmodelica_microgrid_gym.util import dq0_to_abc, inst_power, inst_reactive class Inverter(Component): def __init__(self, u=None, i=None, i_noise: Optional[dict] = None, v=None, v_noise: Optional[dict] = None, i_nom=20, i_lim=30, v_lim=600, v_DC=1000, i_ref=(0, 0, 0), out_vars=None, **kwargs): """ :param u: input variable :param i: :param i_noise: structured like: must contain the key 'fun', the key 'clip' is optional and no clipping is applied if omited :: { 'fun': {: }, 'clip': } :param v: :param v_noise: similar to i_noise :param i_nom: :param i_lim: :param v_lim: :param v_DC: :param i_ref: :param out_vars: implicit parameter not to be passed in the net.yaml, but calculated dynamically in :code:`Network` :param kwargs: """ self.u = u self.v = v self.i = i # to feed static code analyser; vars will be set dynamically in the following loop self.i_noise = None self.v_noise = None for var in ['i', 'v']: # gets vars with reflection to create self.i_noise, self.v_noise noise_var = locals()[f'{var}_noise'] # type:dict if noise_var is None: fun = partial(np.zeros, len(out_vars[var])) else: key, value = [i[0] for i in zip(*noise_var['fun'].items())] clip_kwargs = noise_var.get('clip', dict(a_min=-float('inf'), a_max=float('inf'))) fun = lambda: np.clip(getattr(np.random.default_rng(), key)(**value, size=len(out_vars[var])), **clip_kwargs) setattr(self, f'{var}_noise', fun) self.i_nom = i_nom self.i_lim = i_lim self.v_lim = v_lim self.v_DC = v_DC self.i_ref = i_ref super().__init__(**{'out_calc': dict(i_ref=3), 'out_vars': out_vars, **kwargs}) self.limit_load_integrals = [ LimitLoadIntegral(self.net.ts, self.net.freq_nom, i_nom=i_nom, i_lim=i_lim) for _ in range(3)] def reset(self): [integ.reset() for integ in self.limit_load_integrals] def normalize(self, calc_data): self.i = self.i / self.i_lim self.v = self.v / self.v_lim calc_data['i_ref'] = calc_data['i_ref'] / self.i_lim def risk(self) -> float: return max([integ.risk() for integ in self.limit_load_integrals]) def params(self, actions): return {**super().params(actions), **{self._prefix_var(['.v_DC']): self.v_DC}} def calculate(self): self.i = self.i + self.i_noise() self.v = self.v + self.v_noise() [integ.step(i) for i, integ in zip(self.i, self.limit_load_integrals)] # no reference values or similar added return None class SlaveInverter(Inverter): def __init__(self, pll=None, pdroop=None, qdroop=None, **kwargs): super().__init__(**kwargs) pdroop = {**dict(gain=0.0), **(pdroop or {})} qdroop = {**dict(gain=0.0), **(qdroop or {})} pll = {**dict(kP=10, kI=200), **(pll or {})} # toDo: set time Constant for droop Filter correct self.pdroop_ctl = InverseDroopController( InverseDroopParams(tau=self.net.ts, nom_value=self.net.freq_nom, **pdroop), self.net.ts) self.qdroop_ctl = InverseDroopController( InverseDroopParams(tau=self.net.ts, nom_value=self.net.v_nom, **qdroop), self.net.ts) # default pll params and new ones self.pll = PLL(PLLParams(f_nom=self.net.freq_nom, **pll), self.net.ts) def reset(self): super().reset() self.pdroop_ctl.reset() self.qdroop_ctl.reset() self.pll.reset() def calculate(self): super().calculate() _, _, phase = self.pll.step(self.v) return dict(i_ref=dq0_to_abc(self.i_ref, phase)) class MasterInverter(Inverter): def __init__(self, v_ref=(1, 0, 0), pdroop=None, qdroop=None, **kwargs): self.v_ref = v_ref super().__init__(out_calc=dict(i_ref=3, v_ref=3, phase=1), **kwargs) pdroop = {**dict(gain=0.0, tau=.005), **(pdroop or {})} qdroop = {**dict(gain=0.0, tau=.002), **(qdroop or {})} self.pdroop_ctl = DroopController(DroopParams(nom_value=self.net.freq_nom, **pdroop), self.net.ts) self.qdroop_ctl = DroopController(DroopParams(nom_value=self.net.v_nom, **qdroop), self.net.ts) self.dds = DDS(self.net.ts) self.phase = 0.0 self.v_refdq0 = np.array([0, 0, 0]) def reset(self): super().reset() self.pdroop_ctl.reset() self.qdroop_ctl.reset() self.dds.reset() self.phase = 0.0 self.v_refdq0 = np.array([0, 0, 0]) def calculate(self): super().calculate() instPow = -inst_power(self.v, self.i) freq = self.pdroop_ctl.step(instPow) # Get the next phase rotation angle to implement self.phase = self.dds.step(freq) instQ = -inst_reactive(self.v, self.i) v_refd = self.qdroop_ctl.step(instQ) self.v_refdq0 = np.array([v_refd, 0, 0]) * self.v_ref return dict(i_ref=dq0_to_abc(self.i_ref, self.phase), v_ref=dq0_to_abc(self.v_refdq0, self.phase), phase=np.array([self.phase])) def normalize(self, calc_data): super().normalize(calc_data), calc_data['v_ref'] /= self.v_lim class MasterInverter_dq0(MasterInverter): """ MasterInverter that returns observaton in dq0 """ def calculate(self): super().calculate() return dict(i_ref=np.array(self.i_ref), v_ref=self.v_refdq0, phase=np.array([self.phase])) # hier die phase mit rein class MasterInverterCurrentSourcing(Inverter): def __init__(self, f_nom=50, **kwargs): super().__init__(out_calc=dict(i_ref=3), **kwargs) self.dds = DDS(self.net.ts) self.f_nom = f_nom self.phase = 0.0 def reset(self): super().reset() self.dds.reset() self.phase = 0.0 def calculate(self): super().calculate() # Get the next phase rotation angle to implement self.phase = self.dds.step(self.f_nom) return dict(i_ref=dq0_to_abc(self.i_ref, self.phase)) class Load(Component): def __init__(self, i=None, **kwargs): self.i = i super().__init__(**kwargs) def params(self, actions): # TODO: perhaps provide modelparams that set resistance value return super().params(actions) ================================================ FILE: openmodelica_microgrid_gym/util/__init__.py ================================================ from .fastqueue import Fastqueue from .itertools_ import nested_map, fill_params, nested_depth, flatten, flatten_together from .obs_template import ObsTempl from .randproc import RandProcess from .recorder import EmptyHistory, SingleHistory, FullHistory from .transforms import abc_to_alpha_beta, normalise_abc, abc_to_dq0_cos_sin, dq0_to_abc_cos_sin, abc_to_dq0, cos_sin, \ dq0_to_abc, inst_power, inst_reactive, inst_rms, dq0_to_abc_cos_sin_power_inv __all__ = ['abc_to_alpha_beta', 'normalise_abc', 'abc_to_dq0_cos_sin', 'dq0_to_abc_cos_sin', 'abc_to_dq0', 'cos_sin', 'dq0_to_abc', 'inst_power', 'inst_reactive', 'inst_rms', 'dq0_to_abc_cos_sin_power_inv', 'nested_map', 'fill_params', 'nested_depth', 'flatten', 'flatten_together', 'EmptyHistory', 'SingleHistory', 'FullHistory', 'Fastqueue', 'RandProcess', 'ObsTempl'] ================================================ FILE: openmodelica_microgrid_gym/util/fastqueue.py ================================================ from typing import Optional import numpy as np class Fastqueue: def __init__(self, size: int, dim: Optional[int] = 1): """ Efficient numpy implementation of constant sized queue without queue-shifting (indices-based value selection). Queue size of n leads to a delay of n-1. :param size: Size of queue """ self._buffer = None self._size, self._dim = size, dim self._idx = 0 def shift(self, val): """ Pushes val into buffer and returns popped last element """ if self._buffer is None: raise RuntimeError('please call clear() before using the object') self._idx = self.wrap_index(self._idx + 1) last = self._buffer[self._idx, :].copy() self._buffer[self._idx, :] = val return last def __len__(self): return self._size def clear(self): self._buffer = np.zeros((self._size, self._dim)) def wrap_index(self, i): # ringbuffer implementation with index calculated using np.ravel... -> no shifting return np.ravel_multi_index([i], (len(self),), mode='wrap') ================================================ FILE: openmodelica_microgrid_gym/util/itertools_.py ================================================ from typing import Callable, Mapping, Union, Any, List import numpy as np import pandas as pd from more_itertools import collapse def flatten(data: Union[dict, list], remaining_levels: int = 0) -> list: """ transform this: >>> {'a': {'b': [['i', 'v'], >>> ['k', 'h']]}} results into >>> ['a.b.i', 'a.b.v', 'a.b.k', 'a.b.h'] or >>> [['a.b.i', 'a.b.v'], ['a.b.k', 'a.b.h']] :param data: data to flatten. A nested dictionary containing nested lists as keys in the lowest level. :param remaining_levels: number of levels to preserve in the nested list :return: Flattened data as a nesteted list """ # collapse outer dicts if isinstance(data, dict): # flatten all the dicts df = pd.json_normalize(data) data = df.to_dict(orient='records')[0] # move the key into the lists for k, v in data.items(): data[k] = nested_map(lambda suffix: '.'.join([k, suffix]), v) data = list(data.values()) # count levels and collapse to keep the levels as needed depth = nested_depth(data) if remaining_levels is None: remaining_levels = depth - 1 return list(collapse(data, levels=depth - remaining_levels - 1)) def nested_map(fun: Callable, structure: Union[list, tuple, Mapping, np.ndarray]) \ -> Union[list, tuple, Mapping, np.ndarray]: """ Traverses data structure and substitutes every element with the result of the callable :param fun: Callable to be applied to every value :param structure: Nesting of dictionaries or lists. For mappings, the callable is applied to the values. :return: """ if isinstance(structure, Mapping): return {k: nested_map(fun, v) for k, v in structure.items()} if isinstance(structure, (list, tuple)): return [nested_map(fun, l_) for l_ in structure] if isinstance(structure, np.ndarray): # empty_like would keep the datatype, with empty, # we enforce that the dtype is infered from the result of the mapping function a = np.empty(structure.shape) for idx in np.ndindex(structure.shape): a[idx] = nested_map(fun, structure[idx]) return a return fun(structure) def nested_depth(structure: Any) -> int: """ Calculate the maximum depth of a nested sequence. :param structure: nested sequence. The containing data structures are currently restricted to lists and tuples, because allowing any sequence would also result in traversing strings for example. If a single value is passed, the return is 0 :return: maximum depth """ if isinstance(structure, (list, tuple, set)): if structure: # if the list contains elements return 1 + max((nested_depth(l_) for l_ in structure)) return 1 return 0 def fill_params(template: Union[list, tuple, Mapping, np.ndarray], data: Union[pd.Series, Mapping]) \ -> Union[list, tuple, Mapping, np.ndarray]: """ Uses a template, that can be traversed by nested_map. Each entry in the template, that is a key in the mapping is replaced by the value it is mapped to. :param template: template containing keys :param data: mapping of keys to values :return: """ if isinstance(data, pd.Series): data = data.to_dict() elif not isinstance(data, Mapping): raise ValueError("must be a mapping") # keep key if there is no substitute return nested_map(lambda k: data.get(k, k), template) def flatten_together(structure: List[Union[List, Any]], values: Union[Any, List[Union[List, Any]]]): """ Flattens and fills a list of values relative to the groupings provided by the structure parameter. The explicit values in the structure parameter are ignored. Only the nesting structure is of importance. If a single value is provided it is simply repeated as often as the structure has values e.g. when called with :code:`[[0, 0], [0, 0]]` and :code:`[[0, None], 4]` it will detect the grouping and return :code:`[0, None, 4, 4]` :param structure: nested list used as a template :param values: values matched to the list :return: flattened and filled list of values """ if not isinstance(structure, list): if isinstance(values, list): raise ValueError('There where to many nestings in the values') return values if not isinstance(values, list): values = [values] if len(structure) < len(values): return flatten_together(collapse(structure, base_type=tuple, levels=1), values) elif len(structure) > len(values): # if structure has more elements we need to repeat value elements values = values * (len(structure) // len(values)) if len(structure) != len(values): raise ValueError('stuff does not match up') return list(collapse([flatten_together(s, v) for s, v in zip(structure, values)], base_type=tuple)) ================================================ FILE: openmodelica_microgrid_gym/util/obs_template.py ================================================ from typing import List, Union, Optional import numpy as np from openmodelica_microgrid_gym.agents.util import MutableParams class ObsTempl: def __init__(self, varnames: List[str], simple_tmpl: Optional[List[Union[List[str], np.ndarray]]]): """ Internal dataclass to handle the conversion of dynamic observation templates for the StaticControlAgent :param varnames: list of variable names :param simple_tmpl: list of: - list of strings - matching variable names of the state - must match self.obs_varnames - will be substituted by the values on runtime - will be passed as an np.array of floats to the controller - np.array of floats (to be passed statically to the controller) - a mixture of static and dynamic values in one parameter is not supported for performance reasons. If None: self.fill() will not filter and return its input wrapped into a list """ idx = {v: i for i, v in enumerate(varnames)} self._static_params = set() self._data = [] self.is_tmpl_empty = simple_tmpl is None if not self.is_tmpl_empty: for i, tmpl in enumerate(simple_tmpl): if isinstance(tmpl, np.ndarray) or isinstance(tmpl, MutableParams): # all np.ndarrays are considered static parameters self._static_params.add(i) self._data.append(tmpl) else: # else we save the indices of the variables into an indexarray self._data.append(np.array([idx[varname] for varname in tmpl])) def fill(self, obs: np.ndarray) -> List[np.ndarray]: """ generates a list of parameters by filling the dynamic values and passing the static values :param obs: np.ndarray of values :return: list of parameters """ if self.is_tmpl_empty: return [obs] params = [] for i, arr in enumerate(self._data): # append static data or use dynamic data with indexing params.append(arr if i in self._static_params else obs[arr]) return params ================================================ FILE: openmodelica_microgrid_gym/util/randproc.py ================================================ from typing import Type import numpy as np from stochastic.processes import DiffusionProcess from stochastic.processes.base import BaseProcess class RandProcess: def __init__(self, process_cls: Type[BaseProcess], proc_kwargs=None, bounds=None, initial=0): """ wrapper around stochastic processes to allow easier integration :param process_cls: class of the stochastic process :param proc_kwargs: arguments passed to the class on initialization :param bounds: boundaries of admissible values :param initial: starting value of the process :param gain: scale of the process output (of not set, results are between the clipped bounds) """ self.proc = process_cls(**(proc_kwargs or {})) if bounds is None: self.bounds = (-np.inf, np.inf) else: self.bounds = bounds # will contain the previous value, hence initialized accordingly self._last = initial self._last_t = 0 self._reserve = None def reset(self, initial): self._last = initial self._last_t = 0 self._reserve = None def sample(self, t): """ calculates time differential and calculates the change in the outputs of the process :param t: timestep :return: value at the timestep """ # if not initial actually sample from processes otherwise return initial value if t != self._last_t and t >= 0: if self.reserve is not None: self._last = self.reserve self.reserve = None self.proc.t = t - self._last_t self._last_t = t if isinstance(self.proc, DiffusionProcess): self._last = np.clip(self.proc.sample(1, initial=self._last)[-1], *self.bounds).squeeze() else: self._last = np.clip(self._last + self.proc.sample(1)[-1], *self.bounds).squeeze() return self._last @property def reserve(self): """ This variable is used to prepare for external loadsteps or other abrupt changes in the process' variables. """ return self._reserve @reserve.setter def reserve(self, v): self._reserve = v ================================================ FILE: openmodelica_microgrid_gym/util/recorder.py ================================================ from typing import Sequence, List, Optional, Union import pandas as pd from openmodelica_microgrid_gym.util.itertools_ import flatten class StructuredMapping: def __init__(self, cols: List[Union[List, str]] = None, data=None): """ :param cols: nested lists of strings providing column names and hierarchical structure """ if cols is None: cols = [] self.cols = cols self._data = data """internal field storing the history data""" @property def cols(self) -> List[str]: """ Columns of the History :return: Flat list of the columns """ return self._cols @cols.setter def cols(self, val: List[Union[List, str]]): """ Columns of the History :param val: Nested list of columns as string """ self._structured_cols = val self._cols = flatten(val) @property def data(self): return self._data @property def df(self): # executing this conditionally only if _data is not a df is actually slower!!! return pd.DataFrame([self._data], columns=self.cols) def structured_cols(self, remaining_level: Optional[int] = 1) -> List[Union[List, str]]: """ Get columns with the specified amount of levels retained :param remaining_level: number of levels to retain """ return flatten(self._structured_cols, remaining_level) class EmptyHistory(StructuredMapping): """ Dummy history for recording data in the environment This class will not actually store any data """ def reset(self): """ Removes all data, but keeps the columns. """ self._data = [] def pop(self): """ pop last item :return: """ pass def append(self, values: Sequence): """ Add new data sample to the history. The History class will determine how the data is updated :param values: sequence of data entries """ pass def last(self): return self.df.tail(1).squeeze() def __getitem__(self, item): return self.df[item] class SingleHistory(EmptyHistory): """ Single history that stores only the last added value """ def reset(self): self._data = None def pop(self): val = self.last() self.reset() return val def append(self, values: Sequence): self._data = values def last(self): return self._data class FullHistory(EmptyHistory): """ Full history that stores all data """ def reset(self): self._data = [] def pop(self): return self._data.pop(-1) def append(self, values: Sequence): self._data.append(list(values)) def last(self): return self._data[-1] @property def df(self): # executing this conditionally only if _data is not a df is actually slower!!! return pd.DataFrame(self._data, columns=self.cols) ================================================ FILE: openmodelica_microgrid_gym/util/transforms.py ================================================ # -*- coding: utf-8 -*- """ Created on Fri Jan 24 16:19:01 2020 @author: jarren Common static transforms used commonly in voltage/power inverter control systems """ import numpy as np def dq0_to_abc(dq0: np.ndarray, theta: float) -> np.ndarray: """ Transforms from DQ frame to the abc frame using the provided angle theta :param dq0: The values in the dq0 reference frame :param theta: The angle to transform [In Radians] :return abc: The transformed space in the abc frame """ return dq0_to_abc_cos_sin(dq0, *cos_sin(theta)) def dq0_to_abc_cos_sin(dq0: np.ndarray, cos: float, sin: float) -> np.ndarray: """ Transforms from DQ frame to the abc frame using the provided cos-sin This implementation tries to improve on the dq0Toabc transform by utilising the provided cos-sin to minimise calls to calculate the cossine etc :param dq0: The values in the dq0 reference frame :param cos: cos(theta) :param sin: sin(theta) :return abc: The transformed space in the abc frame """ a = (cos * dq0[0] - sin * dq0[1] + dq0[2]) # implements the cos(a-2pi/3) using cos (A+B) expansion etc cos_shift = cos * (-0.5) - sin * (-0.866) sin_shift = sin * (-0.5) + cos * (-0.866) b = (cos_shift * dq0[0] - sin_shift * dq0[1] + dq0[2]) cos_shift = cos * (-0.5) - sin * (0.866) sin_shift = sin * (-0.5) + cos * (0.866) c = (cos_shift * dq0[0] - sin_shift * dq0[1] + dq0[2]) return np.array([a, b, c]) def dq0_to_abc_cos_sin_power_inv(dq0: np.ndarray, cos: float, sin: float) -> np.ndarray: """ Transforms from DQ frame to the abc frame using the provided cos-sin This implementation tries to improve on the dq0Toabc transform by utilising the provided cos-sin to minimise calls to calculate the cossine etc. Provides the Power Invariant transform (multiplied by SQRT(3/2) = 1.224744871391589) :param dq0: The values in the dq0 reference frame :param cos: cos(theta) :param sin: sin(theta) :return abc: The transformed space in the abc frame """ return dq0_to_abc_cos_sin(dq0, cos, sin) * 1.224744871391589 def abc_to_dq0(abc: np.ndarray, theta: float) -> np.ndarray: """ Transforms from abc frame to the dq0 frame using the provided angle theta :param abc: The values in the abc reference frame :param theta: The angle [radians] :return dq0: The transformed space in the abc frame """ return abc_to_dq0_cos_sin(abc, *cos_sin(theta)) def abc_to_dq0_cos_sin(abc: np.ndarray, cos: float, sin: float) -> np.ndarray: """ Transforms from abc frame to the dq0 frame using the provided cos-sin This implementation tries to improve by utilising the provided cos-sin to minimise calls to calculate the cossine etc :param abc: The values in the abc reference frame :param cos: cos(theta) :param sin: sin(theta) :return dq0: The transformed space in the abc frame """ # implements the cos(a-2pi/3) using cos (A+B) expansion etc cos_shift_neg = cos * (-0.5) - sin * (-0.866) sin_shift_neg = sin * (-0.5) + cos * (-0.866) # implements the cos(a+2pi/3) using cos (A+B) expansion etc cos_shift_pos = cos * (-0.5) - sin * 0.866 sin_shift_pos = sin * (-0.5) + cos * 0.866 # Calculation for d-axis aligned with A axis d = (2 / 3) * (cos * abc[0] + cos_shift_neg * abc[1] + cos_shift_pos * abc[2]) q = (2 / 3) * (-sin * abc[0] - sin_shift_neg * abc[1] - sin_shift_pos * abc[2]) z = (1 / 3) * abc.sum(axis=0) return np.array([d, q, z]) def abc_to_alpha_beta(abc: np.ndarray) -> np.ndarray: """ Transforms from abc frame to the alpha-beta frame :param abc: The values in the abc reference frame :return [alpha,beta]: The transformed alpha beta results """ alpha = (2 / 3) * (abc[0] - 0.5 * abc[1] - 0.5 * abc[2]) beta = (2 / 3) * (0.866 * abc[1] - 0.866 * abc[2]) return np.array([alpha, beta]) def cos_sin(theta: float) -> np.ndarray: """ Transforms from provided angle to the relevant cosine values :param theta: The angle [In RADIANS] :return: [alpha,beta] The resulting cosine """ return np.array([np.cos(theta), np.sin(theta)]) def inst_rms(arr: np.ndarray) -> float: """ Calculates the instantaneous RMS (root mean square) value of the input arr :param arr: Input :return: RMS value of the arr """ return np.linalg.norm(arr) / 1.732050807568877 def normalise_abc(abc: np.ndarray) -> np.ndarray: """ Normalises the abc magnitudes to the RMS of the 3 magnitudes Determines the instantaneous RMS value of the 3 waveforms :param abc: Three phase magnitudes input :return abc_norm: abc result normalised to [-1,1] """ # Get the magnitude of the waveforms to normalise the PLL calcs mag = inst_rms(abc) if mag != 0: abc = abc / mag return abc def inst_power(varr: np.ndarray, iarr: np.ndarray) -> float: """ Calculates the instantaneous power :param varr: voltage :param iarr: current :return: instantaneous power """ return varr @ iarr def inst_reactive(varr: np.ndarray, iarr: np.ndarray): """ Calculates the instantaneous reactive power :param varr: voltage :param iarr: current :return: instantaneous reactive power """ # vline = np.array([varr[1] - varr[2], varr[2] - varr[0], varr[0] - varr[1]]) # Linevoltages cal using np.roll return -0.5773502691896258 * (np.roll(varr, -1) - np.roll(varr, -2)) @ iarr ================================================ FILE: requirements.txt ================================================ ## created with pipreqs gym>=0.15.3 numpy>=1.17.2 matplotlib>=3.1.1 scipy>=1.3.1 pandas>=1.0.1 tqdm>=4 more_itertools>=7 numexpr>=2.7.1 tables>=3.6.1 stochastic>=0.6.0 pyyaml~=5.4 PyFMI>=2.5 safeopt>=0.16 GPy>=1.9.9 future~=0.18.2 pytest~=5.4.3 setuptools~=47.1.1 ================================================ FILE: requirements_dev.txt ================================================ bump2version==0.5.11 watchdog==0.9.0 pytest>=5.2.2 pytest-cov pytest-runner==5.1 tables>=3.4.1 safeopt>=0.16 GPy>=1.9.9 Sphinx==3.1 sphinx-autodoc-typehints>=1.11 sphinx_rtd_theme twine==1.14.0 ================================================ FILE: setup.cfg ================================================ [bumpversion] current_version = 0.4.0 commit = True tag = True [bumpversion:file:setup.py] search = version='{current_version}' replace = version='{new_version}' [bumpversion:file:openmodelica_microgrid_gym/__init__.py] search = __version__ = '{current_version}' replace = __version__ = '{new_version}' [bdist_wheel] universal = 1 [aliases] test = pytest [tool:pytest] collect_ignore = ['setup.py'] testpaths = tests addopts = --cov=openmodelica_microgrid_gym ================================================ FILE: setup.py ================================================ #!/usr/bin/env python """The setup script.""" from setuptools import setup, find_packages with open('README.rst') as readme_file: readme = readme_file.read() with open('HISTORY.rst') as history_file: history = history_file.read() with open('requirements.txt', 'r') as f: requirements = f.read().splitlines() setup_requirements = ['pytest-runner'] test_requirements = ['pytest>=3', 'tables>=3.4.1', 'safeopt>=0.16', 'GPy>=1.9.9'] setup( author="LEA - Uni Paderborn", author_email='upblea@mail.upb.de', python_requires='>=3.7', classifiers=[ 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 'Natural Language :: English', 'Framework :: Sphinx', 'Topic :: Scientific/Engineering', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX :: Linux' ], description="OpenModelica Microgrid Gym", install_requires=requirements, license="GNU General Public License v3", long_description=readme + '\n\n' + history, include_package_data=True, keywords='openmodelica_microgrid_gym', name='openmodelica_microgrid_gym', packages=find_packages(include=['openmodelica_microgrid_gym', 'openmodelica_microgrid_gym.*']), setup_requires=setup_requirements, test_suite='tests', tests_require=test_requirements, extras_require={'examples': ['safeopt>=0.16', 'GPy>=1.9.9']}, url='https://github.com/upb-lea/openmodelica-microgrid-gym', project_urls={ "Documentation": "https://upb-lea.github.io/openmodelica-microgrid-gym/", "Source Code": "https://github.com/upb-lea/openmodelica-microgrid-gym", }, version='0.4.0', zip_safe=False, ) ================================================ FILE: tests/__init__.py ================================================ ================================================ FILE: tests/aux_ctl/test_base.py ================================================ import numpy as np from pytest import approx from openmodelica_microgrid_gym.aux_ctl.base import LimitLoadIntegral def test_limit_load_integral(): dt = .05 freq = 2 i_lim = 5 i2t = LimitLoadIntegral(dt, freq, i_lim=i_lim, i_nom=i_lim / 5) size = int(1 / freq / dt / 2) assert len(i2t._buffer) == size i2t.reset() seq = [5, 4] for i in seq: i2t.step(i) integral = i2t.integral assert integral == (np.power(seq, 2) * dt).sum() assert i2t.risk() == approx(.3) for i in [5, 4, 5, 5, 5, 5, 5, 5] + [0] * size + seq: i2t.step(i) integral = i2t.integral assert integral == (np.power(seq, 2) * dt).sum() ================================================ FILE: tests/aux_ctl/test_inverter_control.py ================================================ import numpy as np import pytest from pytest import approx from openmodelica_microgrid_gym.aux_ctl import * @pytest.fixture def seed(): np.random.seed(1) @pytest.fixture(scope='module') def droop_par(): return InverseDroopParams(1, 4) @pytest.fixture(scope='module') def pll_par(): return PLLParams(2, 3, (-1, 1)) def test_step(seed, pll_par, droop_par): ctl = MultiPhaseABCPIPIController(pll_par, pll_par, droop_par, droop_par, ts_sim=1) ctl.reset() ctl.prepare(np.random.random(3), np.random.random(3)) assert ctl.step() == approx([-1.0, -1.0, -1.0]) def test_step2(seed, droop_par, pll_par): ctl = MultiPhaseDQCurrentController(pll_par, pll_par, 1, droop_par, droop_par, ts_sim=1) ctl.reset() ctl.prepare(np.random.random(3), np.random.random(3), np.random.random(3)) mv = ctl.step() assert mv == approx([1, -1, 0.18577202]) def test_step3(seed, droop_par, pll_par): ctl = MultiPhaseDQ0PIPIController(pll_par, pll_par, droop_par, droop_par, ts_sim=3) ctl.reset() ctl.prepare(np.random.random(3), np.random.random(3)) mv = ctl.step() assert mv == approx([-1, -1, 0.3040774]) ================================================ FILE: tests/helpers.py ================================================ import numpy def nested_arrays_equal(a, b): """ Function to compare two (nested) lists/tuples of numpy arrays a and b from: https://stackoverflow.com/questions/31662528/compare-two-nested-lists-tuples-of-numpy-arrays/31662874#31662874 """ if isinstance(a, (list, tuple)) and isinstance(b, type(a)): for aa, bb in zip(a, b): if not nested_arrays_equal(aa, bb): return False # Short circuit return True else: # numpy arrays return numpy.all(a == b) ================================================ FILE: tests/net/test_net.py ================================================ import pytest from openmodelica_microgrid_gym.net import Network def test_load(): Network.load('net/net_valid.yaml') assert True def test_load_dup_inputs(): with pytest.raises(ValueError): Network.load('net/net_dupinputs.yaml') ================================================ FILE: tests/test__util_plot.py ================================================ import pytest from openmodelica_microgrid_gym.env import PlotTmpl v = [['a', 'b'], ['c', 'd']] tmpl = PlotTmpl(v, color=[None, ['C2', 'C1']], style=[None, '--']) @pytest.mark.parametrize('i,o', [[[k for k in PlotTmpl(v)], [('a', dict(c='C1')), ('b', dict(c='C2')), ('c', dict(c='C1')), ('d', dict(c='C2'))]], [[k for k in PlotTmpl(v, c=[None, ['C2', 'C1']])], [('a', dict(c='C1')), ('b', dict(c='C2')), ('c', dict(c='C2')), ('d', dict(c='C1'))]], [[k for k in PlotTmpl(v, color=[None, ['C2', 'C1']])], [('a', dict(color='C1')), ('b', dict(color='C2')), ('c', dict(color='C2')), ('d', dict(color='C1'))]], [tmpl[2], ('c', dict(color='C2', style='--'))], [tmpl[1], ('b', dict(color='C2'))]]) def test_plot_tmpl(i, o): assert i == o ================================================ FILE: tests/test_modelica.py ================================================ """ Testing the Environment using simple action inputs and verifying the simulation result. """ import gym import numpy as np import pytest from pytest import approx @pytest.fixture def env(): env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', viz_mode=None, model_path='omg_grid/test.fmu', net='net/net_test.yaml') return env @pytest.fixture def initialized_env(): initialized_env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', viz_mode=None, model_path='omg_grid/test.fmu', net='net/net_test.yaml', model_params={ 'lc1.capacitor1.v': lambda t: 200 if t == -1 else None, 'lc1.capacitor2.v': lambda t: 200 if t == -1 else None, 'lc1.capacitor3.v': lambda t: 200 if t == -1 else None, 'lc1.inductor1.i': lambda t: 5 if t == -1 else None, 'lc1.inductor2.i': lambda t: 5 if t == -1 else None, 'lc1.inductor3.i': lambda t: 5 if t == -1 else None, }) return initialized_env def test_reset(env): assert env.reset() == approx( [0., 0., 0., 0., 0., 0., 0., 0., 0., 325.10861867, -153.70643068, -171.40218799, 0.0314159265, 0., 0., 0., 0., 0., 0., 0., 0., 0.]) def test_step(env): np.random.seed(1) env.reset() obs, r, done, _ = env.step(np.random.random(6)) assert obs == approx([172.9788, 285.5633, 3.2967, 35.4845, 61.5703, -0.0585, 0.0000, 0.0000, 0.0000, 324.6152, -144.6233, -179.9919, 0.0628318531, 142.6508, 87.4849, 39.0866, 25.3610, 11.9275, 7.8399, 0.0000, 0.0000, 0.0000], 1e-3) assert r == 1 assert not done def test_proper_reset(env): # Test using initial values in env which are zero np.random.seed(1) actions = np.random.random((100, 6)) env.reset() for a in actions: env.step(a) state = str(env) + str(env.history.df) env.reset() for a in actions: env.step(a) assert state == str(env) + str(env.history.df) def test_proper_reset_init_env(initialized_env): # Test using initial values in env which are not zero np.random.seed(1) actions = np.random.random((100, 6)) initialized_env.reset() for a in actions: initialized_env.step(a) state = str(initialized_env) + str(initialized_env.history.df) initialized_env.reset() for a in actions: initialized_env.step(a) assert state == str(initialized_env) + str(initialized_env.history.df) def test_reset_after_init(initialized_env): """ Check reset. Are the values to be reset (see initialized_env.make) used in first step? """ np.random.seed(1) initialized_env.reset() assert all(initialized_env.model.obs[0:6] == [200, 200, 200, 5, 5, 5]) ================================================ FILE: tests/test_pd_convert.py ================================================ ================================================ FILE: tests/test_recorder.py ================================================ import numpy as np import pandas as pd from openmodelica_microgrid_gym.util import FullHistory def test__append(): rec = FullHistory(['a b c'.split()]) rec.reset() rec.append(np.array([1, 2, 3])) rec.append([3, 3, 3]) assert rec.df.equals(pd.DataFrame([dict(a=1, b=2, c=3), dict(a=3, b=3, c=3)])) ================================================ FILE: tests/test_runner.py ================================================ """ The Tests in this file are high level integration tests. Based on a pre-packed and fixed application example it is verified that the simulated state and action trajectories match expected values. If the simulated system behavior changes against baseline a problem within the software toolchain is likely. """ import gym import numpy as np import pandas as pd import pytest from pytest import approx from openmodelica_microgrid_gym import Runner, Agent from openmodelica_microgrid_gym.agents import StaticControlAgent from openmodelica_microgrid_gym.agents.util import MutableFloat from openmodelica_microgrid_gym.aux_ctl import * from openmodelica_microgrid_gym.net import Network from openmodelica_microgrid_gym.util import flatten @pytest.fixture def agent(): delta_t = 1e-4 nomFreq = 50 nomVoltPeak = 230 * 1.414 iLimit = 30 DroopGain = 40000.0 # W/Hz QDroopGain = 1000.0 # VAR/V mutable_params = dict(voltP=MutableFloat(25e-3), voltI=MutableFloat(60)) ctrl = [] # Voltage PI parameters for the current sourcing inverter voltage_dqp_iparams = PI_params(kP=mutable_params['voltP'], kI=mutable_params['voltI'], limits=(-iLimit, iLimit)) # Current PI parameters for the voltage sourcing inverter current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1)) # Droop of the active power Watt/Hz, delta_t droop_param = DroopParams(DroopGain, 0.005, nomFreq) # Droop of the reactive power VAR/Volt Var.s/Volt qdroop_param = DroopParams(QDroopGain, 0.002, nomVoltPeak) ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param, ts_sim=delta_t, name='master')) # Discrete controller implementation for a DQ based Current controller for the current sourcing inverter # Current PI parameters for the current sourcing inverter current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1)) # PI params for the PLL in the current forming inverter pll_params = PLLParams(kP=10, kI=200, limits=(-10000, 10000), f_nom=nomFreq) # Droop of the active power Watts/Hz, W.s/Hz droop_param = InverseDroopParams(DroopGain, 0, nomFreq, tau_filt=0.04) # Droop of the reactive power VAR/Volt Var.s/Volt qdroop_param = InverseDroopParams(100, 0, nomVoltPeak, tau_filt=0.01) ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, iLimit, droop_param, qdroop_param, ts_sim=delta_t, name='slave')) # validate that parameters can be changed later on agent = StaticControlAgent(ctrl, {'master': [[f'lc1.inductor{i + 1}.i' for i in range(3)], [f'lc1.capacitor{i + 1}.v' for i in range(3)]], 'slave': [[f'lcl1.inductor{i + 1}.i' for i in range(3)], [f'lcl1.capacitor{i + 1}.v' for i in range(3)], np.zeros(3)]}) return mutable_params, agent @pytest.fixture def agent_undersample(): delta_t = 1e-4 nomFreq = 50 nomVoltPeak = 230 * 1.414 iLimit = 30 DroopGain = 40000.0 # W/Hz QDroopGain = 1000.0 # VAR/V mutable_params = dict(voltP=MutableFloat(25e-3), voltI=MutableFloat(60)) ctrl = [] # Voltage PI parameters for the current sourcing inverter voltage_dqp_iparams = PI_params(kP=mutable_params['voltP'], kI=mutable_params['voltI'], limits=(-iLimit, iLimit)) # Current PI parameters for the voltage sourcing inverter current_dqp_iparams = PI_params(kP=0.012, kI=90, limits=(-1, 1)) # Droop of the active power Watt/Hz, delta_t droop_param = DroopParams(DroopGain, 0.005, nomFreq) # Droop of the reactive power VAR/Volt Var.s/Volt qdroop_param = DroopParams(QDroopGain, 0.002, nomVoltPeak) ctrl.append(MultiPhaseDQ0PIPIController(voltage_dqp_iparams, current_dqp_iparams, droop_param, qdroop_param, ts_sim=delta_t, ts_ctrl=2 * delta_t, name='master')) # Discrete controller implementation for a DQ based Current controller for the current sourcing inverter # Current PI parameters for the current sourcing inverter current_dqp_iparams = PI_params(kP=0.005, kI=200, limits=(-1, 1)) # PI params for the PLL in the current forming inverter pll_params = PLLParams(kP=10, kI=200, limits=(-10000, 10000), f_nom=nomFreq) # Droop of the active power Watts/Hz, W.s/Hz droop_param = InverseDroopParams(DroopGain, 0, nomFreq, tau_filt=0.04) # Droop of the reactive power VAR/Volt Var.s/Volt qdroop_param = InverseDroopParams(100, 0, nomVoltPeak, tau_filt=0.01) ctrl.append(MultiPhaseDQCurrentController(current_dqp_iparams, pll_params, iLimit, droop_param, qdroop_param, ts_sim=delta_t, ts_ctrl=5 * delta_t, name='slave')) # validate that parameters can be changed later on agent = StaticControlAgent(ctrl, {'master': [[f'lc1.inductor{i + 1}.i' for i in range(3)], [f'lc1.capacitor{i + 1}.v' for i in range(3)]], 'slave': [[f'lcl1.inductor{i + 1}.i' for i in range(3)], [f'lcl1.capacitor{i + 1}.v' for i in range(3)], np.zeros(3)]}) return agent @pytest.fixture() def env(): net = Network.load('net/net_test.yaml') env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1', viz_mode=None, model_path='omg_grid/test.fmu', max_episode_steps=100, net=net) return env, net.in_vars(), flatten(net.out_vars()) def test_main(agent, env): env, _, out_params = env runner = Runner(agent[1], env) runner.run(1) # env.history.df.to_hdf('tests/test_main.hd5', 'hist') df = env.history.df.head(100) df = df.reindex(sorted(df.columns), axis=1) df2 = pd.read_hdf('tests/test_main.hd5', 'hist').head(100) # noqa df2 = df2.reindex(sorted(df2.columns), axis=1) assert df[out_params].to_numpy() == approx(df2[out_params].to_numpy(), 5e-2) def test_main_paramchange(agent, env): params, agent = agent env, _, out_params = env runner = Runner(agent, env) params['voltP'].val = 4 runner.run(1) # env.history.df.to_hdf('tests/test_main2.hd5', 'hist') df = env.history.df.head(20) df = df.reindex(sorted(df.columns), axis=1) df2 = pd.read_hdf('tests/test_main.hd5', 'hist').head(20) # noqa df2 = df2.reindex(sorted(df2.columns), axis=1) assert df[out_params].to_numpy() != approx(df2[out_params].to_numpy(), 5e-3) df2 = pd.read_hdf('tests/test_main2.hd5', 'hist').head(20) # noqa df2 = df2.reindex(sorted(df2.columns), axis=1) assert df[out_params].to_numpy() == approx(df2[out_params].to_numpy(), 5e-3) def test_main_undersample(agent_undersample, env): env, _, out_params = env runner = Runner(agent_undersample, env) runner.run(1) # env.history.df.to_hdf('tests/test_main4.hd5', 'hist') df = env.history.df.head(11) df = df.reindex(sorted(df.columns), axis=1) df2 = pd.read_hdf('tests/test_main4.hd5', 'hist').head(11) # noqa df2 = df2.reindex(sorted(df2.columns), axis=1) assert df[out_params].to_numpy() == approx(df2[out_params].to_numpy(), 5e-2) def test_simpleagent(env): np.random.seed(1) env, inputs, out_params = env class RndAgent(Agent): def act(self, obs: pd.Series) -> np.ndarray: return np.random.random(len(inputs)) agent = RndAgent() runner = Runner(agent, env) runner.run(1) # env.history.df.to_hdf('tests/test_main3.hd5', 'hist') df = env.history.df.head(23) df = df.reindex(sorted(df.columns), axis=1) df2 = pd.read_hdf('tests/test_main3.hd5', 'hist').head(23) # noqa df2 = df2.reindex(sorted(df2.columns), axis=1) assert df[out_params].to_numpy() == approx(df2[out_params].to_numpy(), 5e-4) ================================================ FILE: tests/test_transforms.py ================================================ import numpy as np import pytest from pytest import approx from openmodelica_microgrid_gym.util import * @pytest.fixture def seed(): np.random.seed(1) def test_inst_reactive(seed): assert inst_reactive(np.random.random(3), np.random.random(3)) == approx(-0.07421999471678885) def test_inst_power(seed): assert inst_power(np.random.random(3), np.random.random(3)) == approx(0.23180175944821654) def test_inst_rms(seed): assert inst_rms(np.random.random(3)) == approx(0.4805464741106228) def test_dq0_to_abc(seed): assert dq0_to_abc(np.random.random(3), np.random.random(1)) == approx([0.18374714, 0.61133519, -0.79473921]) def test_dq0_to_abc_cos_sin(seed): assert dq0_to_abc_cos_sin(np.random.random(3), *np.random.random(2)) == approx([0.02048185, 0.23152558, -0.2516643]) def test_dq0_to_abc_cos_sin_power_inv(seed): assert dq0_to_abc_cos_sin_power_inv(np.random.random(3), *np.random.random(2)) == approx( [0.025085037842289073, 0.28355976715193565, -0.3082245650813462]) def test_abc_to_dq0(seed): assert abc_to_dq0(np.random.random(3), np.random.random(1)) == approx([0.15995477, 0.38566723, 0.37915362432069233]) def test_abc_to_dq0_cos_sin(seed): assert abc_to_dq0_cos_sin(np.random.random(3), *np.random.random(2)) == approx([0.07247014, 0.12015287, 0.37915362]) def test_abc_to_alpha_beta(seed): assert abc_to_alpha_beta(np.random.random(3)) == approx([0.03786838, 0.41580131]) ================================================ FILE: tests/util/__init__.py ================================================ ================================================ FILE: tests/util/test_fastqueue.py ================================================ import numpy as np import pytest from pytest import approx from openmodelica_microgrid_gym.util import Fastqueue def test_fastqueue_1_element(): np.random.seed(1) test_queue1d = Fastqueue(1) test_queue1d.clear() first_val = np.random.uniform() test_queue1d.shift(first_val) assert first_val == test_queue1d.shift(np.random.uniform()) def test_fastqueue_2_element(): np.random.seed(1) test_queue1d = Fastqueue(2) test_queue1d.clear() first_val = np.random.uniform() test_queue1d.shift(first_val) test_queue1d.shift(np.random.uniform()) assert first_val == test_queue1d.shift(np.random.uniform()) def test_fastqueue_3_elem(): np.random.seed(1) test_queue1d = Fastqueue(3) test_queue1d.clear() first_val = np.random.uniform() test_queue1d.shift(first_val) test_queue1d.shift(np.random.uniform()) test_queue1d.shift(np.random.uniform()) assert first_val == test_queue1d.shift(np.random.uniform()) def test_fastqueue2d(): np.random.seed(1) test_queue2d = Fastqueue(3, 2) test_queue2d.clear() first_val = np.random.uniform(size=2) test_queue2d.shift(first_val) test_queue2d.shift(np.random.uniform(size=2)) test_queue2d.shift(np.random.uniform(size=2)) assert first_val == approx(test_queue2d.shift(np.random.uniform(size=2))) def test_fastqueue_initialize(): q = Fastqueue(5) with pytest.raises(RuntimeError): q.shift(3) def test_fastqueue2d_not_random(): np.random.seed(1) test_queue2d = Fastqueue(3, 2) test_queue2d.clear() first_val = np.array([1, 2]) test_queue2d.shift(first_val) test_queue2d.shift(np.random.uniform(size=2)) test_queue2d.shift(np.random.uniform(size=2)) assert np.array([1, 2]) == approx(test_queue2d.shift(np.random.uniform(size=2))) ================================================ FILE: tests/util/test_flattendict.py ================================================ import numpy as np import pandas as pd import pytest from openmodelica_microgrid_gym.util import fill_params, flatten, nested_map, nested_depth conf = { 'lc1': [ ['inductor1.i', 'inductor2.i', 'inductor3.i'], ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']], 'lcl1': [['inductor1.i', 'inductor2.i', 'inductor3.i'], ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']]} conf2 = { 'lc1': [ ['inductor1.i', 'inductor2.i', 'inductor3.i'], ['capacitor1.v', 'capacitor2.v'], ['capacitor3.v']], 'lcl1': conf['lcl1']} conf3 = { 'lc1': [ ['inductor1.i', 'inductor2.i', 'inductor3.i'], ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']], 'lcl1': [['inductor1.i', 'inductor2.i', 'inductor3.i'], ['capacitor1.v', 'capacitor2.v', 'capacitor3.v']], 'pll': ['add_freq_nom_delta_f.y']} result_1 = [['lc1.inductor1.i', 'lc1.inductor2.i', 'lc1.inductor3.i'], ['lc1.capacitor1.v', 'lc1.capacitor2.v', 'lc1.capacitor3.v'], ['lcl1.inductor1.i', 'lcl1.inductor2.i', 'lcl1.inductor3.i'], ['lcl1.capacitor1.v', 'lcl1.capacitor2.v', 'lcl1.capacitor3.v']] result_1_2 = [['lc1.inductor1.i', 'lc1.inductor2.i', 'lc1.inductor3.i'], ['lc1.capacitor1.v', 'lc1.capacitor2.v'], ['lc1.capacitor3.v'], ['lcl1.inductor1.i', 'lcl1.inductor2.i', 'lcl1.inductor3.i'], ['lcl1.capacitor1.v', 'lcl1.capacitor2.v', 'lcl1.capacitor3.v']] result_0 = ['lc1.inductor1.i', 'lc1.inductor2.i', 'lc1.inductor3.i', 'lc1.capacitor1.v', 'lc1.capacitor2.v', 'lc1.capacitor3.v', 'lcl1.inductor1.i', 'lcl1.inductor2.i', 'lcl1.inductor3.i', 'lcl1.capacitor1.v', 'lcl1.capacitor2.v', 'lcl1.capacitor3.v'] result_3_0 = result_0 + ['pll.add_freq_nom_delta_f.y'] @pytest.mark.parametrize('i,o', [[[conf], result_0], [[result_1], result_0], [[conf2], result_0], [[conf3], result_3_0], [[conf, 1], result_1], [[result_1, 1], result_1], [[conf2, 1], result_1_2], [[result_1_2, None], result_1_2]]) def test_flatten(i, o): assert flatten(*i) == o def test_nested_map(): assert nested_map(lambda x: 'p' + x, ['a', 'b', 'c']) == ['pa', 'pb', 'pc'] def test_nested_map1(): assert np.array_equal(nested_map(len, np.array(['a', 'b', 'c'])), np.array([1, 1, 1])) @pytest.mark.parametrize('i,o', [[1, 0], [[1], 1], [[], 1], [[[], 1], 2], [result_1, 2], [result_1_2, 2]]) def test_nested_depth(i, o): assert nested_depth(i) == o @pytest.mark.parametrize('tmpl,data,result', [[dict(a=['a', 'b', 'c']), pd.Series(dict(a=1, b=2, c=3)), dict(a=[1, 2, 3])], [dict(a=[np.array(['a', 'b', 'c']), np.array(['d', 'b', 'c']), 1]), pd.Series(dict(a=1, b=2, c=3, d=4)), dict(a=[np.array([1., 2., 3.]), np.array([4., 2., 3.]), 1])]]) def test_fill_params(tmpl, data, result): # properly testing datastructures with nested numpy arrays is complicated because of "ambiguous truthness" assert str(fill_params(tmpl, data)) == str(result) ================================================ FILE: tests/util/test_itertools_flatten_together.py ================================================ import pytest from openmodelica_microgrid_gym.util.itertools_ import flatten_together @pytest.mark.parametrize('i,o', [[([1, 2, 3], [4, 5, 6]), [4, 5, 6]], [([1, 1], [0]), [0, 0]], [([[1, 1]], [0]), [0, 0]], [([[3, 2], [4]], [2, 3]), [2, 2, 3]], [([[3, 2], 4], [2, 3]), [2, 2, 3]], [([[3, 2], 4], 13), [13, 13, 13]], ]) def test_flatten_together(i, o): assert flatten_together(*i) == o def test_flatten_together_negative(): with pytest.raises(ValueError): # params don't match up flatten_together([[3, 3], [3, 3], [3, 3]], [[1], [2]]) def test_flatten_together_negative2(): with pytest.raises(ValueError): # to many nestings in values flatten_together([4, 4], [[1], [3]]) ================================================ FILE: tests/util/test_obs_template.py ================================================ import numpy as np import pytest from openmodelica_microgrid_gym.util import ObsTempl from tests.helpers import nested_arrays_equal @pytest.mark.parametrize('i,o', [[list('ab'), [np.array([1]), np.array([2])]], [list('a'), [np.array([1])]], [[['a', 'b']], [np.array([1, 2])]], [[['a', 'b'], ['c']], [np.array([1, 2]), np.array([3])]], [None, [np.array([1, 2, 3])]] ]) def test_obs_templ(i, o): tmpl = ObsTempl(list('abc'), i) assert nested_arrays_equal(o, tmpl.fill(np.array([1, 2, 3])))