Showing preview only (1,043K chars total). Download the full file or copy to clipboard to get everything.
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 <upblea@mail.upb.de>
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. <https://fsf.org/>
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 <https://www.gnu.org/licenses/>.
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:
<program> Copyright (C) <year> <name of author>
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
<https://www.gnu.org/licenses/>.
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
<https://www.gnu.org/licenses/why-not-lgpl.html>.
================================================
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].

_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 <https://openmodelica.org/>`__ 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 <https://openmodelica.org/download/download-windows>`__ 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 <https://openmodelica.org/download/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 <Pythoncode.html>`__).
Setting of v\_DC
^^^^^^^^^^^^^^^^
The DC supply voltage *v*\_DC can be set either directly in the
OpenModelica model or via `Python <Pythoncode.html#setting-of-v-dc>`__.
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 <https://openmodelica.org/download/download-mac>`__ is hardly supported for Mac, they suggest to install in a Linux VM. For this reason, running OMG in a Linux `VM <https://openmodelica.org/download/virtual-machine>`__ is strongly recommended for Mac users!
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 <https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html>`__
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 <OpenModelica.md>`__.
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 <OpenModelica.html#setting-of-v-dc>`__ 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 <https://docs.python.org/3/library/logging.html>`__.
================================================
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 <fmu.html>`__ 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 <controller_tuning.html>`__.
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 <https://fmi-standard.org/>`__)
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 <https://openmodelica.org/download/download-windows>`__ 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 <OpenModelica.html>`__, 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 <https://www.openmodelica.org/>`__ 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) <https://fmi-standard.org/>`__.
The creation process of the FMU is shown `here <fmu.html>`__. It is used to
build a gym environment like in the examples from `OpenAI
Gym <https://gym.openai.com/>`__. 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 <examples.html#two-inverter-static-droop-control-py>`__.
.. 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 <examples.html#single-inverter-current-control-safe-opt-py>`__.
.. 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,
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
SYMBOL INDEX (484 symbols across 62 files)
FILE: examples/network_callbacks.py
function load_step (line 40) | def load_step(t, gain):
function update_legend (line 90) | def update_legend(fig):
function timeshift (line 127) | def timeshift(component, t):
FILE: examples/plotting.py
function second_plot (line 6) | def second_plot(fig):
FILE: examples/simple_agent.py
class RndAgent (line 8) | class RndAgent(Agent):
method act (line 9) | def act(self, obs: pd.Series) -> np.ndarray:
FILE: examples/single_inverter_current_control_safe_opt.py
class Reward (line 44) | class Reward:
method __init__ (line 45) | def __init__(self):
method set_idx (line 48) | def set_idx(self, obs):
method rew_fun (line 54) | def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:
function xylables (line 182) | def xylables(fig):
FILE: examples/single_inverter_voltage_current_control_safe_opt.py
class Reward (line 40) | class Reward:
method __init__ (line 41) | def __init__(self):
method set_idx (line 44) | def set_idx(self, obs):
method rew_fun (line 51) | def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:
function xylables_i (line 175) | def xylables_i(fig):
function xylables_v_abc (line 184) | def xylables_v_abc(fig):
function xylables_v_dq0 (line 193) | def xylables_v_dq0(fig):
FILE: examples/stocastic_load.py
function load_step (line 17) | def load_step(t):
function xylables (line 37) | def xylables(fig):
FILE: examples/two_inverter_droop_safe_opt.py
function load_step (line 46) | def load_step(t, gain):
class Reward (line 57) | class Reward:
method __init__ (line 58) | def __init__(self):
method set_idx (line 61) | def set_idx(self, obs):
method rew_fun (line 68) | def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:
function xylables_i (line 200) | def xylables_i(fig):
function xylables_v_abc (line 209) | def xylables_v_abc(fig):
function xylables_v_dq0 (line 218) | def xylables_v_dq0(fig):
function xylables_P_master (line 227) | def xylables_P_master(fig):
function xylables_P_slave (line 236) | def xylables_P_slave(fig):
function xylables_freq (line 245) | def xylables_freq(fig):
FILE: examples/two_inverter_static_droop_control.py
function load_step (line 38) | def load_step(t, gain):
FILE: experiments/model_validation/env/physical_testbench.py
class TestbenchEnv (line 9) | class TestbenchEnv(gym.Env):
method __init__ (line 13) | def __init__(self, host: str = '131.234.172.139', username: str = 'roo...
method __decode_result (line 48) | def __decode_result(ssh_result):
method rew_fun (line 71) | def rew_fun(self, Iabc_meas, Iabc_SP) -> float:
method reset (line 93) | def reset(self, kP, kI):
method step (line 133) | def step(self):
method render (line 153) | def render(self, J, save_folder):
FILE: experiments/model_validation/env/rewards.py
class Reward (line 8) | class Reward:
method __init__ (line 10) | def __init__(self, i_limit: float = 15, i_nominal: float = 10, v_limit...
method set_idx (line 37) | def set_idx(self, obs):
method rew_fun_c (line 45) | def rew_fun_c(self, cols: List[str], data: np.ndarray, risk) -> float:
method rew_fun_v (line 80) | def rew_fun_v(self, cols: List[str], data: np.ndarray, risk) -> float:
method rew_fun_vc (line 114) | def rew_fun_vc(self, cols: List[str], data: np.ndarray, risk) -> float:
method rew_fun_funnel (line 154) | def rew_fun_funnel(self, cols: List[str], data: np.ndarray, risk) -> f...
FILE: experiments/model_validation/env/stochastic_components.py
class Load (line 4) | class Load:
method __init__ (line 6) | def __init__(self, load_mean: float, load_std: float = None, balanced:...
method load_step (line 28) | def load_step(self, t, n: int):
method give_value (line 45) | def give_value(self, t, n: int):
method reset (line 62) | def reset(self):
FILE: experiments/model_validation/env/testbench_voltage_ctrl.py
class TestbenchEnvVoltage (line 15) | class TestbenchEnvVoltage(gym.Env):
method __init__ (line 20) | def __init__(self, host: str = '131.234.172.139', username: str = 'roo...
method __decode_result (line 58) | def __decode_result(ssh_result):
method __decode_result_obs (line 87) | def __decode_result_obs(ssh_result):
method rew_fun (line 109) | def rew_fun(self, vabc_meas, vsp_abc) -> float:
method reset (line 138) | def reset(self, kPv, kIv):
method rew_fun4D (line 189) | def rew_fun4D(self, vabc_meas, vsp_abc, iabc_meas, iabc_SP) -> float:
method reset4D (line 220) | def reset4D(self, kP, kI, kPv, kIv):
method step (line 269) | def step(self):
method render (line 299) | def render(self, J, save_folder):
FILE: experiments/model_validation/execution/monte_carlo_runner.py
class MonteCarloRunner (line 8) | class MonteCarloRunner:
method __init__ (line 21) | def __init__(self, agent: EpisodicLearnerAgent, env: ModelicaEnv):
method run (line 39) | def run(self, n_episodes: int = 10, n_mc: int = 5, visualise: bool = F...
FILE: experiments/model_validation/execution/runner_hardware.py
class RunnerHardware (line 11) | class RunnerHardware:
method __init__ (line 17) | def __init__(self, agent: Agent, env: TestbenchEnv):
method run (line 37) | def run(self, n_episodes: int = 10, visualise: bool = False, save_fold...
class RunnerHardwareGradient (line 79) | class RunnerHardwareGradient:
method __init__ (line 86) | def __init__(self, agent: Agent, env: TestbenchEnvVoltage):
method run (line 106) | def run(self, n_episodes: int = 10, visualise: bool = False, save_fold...
FILE: experiments/model_validation/lengthScaleSweepMC650.py
function pairwise (line 99) | def pairwise(iterable):
function cal_j_min (line 106) | def cal_j_min(phase_shift, amp_dev):
function run_experiment (line 143) | def run_experiment(len_kp, len_ki):
FILE: experiments/model_validation/single_inverter_current_control_safe_opt_includingTB.py
function pairwise (line 97) | def pairwise(iterable):
function cal_j_min (line 104) | def cal_j_min(phase_shift, amp_dev):
function reset_loads (line 261) | def reset_loads():
function reference_step (line 270) | def reference_step(t):
FILE: experiments/model_validation/single_inverter_voltage_current_control_safe_opt_includingTB.py
function pairwise (line 117) | def pairwise(iterable):
function cal_J_min (line 124) | def cal_J_min(phase_shift, amp_dev):
function reset_loads (line 285) | def reset_loads():
FILE: experiments/model_validation/single_inverter_voltage_current_control_safe_opt_includingTB_4D.py
function reset_loads (line 240) | def reset_loads():
FILE: experiments/testing_framework_control/metrics.py
class Metrics (line 11) | class Metrics:
method __init__ (line 12) | def __init__(self, quantity, ref_value: float, ts: float, max_episode_...
method overshoot (line 41) | def overshoot(self):
method rise_time (line 56) | def rise_time(self):
method settling_time (line 69) | def settling_time(self):
method settling_time_vd_droop (line 103) | def settling_time_vd_droop(
method RMSE (line 118) | def RMSE(self):
method steady_state_error (line 130) | def steady_state_error(self):
method absolute_peak (line 141) | def absolute_peak(self):
FILE: experiments/testing_framework_control/tf_innerlevel_idq.py
function load_step_random_walk_resistor (line 71) | def load_step_random_walk_resistor():
function load_step_inductance_random_walk (line 81) | def load_step_inductance_random_walk():
class Reward (line 95) | class Reward:
method __init__ (line 96) | def __init__(self):
method set_idx (line 99) | def set_idx(self, obs):
method rew_fun (line 106) | def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:
class LoadstepCallback (line 145) | class LoadstepCallback(Callback):
method __init__ (line 146) | def __init__(self):
method set_idx (line 158) | def set_idx(self, obs):
method reset (line 165) | def reset(self):
method __call__ (line 168) | def __call__(self, cols, obs):
method load_step_resistance (line 195) | def load_step_resistance(self, t):
method load_step_inductance (line 206) | def load_step_inductance(self, t):
function xylables (line 311) | def xylables(fig):
function xylables_R (line 318) | def xylables_R(fig):
function xylables_L (line 325) | def xylables_L(fig):
function xylables_i_dq0 (line 332) | def xylables_i_dq0(fig):
FILE: experiments/testing_framework_control/tf_innerlevel_vdq.py
function load_step_random_walk_resistor (line 60) | def load_step_random_walk_resistor():
function load_step_inductance_random_walk (line 70) | def load_step_inductance_random_walk():
class Reward (line 89) | class Reward:
method __init__ (line 90) | def __init__(self):
method set_idx (line 93) | def set_idx(self, obs): # [f'lc1.inductor{k}.i' for k in '123']
method rew_fun (line 100) | def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:
class LoadstepCallback (line 142) | class LoadstepCallback(Callback):
method __init__ (line 143) | def __init__(self):
method set_idx (line 154) | def set_idx(self, obs):
method reset (line 162) | def reset(self):
method __call__ (line 165) | def __call__(self, cols, obs):
method load_step_resistance (line 192) | def load_step_resistance(self, t):
method load_step_inductance (line 203) | def load_step_inductance(self, t):
function xylables_i (line 303) | def xylables_i(fig):
function xylables_v_abc (line 312) | def xylables_v_abc(fig):
function xylables_R (line 321) | def xylables_R(fig):
function xylables_i_dq0 (line 330) | def xylables_i_dq0(fig):
function xylables_v_dq0 (line 339) | def xylables_v_dq0(fig):
function xylables_L (line 348) | def xylables_L(fig):
FILE: experiments/testing_framework_control/tf_primarylevel_vdq_slavefreq.py
function load_step_random_walk_resistor (line 68) | def load_step_random_walk_resistor():
function load_step_inductance_random_walk (line 77) | def load_step_inductance_random_walk():
class Reward (line 90) | class Reward:
method __init__ (line 91) | def __init__(self):
method set_idx (line 94) | def set_idx(self, obs):
method rew_fun (line 101) | def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float:
class LoadstepCallback (line 133) | class LoadstepCallback(Callback):
method __init__ (line 134) | def __init__(self):
method set_idx (line 149) | def set_idx(self, obs):
method reset (line 156) | def reset(self):
method __call__ (line 159) | def __call__(self, cols, obs):
method load_step_resistance (line 193) | def load_step_resistance(self, t):
method load_step_inductance (line 204) | def load_step_inductance(self, t):
function xylables_i (line 320) | def xylables_i(fig):
function xylables_v_abc (line 329) | def xylables_v_abc(fig):
function xylables_v_dq0 (line 338) | def xylables_v_dq0(fig):
function xylables_P_master (line 347) | def xylables_P_master(fig):
function xylables_P_slave (line 356) | def xylables_P_slave(fig):
function xylables_freq (line 365) | def xylables_freq(fig):
function xylables_R (line 375) | def xylables_R(fig):
function xylables_L (line 382) | def xylables_L(fig):
FILE: omg_grid/merge_fmus.py
function get_fmu_key (line 13) | def get_fmu_key(path):
function copy_binaries (line 25) | def copy_binaries(wd, fmus, binaries):
FILE: openmodelica_microgrid_gym/agents/agent.py
class Agent (line 10) | class Agent:
method __init__ (line 11) | def __init__(self, obs_varnames: List[str] = None, history: EmptyHisto...
method reset (line 27) | def reset(self):
method act (line 34) | def act(self, obs: np.ndarray) -> np.ndarray:
method observe (line 43) | def observe(self, reward: float, terminated: bool):
method measurement_cols (line 54) | def measurement_cols(self) -> List[Union[List, str]]:
method measure (line 62) | def measure(self, obs: np.ndarray) -> np.ndarray:
method render (line 73) | def render(self) -> Figure:
method prepare_episode (line 79) | def prepare_episode(self):
method has_improved (line 86) | def has_improved(self) -> bool:
method has_worsened (line 95) | def has_worsened(self) -> bool:
FILE: openmodelica_microgrid_gym/agents/episodic.py
class EpisodicLearnerAgent (line 4) | class EpisodicLearnerAgent(Agent):
method observe (line 6) | def observe(self, reward: float, terminated: bool):
method update_params (line 15) | def update_params(self):
method performance (line 19) | def performance(self):
method performance (line 23) | def performance(self, val):
FILE: openmodelica_microgrid_gym/agents/safeopt.py
class SafeOptAgent (line 20) | class SafeOptAgent(StaticControlAgent, EpisodicLearnerAgent):
method __init__ (line 21) | def __init__(self, mutable_params: Union[dict, list], abort_reward: in...
method reset (line 78) | def reset(self):
method observe (line 104) | def observe(self, reward, terminated):
method update_params (line 129) | def update_params(self):
method render (line 171) | def render(self) -> Figure:
method prepare_episode (line 202) | def prepare_episode(self):
method has_improved (line 211) | def has_improved(self) -> bool:
method has_worsened (line 220) | def has_worsened(self) -> bool:
method performance (line 229) | def performance(self):
method performance (line 263) | def performance(self, new_performance):
method min_performance (line 268) | def min_performance(self):
FILE: openmodelica_microgrid_gym/agents/staticctrl.py
class StaticControlAgent (line 12) | class StaticControlAgent(Agent):
method __init__ (line 13) | def __init__(self, ctrls: List[Controller], obs_template: Mapping[str,...
method obs_template (line 42) | def obs_template(self):
method act (line 53) | def act(self, state: np.ndarray):
method observe (line 63) | def observe(self, reward: float, terminated: bool):
method measurement_cols (line 78) | def measurement_cols(self) -> List[Union[List, str]]:
method measure (line 87) | def measure(self, state) -> np.ndarray:
method prepare_episode (line 101) | def prepare_episode(self):
method has_improved (line 110) | def has_improved(self) -> bool:
FILE: openmodelica_microgrid_gym/agents/util.py
class MutableFloat (line 4) | class MutableFloat:
method __init__ (line 5) | def __init__(self, f: float):
method __float__ (line 13) | def __float__(self):
method __repr__ (line 16) | def __repr__(self):
method val (line 20) | def val(self):
method val (line 30) | def val(self, v: float):
class MutableParams (line 34) | class MutableParams:
method __init__ (line 35) | def __init__(self, params: Sequence[MutableFloat]):
method reset (line 46) | def reset(self):
method __setitem__ (line 53) | def __setitem__(self, key, value):
method __getitem__ (line 60) | def __getitem__(self, item):
method __repr__ (line 65) | def __repr__(self):
FILE: openmodelica_microgrid_gym/aux_ctl/base.py
class DDS (line 10) | class DDS:
method __init__ (line 17) | def __init__(self, ts: float, dds_max: float = 1, theta_0: float = 0):
method reset (line 28) | def reset(self):
method step (line 34) | def step(self, freq: float):
class LimitLoadIntegral (line 51) | class LimitLoadIntegral:
method __init__ (line 52) | def __init__(self, dt, freq, i_lim, i_nom=None):
method reset (line 70) | def reset(self):
method step (line 74) | def step(self, value):
method risk (line 77) | def risk(self):
class PLL (line 81) | class PLL:
method __init__ (line 87) | def __init__(self, params: PLLParams, ts: float):
method step (line 101) | def step(self, v_abc: np.ndarray) -> Tuple[np.ndarray, float, float]:
method reset (line 121) | def reset(self):
method __phase_comp (line 125) | def __phase_comp(cos_sin_x: np.ndarray, cos_sin_i: np.ndarray):
FILE: openmodelica_microgrid_gym/aux_ctl/droop_controllers.py
class DroopController (line 5) | class DroopController(PT1Filter):
method __init__ (line 14) | def __init__(self, DroopParams, ts):
method step (line 24) | def step(self, val_in):
class InverseDroopController (line 36) | class InverseDroopController(DroopController):
method __init__ (line 46) | def __init__(self, DroopParams: InverseDroopParams, ts: float):
method step (line 58) | def step(self, val_in: float):
FILE: openmodelica_microgrid_gym/aux_ctl/filter.py
class Filter (line 4) | class Filter:
method step (line 9) | def step(self, value):
class PT1Filter (line 13) | class PT1Filter(Filter):
method __init__ (line 18) | def __init__(self, filtParams: DroopParams, ts: float):
method reset (line 27) | def reset(self):
method step (line 33) | def step(self, val_in):
FILE: openmodelica_microgrid_gym/aux_ctl/inverter_controllers.py
class Controller (line 19) | class Controller:
method __init__ (line 24) | def __init__(self, IPIParams: PI_params, ts_sim: float, ts_ctrl: Optio...
method _set_hist_cols (line 59) | def _set_hist_cols(self, cols):
method reset (line 67) | def reset(self):
method step (line 78) | def step(self):
method prepare (line 88) | def prepare(self, *args, **kwargs):
method control (line 101) | def control(self, *args, **kwargs) -> np.ndarray:
class VoltageCtl (line 114) | class VoltageCtl(Controller):
method __init__ (line 115) | def __init__(self, VPIParams: PI_params, IPIParams: PI_params, Pdroop_...
method reset (line 136) | def reset(self):
class CurrentCtl (line 144) | class CurrentCtl(Controller):
method __init__ (line 145) | def __init__(self, IPIParams: PI_params, pllPIParams: PLLParams, i_lim...
method reset (line 169) | def reset(self):
class MultiPhaseABCPIPIController (line 176) | class MultiPhaseABCPIPIController(VoltageCtl):
method control (line 184) | def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, **kwar...
class MultiPhaseDQ0PIPIController (line 208) | class MultiPhaseDQ0PIPIController(VoltageCtl):
method __init__ (line 216) | def __init__(self, VPIParams: PI_params, IPIParams: PI_params,
method reset (line 244) | def reset(self):
method control (line 249) | def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, **kwar...
class MultiPhaseDQCurrentController (line 307) | class MultiPhaseDQCurrentController(CurrentCtl):
method __init__ (line 319) | def __init__(self, IPIParams: PI_params, pllPIParams: PLLParams, i_lim...
method control (line 349) | def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, idq0SP...
class MultiPhaseDQCurrentSourcingController (line 401) | class MultiPhaseDQCurrentSourcingController(Controller):
method __init__ (line 410) | def __init__(self, IPIParams: PI_params, ts_sim: float, ts_ctrl: Optio...
method reset (line 428) | def reset(self):
method control (line 432) | def control(self, currentCV: np.ndarray, idq0SP: np.ndarray = np.zeros...
FILE: openmodelica_microgrid_gym/aux_ctl/observers.py
class Observer (line 6) | class Observer():
method __init__ (line 7) | def __init__(self):
method cal_estimate (line 10) | def cal_estimate(self, y, u):
class Lueneberger (line 22) | class Lueneberger(Observer):
method __init__ (line 24) | def __init__(self, A, B, C, L, ts, vDC):
method cal_estimate (line 34) | def cal_estimate(self, y, u):
FILE: openmodelica_microgrid_gym/aux_ctl/params.py
class FilterParams (line 12) | class FilterParams:
method __init__ (line 14) | def __init__(self, gain: Union[MutableFloat, float], tau: Union[Mutabl...
method gain (line 25) | def gain(self):
method tau (line 29) | def tau(self):
class DroopParams (line 33) | class DroopParams(FilterParams):
method __init__ (line 38) | def __init__(self, gain: Union[MutableFloat, float], tau: Union[Mutabl...
method gain (line 54) | def gain(self):
class InverseDroopParams (line 60) | class InverseDroopParams(DroopParams):
method __init__ (line 65) | def __init__(self, gain: Union[MutableFloat, float], tau: Union[Mutabl...
class PI_params (line 84) | class PI_params:
method __init__ (line 90) | def __init__(self, kP: Union[MutableFloat, float], kI: Union[MutableFl...
method kP (line 104) | def kP(self):
method kI (line 108) | def kI(self):
method limits (line 112) | def limits(self):
method kB (line 118) | def kB(self):
class PLLParams (line 122) | class PLLParams(PI_params):
method __init__ (line 127) | def __init__(self, kP: Union[MutableFloat, float], kI: Union[MutableFl...
method f_nom (line 143) | def f_nom(self):
method theta_0 (line 147) | def theta_0(self):
FILE: openmodelica_microgrid_gym/aux_ctl/pi_controllers.py
class PIController (line 11) | class PIController:
method __init__ (line 17) | def __init__(self, PI_param: PI_params, ts: float):
method reset (line 28) | def reset(self):
method step (line 34) | def step(self, error: float, feedforward: float = 0) -> float:
class MultiPhasePIController (line 51) | class MultiPhasePIController:
method __init__ (line 57) | def __init__(self, PI_param: PI_params, ts: float):
method reset (line 66) | def reset(self):
method step (line 73) | def step(self, SP: np.ndarray, CV: np.ndarray, feedforward: Optional[n...
FILE: openmodelica_microgrid_gym/env/modelica.py
class ModelicaEnv (line 22) | class ModelicaEnv(gym.Env):
method __init__ (line 30) | def __init__(self, net: Union[str, Network], time_start: float = 0,
method _calc_jac (line 198) | def _calc_jac(self, t, x) -> np.ndarray: # noqa
method _get_deriv (line 210) | def _get_deriv(self, t: float, x: np.ndarray) -> np.ndarray:
method _simulate (line 225) | def _simulate(self) -> np.ndarray:
method is_done (line 247) | def is_done(self) -> bool:
method reset (line 259) | def reset(self) -> np.ndarray:
method step (line 289) | def step(self, action: Sequence) -> Tuple[np.ndarray, float, bool, Map...
method _create_state (line 369) | def _create_state(self, is_init: bool = False):
method render (line 385) | def render(self, mode: str = 'human', close: bool = False) -> List[Fig...
method close (line 436) | def close(self) -> Tuple[bool, Any]:
FILE: openmodelica_microgrid_gym/env/plot.py
class PlotTmpl (line 10) | class PlotTmpl:
method __init__ (line 11) | def __init__(self, variables: List[Union[List, str]], callback: Option...
method callback (line 73) | def callback(self, fig: Figure):
method __iter__ (line 88) | def __iter__(self):
method __next__ (line 92) | def __next__(self):
method __getitem__ (line 99) | def __getitem__(self, item):
FILE: openmodelica_microgrid_gym/env/plotmanager.py
class PlotManager (line 8) | class PlotManager:
method __init__ (line 9) | def __init__(self, used_agent: SafeOptAgent,
method xylables_v_abc (line 25) | def xylables_v_abc(self, fig):
method xylables_v_dq0 (line 31) | def xylables_v_dq0(self, fig):
method xylables_i_abc (line 37) | def xylables_i_abc(self, fig):
method xylables_i_dq0 (line 43) | def xylables_i_dq0(self, fig):
method update_axes (line 49) | def update_axes(self, fig, title=None, xlabel=r'$t\,/\,\mathrm{s}$', y...
FILE: openmodelica_microgrid_gym/env/pyfmi.py
class PyFMI_Wrapper (line 12) | class PyFMI_Wrapper:
method __init__ (line 15) | def __init__(self, model):
method load (line 19) | def load(cls, path):
method setup (line 26) | def setup(self, time_start, output_names, model_params: Dict[str, Call...
method obs (line 53) | def obs(self):
method states (line 57) | def states(self):
method states (line 61) | def states(self, val):
method deriv (line 65) | def deriv(self):
method time (line 69) | def time(self):
method time (line 73) | def time(self, val):
method jacc (line 76) | def jacc(self):
method set (line 85) | def set(self, **kwargs):
method set_params (line 88) | def set_params(self, **kwargs):
FILE: openmodelica_microgrid_gym/execution/callbacks.py
class Callback (line 4) | class Callback(ABC):
method reset (line 5) | def reset(self):
method __call__ (line 8) | def __call__(self, *args, **kwargs):
FILE: openmodelica_microgrid_gym/execution/runner.py
class Runner (line 10) | class Runner:
method __init__ (line 16) | def __init__(self, agent: Agent, env: ModelicaEnv, callback: Optional[...
method run (line 35) | def run(self, n_episodes: int = 10, visualise: bool = False):
FILE: openmodelica_microgrid_gym/net/base.py
class Component (line 10) | class Component:
method __init__ (line 11) | def __init__(self, net: 'Network', id=None, in_vars=None, out_vars=Non...
method reset (line 34) | def reset(self):
method risk (line 37) | def risk(self) -> float:
method params (line 40) | def params(self, actions):
method get_in_vars (line 49) | def get_in_vars(self):
method get_out_vars (line 57) | def get_out_vars(self, with_aug=False):
method fill_tmpl (line 68) | def fill_tmpl(self, state: np.ndarray):
method set_outidx (line 75) | def set_outidx(self, keys):
method _prefix_var (line 86) | def _prefix_var(self, strs):
method calculate (line 97) | def calculate(self) -> Dict[str, np.ndarray]:
method normalize (line 113) | def normalize(self, calc_data):
method augment (line 120) | def augment(self, state: np.ndarray, t: float):
method extract_data (line 139) | def extract_data(self, calc_data):
class Network (line 163) | class Network:
method __init__ (line 171) | def __init__(self, ts: float, v_nom: Union[int, str], freq_nom: float ...
method _validate_load_data (line 177) | def _validate_load_data(data):
method load (line 189) | def load(cls, configurl='net.yaml'):
method risk (line 246) | def risk(self) -> float:
method reset (line 249) | def reset(self):
method params (line 253) | def params(self, actions):
method augment (line 266) | def augment(self, state: np.ndarray, t: float) -> Tuple[np.ndarray]:
method in_vars (line 279) | def in_vars(self):
method out_vars (line 282) | def out_vars(self, with_aug=True, flattened=True):
method components (line 289) | def components(self) -> List[Component]:
method components (line 293) | def components(self, val: List[Component]):
method __getitem__ (line 299) | def __getitem__(self, item):
FILE: openmodelica_microgrid_gym/net/components.py
class Inverter (line 13) | class Inverter(Component):
method __init__ (line 14) | def __init__(self, u=None, i=None, i_noise: Optional[dict] = None, v=N...
method reset (line 70) | def reset(self):
method normalize (line 73) | def normalize(self, calc_data):
method risk (line 78) | def risk(self) -> float:
method params (line 81) | def params(self, actions):
method calculate (line 84) | def calculate(self):
class SlaveInverter (line 93) | class SlaveInverter(Inverter):
method __init__ (line 94) | def __init__(self, pll=None, pdroop=None, qdroop=None, **kwargs):
method reset (line 109) | def reset(self):
method calculate (line 115) | def calculate(self):
class MasterInverter (line 121) | class MasterInverter(Inverter):
method __init__ (line 122) | def __init__(self, v_ref=(1, 0, 0), pdroop=None, qdroop=None, **kwargs):
method reset (line 134) | def reset(self):
method calculate (line 142) | def calculate(self):
method normalize (line 156) | def normalize(self, calc_data):
class MasterInverter_dq0 (line 161) | class MasterInverter_dq0(MasterInverter):
method calculate (line 166) | def calculate(self):
class MasterInverterCurrentSourcing (line 173) | class MasterInverterCurrentSourcing(Inverter):
method __init__ (line 174) | def __init__(self, f_nom=50, **kwargs):
method reset (line 180) | def reset(self):
method calculate (line 185) | def calculate(self):
class Load (line 192) | class Load(Component):
method __init__ (line 193) | def __init__(self, i=None, **kwargs):
method params (line 197) | def params(self, actions):
FILE: openmodelica_microgrid_gym/util/fastqueue.py
class Fastqueue (line 6) | class Fastqueue:
method __init__ (line 7) | def __init__(self, size: int, dim: Optional[int] = 1):
method shift (line 17) | def shift(self, val):
method __len__ (line 28) | def __len__(self):
method clear (line 31) | def clear(self):
method wrap_index (line 34) | def wrap_index(self, i):
FILE: openmodelica_microgrid_gym/util/itertools_.py
function flatten (line 8) | def flatten(data: Union[dict, list], remaining_levels: int = 0) -> list:
function nested_map (line 44) | def nested_map(fun: Callable, structure: Union[list, tuple, Mapping, np....
function nested_depth (line 67) | def nested_depth(structure: Any) -> int:
function fill_params (line 84) | def fill_params(template: Union[list, tuple, Mapping, np.ndarray], data:...
function flatten_together (line 103) | def flatten_together(structure: List[Union[List, Any]], values: Union[An...
FILE: openmodelica_microgrid_gym/util/obs_template.py
class ObsTempl (line 6) | class ObsTempl:
method __init__ (line 7) | def __init__(self, varnames: List[str], simple_tmpl: Optional[List[Uni...
method fill (line 37) | def fill(self, obs: np.ndarray) -> List[np.ndarray]:
FILE: openmodelica_microgrid_gym/util/randproc.py
class RandProcess (line 8) | class RandProcess:
method __init__ (line 9) | def __init__(self, process_cls: Type[BaseProcess], proc_kwargs=None, b...
method reset (line 30) | def reset(self, initial):
method sample (line 35) | def sample(self, t):
method reserve (line 56) | def reserve(self):
method reserve (line 63) | def reserve(self, v):
FILE: openmodelica_microgrid_gym/util/recorder.py
class StructuredMapping (line 8) | class StructuredMapping:
method __init__ (line 9) | def __init__(self, cols: List[Union[List, str]] = None, data=None):
method cols (line 22) | def cols(self) -> List[str]:
method cols (line 31) | def cols(self, val: List[Union[List, str]]):
method data (line 41) | def data(self):
method df (line 45) | def df(self):
method structured_cols (line 49) | def structured_cols(self, remaining_level: Optional[int] = 1) -> List[...
class EmptyHistory (line 58) | class EmptyHistory(StructuredMapping):
method reset (line 64) | def reset(self):
method pop (line 70) | def pop(self):
method append (line 77) | def append(self, values: Sequence):
method last (line 85) | def last(self):
method __getitem__ (line 88) | def __getitem__(self, item):
class SingleHistory (line 92) | class SingleHistory(EmptyHistory):
method reset (line 97) | def reset(self):
method pop (line 100) | def pop(self):
method append (line 105) | def append(self, values: Sequence):
method last (line 108) | def last(self):
class FullHistory (line 112) | class FullHistory(EmptyHistory):
method reset (line 117) | def reset(self):
method pop (line 120) | def pop(self):
method append (line 123) | def append(self, values: Sequence):
method last (line 126) | def last(self):
method df (line 130) | def df(self):
FILE: openmodelica_microgrid_gym/util/transforms.py
function dq0_to_abc (line 13) | def dq0_to_abc(dq0: np.ndarray, theta: float) -> np.ndarray:
function dq0_to_abc_cos_sin (line 25) | def dq0_to_abc_cos_sin(dq0: np.ndarray, cos: float, sin: float) -> np.nd...
function dq0_to_abc_cos_sin_power_inv (line 57) | def dq0_to_abc_cos_sin_power_inv(dq0: np.ndarray, cos: float, sin: float...
function abc_to_dq0 (line 76) | def abc_to_dq0(abc: np.ndarray, theta: float) -> np.ndarray:
function abc_to_dq0_cos_sin (line 88) | def abc_to_dq0_cos_sin(abc: np.ndarray, cos: float, sin: float) -> np.nd...
function abc_to_alpha_beta (line 120) | def abc_to_alpha_beta(abc: np.ndarray) -> np.ndarray:
function cos_sin (line 134) | def cos_sin(theta: float) -> np.ndarray:
function inst_rms (line 144) | def inst_rms(arr: np.ndarray) -> float:
function normalise_abc (line 154) | def normalise_abc(abc: np.ndarray) -> np.ndarray:
function inst_power (line 170) | def inst_power(varr: np.ndarray, iarr: np.ndarray) -> float:
function inst_reactive (line 181) | def inst_reactive(varr: np.ndarray, iarr: np.ndarray):
FILE: tests/aux_ctl/test_base.py
function test_limit_load_integral (line 7) | def test_limit_load_integral():
FILE: tests/aux_ctl/test_inverter_control.py
function seed (line 9) | def seed():
function droop_par (line 14) | def droop_par():
function pll_par (line 19) | def pll_par():
function test_step (line 23) | def test_step(seed, pll_par, droop_par):
function test_step2 (line 30) | def test_step2(seed, droop_par, pll_par):
function test_step3 (line 38) | def test_step3(seed, droop_par, pll_par):
FILE: tests/helpers.py
function nested_arrays_equal (line 4) | def nested_arrays_equal(a, b):
FILE: tests/net/test_net.py
function test_load (line 6) | def test_load():
function test_load_dup_inputs (line 11) | def test_load_dup_inputs():
FILE: tests/test__util_plot.py
function test_plot_tmpl (line 20) | def test_plot_tmpl(i, o):
FILE: tests/test_modelica.py
function env (line 12) | def env():
function initialized_env (line 21) | def initialized_env():
function test_reset (line 36) | def test_reset(env):
function test_step (line 42) | def test_step(env):
function test_proper_reset (line 54) | def test_proper_reset(env):
function test_proper_reset_init_env (line 69) | def test_proper_reset_init_env(initialized_env):
function test_reset_after_init (line 84) | def test_reset_after_init(initialized_env):
FILE: tests/test_recorder.py
function test__append (line 7) | def test__append():
FILE: tests/test_runner.py
function agent (line 22) | def agent():
function agent_undersample (line 66) | def agent_undersample():
function env (line 110) | def env():
function test_main (line 121) | def test_main(agent, env):
function test_main_paramchange (line 133) | def test_main_paramchange(agent, env):
function test_main_undersample (line 152) | def test_main_undersample(agent_undersample, env):
function test_simpleagent (line 165) | def test_simpleagent(env):
FILE: tests/test_transforms.py
function seed (line 9) | def seed():
function test_inst_reactive (line 13) | def test_inst_reactive(seed):
function test_inst_power (line 17) | def test_inst_power(seed):
function test_inst_rms (line 21) | def test_inst_rms(seed):
function test_dq0_to_abc (line 25) | def test_dq0_to_abc(seed):
function test_dq0_to_abc_cos_sin (line 29) | def test_dq0_to_abc_cos_sin(seed):
function test_dq0_to_abc_cos_sin_power_inv (line 33) | def test_dq0_to_abc_cos_sin_power_inv(seed):
function test_abc_to_dq0 (line 38) | def test_abc_to_dq0(seed):
function test_abc_to_dq0_cos_sin (line 42) | def test_abc_to_dq0_cos_sin(seed):
function test_abc_to_alpha_beta (line 46) | def test_abc_to_alpha_beta(seed):
FILE: tests/util/test_fastqueue.py
function test_fastqueue_1_element (line 8) | def test_fastqueue_1_element():
function test_fastqueue_2_element (line 17) | def test_fastqueue_2_element():
function test_fastqueue_3_elem (line 27) | def test_fastqueue_3_elem():
function test_fastqueue2d (line 38) | def test_fastqueue2d():
function test_fastqueue_initialize (line 49) | def test_fastqueue_initialize():
function test_fastqueue2d_not_random (line 55) | def test_fastqueue2d_not_random():
FILE: tests/util/test_flattendict.py
function test_flatten (line 50) | def test_flatten(i, o):
function test_nested_map (line 54) | def test_nested_map():
function test_nested_map1 (line 58) | def test_nested_map1():
function test_nested_depth (line 63) | def test_nested_depth(i, o):
function test_fill_params (line 72) | def test_fill_params(tmpl, data, result):
FILE: tests/util/test_itertools_flatten_together.py
function test_flatten_together (line 13) | def test_flatten_together(i, o):
function test_flatten_together_negative (line 17) | def test_flatten_together_negative():
function test_flatten_together_negative2 (line 23) | def test_flatten_together_negative2():
FILE: tests/util/test_obs_template.py
function test_obs_templ (line 14) | def test_obs_templ(i, o):
Condensed preview — 242 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,038K chars).
[
{
"path": ".github/workflows/build_and_test.yml",
"chars": 1950,
"preview": "name: Build\n\non:\n push:\n branches: [ master, develop ]\n pull_request:\n branches: [ master, develop ]\n workflow_"
},
{
"path": ".gitignore",
"chars": 935,
"preview": ".idea/\n__pycache__/\n.mypy_cache/\n*.fmu.txt\n\n.coverage\n\n# release\n/openmodelica_microgrid_gym.egg-info/\n/dist/\n/build/\n/."
},
{
"path": "AUTHORS.rst",
"chars": 163,
"preview": "=======\nCredits\n=======\n\nDevelopment Lead\n----------------\n\n* LEA - Uni Paderborn <upblea@mail.upb.de>\n\nContributors\n---"
},
{
"path": "CONTRIBUTING.rst",
"chars": 3531,
"preview": ".. highlight:: shell\n\n============\nContributing\n============\n\nContributions are welcome, and they are greatly appreciate"
},
{
"path": "HISTORY.rst",
"chars": 2633,
"preview": "=======\nHistory\n=======\n\nNext\n-------\n\n0.4.0 (2021-04-07)\n------------------\nChanges\n^^^^^^^\n* ModelicaEnv:\n - Introd"
},
{
"path": "LICENSE",
"chars": 35149,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "MANIFEST.in",
"chars": 262,
"preview": "include AUTHORS.rst\ninclude CONTRIBUTING.rst\ninclude HISTORY.rst\ninclude LICENSE\ninclude README.rst\n\nrecursive-include t"
},
{
"path": "Makefile",
"chars": 2273,
"preview": ".PHONY: clean clean-test clean-pyc clean-build docs help\n.DEFAULT_GOAL := help\n\ndefine BROWSER_PYSCRIPT\nimport os, webbr"
},
{
"path": "README.rst",
"chars": 8082,
"preview": "==========================\nOpenModelica Microgrid Gym\n==========================\n\n| |build| |cov| |nbsp| |nbsp| |python|"
},
{
"path": "docs/JOSS/paper.bib",
"chars": 6458,
"preview": "@Misc{Berkenkamp2020,\n author = {Felix Berkenkamp},\n title = {{SafeOpt: Safe Bayesian Optimization}},\n year = {202"
},
{
"path": "docs/JOSS/paper.md",
"chars": 9665,
"preview": "---\ntitle: 'OMG: A Scalable and Flexible Simulation and Testing Environment Toolbox for Intelligent Microgrid Control'\nt"
},
{
"path": "docs/Makefile",
"chars": 634,
"preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the "
},
{
"path": "docs/api/omg.agents.agent.rst",
"chars": 163,
"preview": "omg.agents.agent\n==================================\n\n.. automodule:: openmodelica_microgrid_gym.agents.agent\n :members"
},
{
"path": "docs/api/omg.agents.episodic.rst",
"chars": 186,
"preview": "omg.agents.episodic\n===================================================\n\n.. automodule:: openmodelica_microgrid_gym.agen"
},
{
"path": "docs/api/omg.agents.rst",
"chars": 326,
"preview": "omg.agents\n=============================\n\nSubmodules\n----------\n\n.. toctree::\n\n omg.agents.agent\n omg.agents.episodi"
},
{
"path": "docs/api/omg.agents.safeopt.rst",
"chars": 169,
"preview": "omg.agents.safeopt\n====================================\n\n.. automodule:: openmodelica_microgrid_gym.agents.safeopt\n :m"
},
{
"path": "docs/api/omg.agents.staticctrl.rst",
"chars": 178,
"preview": "omg.agents.staticctrl\n=======================================\n\n.. automodule:: openmodelica_microgrid_gym.agents.staticc"
},
{
"path": "docs/api/omg.agents.util.rst",
"chars": 243,
"preview": "omg.agents.util\n=================================\n\n.. automodule:: openmodelica_microgrid_gym.agents.util\n :members:\n "
},
{
"path": "docs/api/omg.aux_ctl.base.rst",
"chars": 167,
"preview": "omg.aux_ctl.base\n======================================\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl.base\n :mem"
},
{
"path": "docs/api/omg.aux_ctl.droop_controllers.rst",
"chars": 220,
"preview": "omg.aux\\_ctl.droop\\_controllers\n===============================================================\n\n.. automodule:: openmod"
},
{
"path": "docs/api/omg.aux_ctl.filter.rst",
"chars": 173,
"preview": "omg.aux_ctl.filter\n========================================\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl.filter\n "
},
{
"path": "docs/api/omg.aux_ctl.inverter_controllers.rst",
"chars": 210,
"preview": "omg.aux_ctl.inverter\\_controllers\n================================================\n\n.. automodule:: openmodelica_microgr"
},
{
"path": "docs/api/omg.aux_ctl.observers.rst",
"chars": 194,
"preview": "omg.aux\\_ctl.observers\n======================================================\n\n.. automodule:: openmodelica_microgrid_gy"
},
{
"path": "docs/api/omg.aux_ctl.params.rst",
"chars": 173,
"preview": "omg.aux_ctl.params\n========================================\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl.params\n "
},
{
"path": "docs/api/omg.aux_ctl.pi_controllers.rst",
"chars": 185,
"preview": "omg.aux_ctl.pi_controllers\n====================================\n\n.. automodule:: openmodelica_microgrid_gym.aux_ctl.pi_c"
},
{
"path": "docs/api/omg.aux_ctl.rst",
"chars": 412,
"preview": "omg.aux_ctl\n==================================\n\nSubmodules\n----------\n\n.. toctree::\n\n omg.aux_ctl.base\n omg.aux_ctl."
},
{
"path": "docs/api/omg.env.modelica.rst",
"chars": 163,
"preview": "omg.env.modelica\n==================================\n\n.. automodule:: openmodelica_microgrid_gym.env.modelica\n :members"
},
{
"path": "docs/api/omg.env.plot.rst",
"chars": 155,
"preview": "omg.env.plot\n==================================\n\n.. automodule:: openmodelica_microgrid_gym.env.plot\n :members:\n :un"
},
{
"path": "docs/api/omg.env.plotmanager.rst",
"chars": 186,
"preview": "omg.env.plotmanager\n===================================================\n\n.. automodule:: openmodelica_microgrid_gym.env."
},
{
"path": "docs/api/omg.env.pyfmi.rst",
"chars": 168,
"preview": "omg.env.pyfmi\n=============================================\n\n.. automodule:: openmodelica_microgrid_gym.env.pyfmi\n :me"
},
{
"path": "docs/api/omg.env.rst",
"chars": 284,
"preview": "omg.env\n==========================\n\nSubmodules\n----------\n\n.. toctree::\n\n omg.env.modelica\n omg.env.pyfmi\n omg.env"
},
{
"path": "docs/api/omg.execution.callbacks.rst",
"chars": 198,
"preview": "omg.execution.callbacks\n=======================================================\n\n.. automodule:: openmodelica_microgrid_"
},
{
"path": "docs/api/omg.execution.rst",
"chars": 277,
"preview": "omg.execution\n================================\n\nSubmodules\n----------\n\n.. toctree::\n\n omg.execution.runner\n omg.exec"
},
{
"path": "docs/api/omg.execution.runner.rst",
"chars": 175,
"preview": "omg.execution.runner\n======================================\n\n.. automodule:: openmodelica_microgrid_gym.execution.runner"
},
{
"path": "docs/api/omg.net.base.rst",
"chars": 165,
"preview": "omg.net.base\n============================================\n\n.. automodule:: openmodelica_microgrid_gym.net.base\n :membe"
},
{
"path": "docs/api/omg.net.components.rst",
"chars": 183,
"preview": "omg.net.components\n==================================================\n\n.. automodule:: openmodelica_microgrid_gym.net.co"
},
{
"path": "docs/api/omg.net.rst",
"chars": 276,
"preview": "omg.net\n========================================\n\nSubmodules\n----------\n\n.. toctree::\n :maxdepth: 4\n\n omg.net.base\n "
},
{
"path": "docs/api/omg.util.fastqueue.rst",
"chars": 183,
"preview": "omg.util.fastqueue\n==================================================\n\n.. automodule:: openmodelica_microgrid_gym.util.f"
},
{
"path": "docs/api/omg.util.itertools_.rst",
"chars": 176,
"preview": "omg.util.itertools\\_\n========================================\n\n.. automodule:: openmodelica_microgrid_gym.util.itertools"
},
{
"path": "docs/api/omg.util.randproc.rst",
"chars": 165,
"preview": "omg.util.randproc\n==================================\n\n.. automodule:: openmodelica_microgrid_gym.util.randproc\n :membe"
},
{
"path": "docs/api/omg.util.recorder.rst",
"chars": 165,
"preview": "omg.util.recorder\n==================================\n\n.. automodule:: openmodelica_microgrid_gym.util.recorder\n :membe"
},
{
"path": "docs/api/omg.util.rst",
"chars": 323,
"preview": "omg.util\n=============================\n\nSubmodules\n----------\n\n.. toctree::\n\n omg.util.fastqueue\n omg.util.itertools"
},
{
"path": "docs/api/omg.util.transforms.rst",
"chars": 174,
"preview": "omg.util.transforms\n=======================================\n\n.. automodule:: openmodelica_microgrid_gym.util.transforms\n"
},
{
"path": "docs/conf.py",
"chars": 5108,
"preview": "# -*- coding: utf-8 -*-\n#\n# Configuration file for the Sphinx documentation builder.\n#\n# This file does only contain a s"
},
{
"path": "docs/index.rst",
"chars": 1609,
"preview": "Welcome to OpenModelica Microgrid Gym Toolbox Documentation!\n==========================================================="
},
{
"path": "docs/make.bat",
"chars": 760,
"preview": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-bu"
},
{
"path": "docs/parts/user_guide/OpenModelica.rst",
"chars": 4962,
"preview": "OpenModelica\n============\n\n`OpenModelica <https://openmodelica.org/>`__ is an open-source\nModelica-based modeling and si"
},
{
"path": "docs/parts/user_guide/Pythoncode.rst",
"chars": 3909,
"preview": "Toolbox Installation and General Remarks\n================================\n\n\nIn the following, some installation hints an"
},
{
"path": "docs/parts/user_guide/controller_tuning.rst",
"chars": 2334,
"preview": "Controller Tuning Hints\n=======================\n\n1. Current controller of primary inverter\n\n- With no droop, in other w"
},
{
"path": "docs/parts/user_guide/examples/basic_agent.rst",
"chars": 908,
"preview": "Creating an agent and a runner\n==============================\n\nAdditionally to the environment, an an agent will be crea"
},
{
"path": "docs/parts/user_guide/examples/creating_env.rst",
"chars": 950,
"preview": "Creating an environment\n=======================\n\n\nFollowing is a minimal example how to set-up and run an environment.\nN"
},
{
"path": "docs/parts/user_guide/examples/plotting.rst",
"chars": 582,
"preview": "Creating plots of environment variables\n=======================================\n\nIn this example is shown how the enviro"
},
{
"path": "docs/parts/user_guide/examples/single_inverter_current_control_safe_opt.rst",
"chars": 6574,
"preview": "Single Inverter Current Control\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn this example a three phase inverter is supplying a l"
},
{
"path": "docs/parts/user_guide/examples/two_inverter_static_droop_control.rst",
"chars": 2536,
"preview": "Two Inverter Static Droop Control\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn this example, a FMU generated by OpenModelica as"
},
{
"path": "docs/parts/user_guide/examples.rst",
"chars": 1021,
"preview": "Examples\n========\n\n.. toctree::\n :maxdepth: 4\n :titlesonly:\n :hidden:\n\n examples/creating_env\n examples/basic_"
},
{
"path": "docs/parts/user_guide/fmu.rst",
"chars": 3756,
"preview": "Functional Mock-up Unit (FMU)\n=============================\n\nWhat is FMI and FMU?\n^^^^^^^^^^^^^^^^^^^^\n\nThe Functional M"
},
{
"path": "docs/parts/user_guide/getting_started.rst",
"chars": 3846,
"preview": "Getting Started\n===============\n\n.. figure:: ../../pictures/omg_flow.png\n :alt: \n\nThis user guide covers all of OpenMo"
},
{
"path": "examples/basic_env.py",
"chars": 398,
"preview": "import gym\n\nif __name__ == '__main__':\n env = gym.make('openmodelica_microgrid_gym:ModelicaEnv-v1',\n "
},
{
"path": "examples/basic_env_norm.py",
"chars": 457,
"preview": "import gym\n\nif __name__ == '__main__':\n env = gym.make('openmodelica_microgrid_gym:ModelicaEnv_test-v1',\n "
},
{
"path": "examples/network_callbacks.py",
"chars": 6480,
"preview": "#####################################\n# Example using a FMU by OpenModelica as gym environment containing two inverters,"
},
{
"path": "examples/plotting.py",
"chars": 1399,
"preview": "import gym\n\nfrom openmodelica_microgrid_gym.env import PlotTmpl\n\nif __name__ == '__main__':\n def second_plot(fig):\n "
},
{
"path": "examples/simple_agent.py",
"chars": 496,
"preview": "import gym\nimport numpy as np\nimport pandas as pd\n\nfrom openmodelica_microgrid_gym import Agent, Runner\n\n\nclass RndAgent"
},
{
"path": "examples/single_inverter_current_control_safe_opt.py",
"chars": 11552,
"preview": "#####################################\n# Example using a FMU by OpenModelica and SafeOpt algorithm to find optimal contro"
},
{
"path": "examples/single_inverter_voltage_current_control_safe_opt.py",
"chars": 12151,
"preview": "#####################################\n# Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal contr"
},
{
"path": "examples/stocastic_load.py",
"chars": 2048,
"preview": "from functools import partial\n\nimport gym\nimport matplotlib.pyplot as plt\nfrom stochastic.processes import VasicekProces"
},
{
"path": "examples/two_inverter_droop_safe_opt.py",
"chars": 14356,
"preview": "#####################################\n# Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal contr"
},
{
"path": "examples/two_inverter_static_droop_control.py",
"chars": 5526,
"preview": "#####################################\n# Example using a FMU by OpenModelica as gym environment containing two inverters,"
},
{
"path": "experiments/model_validation/env/physical_testbench.py",
"chars": 9318,
"preview": "import gym\nimport paramiko\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom time import strft"
},
{
"path": "experiments/model_validation/env/rewards.py",
"chars": 9650,
"preview": "from typing import List\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.util import nested_map, dq0_to_abc\n\n\nclass "
},
{
"path": "experiments/model_validation/env/stochastic_components.py",
"chars": 2547,
"preview": "import numpy as np\n\n\nclass Load:\n\n def __init__(self, load_mean: float, load_std: float = None, balanced: bool = True"
},
{
"path": "experiments/model_validation/env/testbench_voltage_ctrl.py",
"chars": 18095,
"preview": "from socket import socket\n\nimport gym\nimport paramiko\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as"
},
{
"path": "experiments/model_validation/execution/monte_carlo_runner.py",
"chars": 8033,
"preview": "import numpy as np\nfrom typing import Dict, Any\nfrom tqdm import tqdm\nfrom openmodelica_microgrid_gym.agents.episodic im"
},
{
"path": "experiments/model_validation/execution/runner_hardware.py",
"chars": 6944,
"preview": "from typing import Dict, Any\n\nfrom tqdm import tqdm\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.agents import Ag"
},
{
"path": "experiments/model_validation/lengthScaleSweepMC650.py",
"chars": 15740,
"preview": "#####################################\n# Experiment Multicore to search for propper lengthscale: Single inverter supplyin"
},
{
"path": "experiments/model_validation/single_inverter_current_control_safe_opt_includingTB.py",
"chars": 18572,
"preview": "#####################################\n# Experiment : Single inverter supplying current to an short circuit via a LR filt"
},
{
"path": "experiments/model_validation/single_inverter_voltage_current_control_safe_opt_includingTB.py",
"chars": 21949,
"preview": "#####################################\n# Experiment : Single voltage forming inverter supplying an RL-load via an LC-filt"
},
{
"path": "experiments/model_validation/single_inverter_voltage_current_control_safe_opt_includingTB_4D.py",
"chars": 18377,
"preview": "#####################################\n# Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal contr"
},
{
"path": "experiments/testing_framework_control/metrics.py",
"chars": 6169,
"preview": "from math import sqrt\n\nimport numpy as np\nfrom scipy.signal import argrelextrema\nfrom sklearn.metrics import mean_square"
},
{
"path": "experiments/testing_framework_control/net.yaml",
"chars": 952,
"preview": "v_nom: 230*sqrt(2)\nfreq_nom: 50\nts: .5e-4\n\ncomponents:\n inv1:\n id: inverter1\n #i_nom: 20\n #i_lim: 30\n #v_DC"
},
{
"path": "experiments/testing_framework_control/net_RL_load.yaml",
"chars": 1134,
"preview": "v_nom: 230*sqrt(2)\nfreq_nom: 50\nts: .5e-4\n\ncomponents:\n inv1:\n id: inverter1\n #i_nom: 20\n #i_lim: 30\n #v_DC"
},
{
"path": "experiments/testing_framework_control/net_single-inv-curr.yaml",
"chars": 550,
"preview": "v_nom: 230*sqrt(2)\n#freq_nom: 50\nts: 1e-4\n\ncomponents:\n inv1:\n id: inverter1\n #i_nom: 20\n #i_lim: 30\n v_DC:"
},
{
"path": "experiments/testing_framework_control/scoringmodel_innerlevel.py",
"chars": 12035,
"preview": "########################################################################\n# Scoring Model of the inner level to quantify "
},
{
"path": "experiments/testing_framework_control/scoringmodel_primarylevel.py",
"chars": 8404,
"preview": "########################################################################\n# Scoring Model of the primary level to quantif"
},
{
"path": "experiments/testing_framework_control/tf_innerlevel_idq.py",
"chars": 21640,
"preview": "#####################################\n# Example using a FMU by OpenModelica and SafeOpt algorithm to find optimal contro"
},
{
"path": "experiments/testing_framework_control/tf_innerlevel_vdq.py",
"chars": 22917,
"preview": "#####################################\n# Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal contr"
},
{
"path": "experiments/testing_framework_control/tf_primarylevel_vdq_slavefreq.py",
"chars": 24674,
"preview": "#####################################\n# Example using an FMU by OpenModelica and SafeOpt algorithm to find optimal contr"
},
{
"path": "net/net.yaml",
"chars": 1080,
"preview": "v_nom: 230*sqrt(2)\nfreq_nom: 50\nts: .5e-4\n\ncomponents:\n inv1:\n id: inverter1\n #i_nom: 20\n #i_lim: 30\n #v_DC"
},
{
"path": "net/net_dupinputs.yaml",
"chars": 781,
"preview": "v_nom: 230\n#freq_nom: 50\nts: .5e-4\n\ncomponents:\n inv1:\n #i_nom: 20\n #i_lim: 30\n #v_DC: 1000\n id: inverter1\n"
},
{
"path": "net/net_single-inv-Paper_Loadstep.yaml",
"chars": 844,
"preview": "v_nom: 169.7\nfreq_nom: 60\nts: 1e-4\n\ncomponents:\n inv1:\n id: inverter1\n #i_nom: 20\n #i_lim: 30\n v_DC: 600\n "
},
{
"path": "net/net_single-inv-curr.yaml",
"chars": 411,
"preview": "v_nom: 230*sqrt(2)\n#freq_nom: 50\nts: .5e-4\n\ncomponents:\n inv1:\n id: inverter1\n #i_nom: 20\n #i_lim: 30\n v_DC"
},
{
"path": "net/net_single-inv-curr_Paper_SC.yaml",
"chars": 559,
"preview": "v_nom: 230*sqrt(2)\nfreq_nom: 60\nts: 1e-4\n\ncomponents:\n inv1:\n id: inverter1\n #i_nom: 20\n #i_lim: 30\n v_DC: "
},
{
"path": "net/net_single-inv-volt.yaml",
"chars": 366,
"preview": "v_nom: 230*sqrt(2)\nfreq_nom: 50\nts: .5e-4\n\ncomponents:\n inv1:\n id: inverter1\n #i_nom: 20\n #i_lim: 30\n v_DC:"
},
{
"path": "net/net_singleinverter.yaml",
"chars": 474,
"preview": "v_nom: 230\n#freq_nom: 50\nts: .5e-4\n\ncomponents:\n inv1:\n cls: MasterInverter\n id: inverter1\n #i_nom: 20\n #i_"
},
{
"path": "net/net_static_droop_controller.yaml",
"chars": 812,
"preview": "# Simulation definitions\nts : 0.5e-4\n\nfreq_nom: 50 # grid frequency / Hz\nv_nom: 325.22 # nominal grid voltage / V\n\n\ncom"
},
{
"path": "net/net_test.yaml",
"chars": 668,
"preview": "v_nom: 230*sqrt(2)\nfreq_nom: 50\nts: 1e-4\n\ncomponents:\n inv1:\n id: inverter1\n #i_nom: 20\n #i_lim: 30\n v_DC: "
},
{
"path": "net/net_valid.yaml",
"chars": 756,
"preview": "v_nom: 230\n#freq_nom: 50\nts: .5e-4\n\ncomponents:\n inv1:\n #i_nom: 20\n #i_lim: 30\n #v_DC: 1000\n id: inverter1\n"
},
{
"path": "omg_grid/ActiveLoads/ActiveLoad.mo",
"chars": 8425,
"preview": "within omg_grid.ActiveLoads;\n\nmodel ActiveLoad\n parameter SI.Power p_ref(start = 5000);\n Modelica.Electrical.Analog.In"
},
{
"path": "omg_grid/ActiveLoads/package.mo",
"chars": 890,
"preview": "within omg_grid;\npackage ActiveLoads \n//extends Modelica.Icons.Package;\n\nannotation (Icon(coordinateSystem(preserveAspec"
},
{
"path": "omg_grid/ActiveLoads/package.order",
"chars": 11,
"preview": "ActiveLoad\n"
},
{
"path": "omg_grid/Components/PhaseAngle.mo",
"chars": 1200,
"preview": "within omg_grid.Components;\n\nmodel PhaseAngle\n parameter Real freq(start = 50);\n Modelica.Blocks.Math.Gain deg2rad(k ="
},
{
"path": "omg_grid/Components/StartValues.mo",
"chars": 1462,
"preview": "within omg_grid.Components;\n\nblock StartValues \"Output the input signal filtered with a low pass Butterworth filter of a"
},
{
"path": "omg_grid/Components/package.mo",
"chars": 96,
"preview": "within omg_grid;\npackage Components \n\n\nextends Modelica.Icons.UtilitiesPackage;\nend Components;\n"
},
{
"path": "omg_grid/Components/package.order",
"chars": 23,
"preview": "PhaseAngle\nStartValues\n"
},
{
"path": "omg_grid/Examples/ControlledNetworkSC.mo",
"chars": 6525,
"preview": "within omg_grid.Examples;\n\nmodel ControlledNetworkSC\n omg_grid.Inverters.Inverter inverter1(v_DC = 5000) annotation(\n "
},
{
"path": "omg_grid/Examples/ControlledNetworkSingleInverter.mo",
"chars": 6691,
"preview": "within omg_grid.Examples;\n\nmodel ControlledNetworkSingleInverter\n omg_grid.Inverters.Inverter inverter1 annotation(\n "
},
{
"path": "omg_grid/Examples/NetworkSineTest.mo",
"chars": 2346,
"preview": "within omg_grid.Examples;\n\nmodel NetworkSineTest\n\n Modelica.Blocks.Sources.Sine sine(amplitude = 230, freqHz = 50) anno"
},
{
"path": "omg_grid/Examples/PLL_Test.mo",
"chars": 2391,
"preview": "within omg_grid.Examples;\n\nmodel PLL_Test\n\n omg_grid.Transformations.ABC2AlphaBeta abc2AlphaBeta annotation(\n Placem"
},
{
"path": "omg_grid/Examples/package.mo",
"chars": 91,
"preview": "within omg_grid;\n\npackage Examples \nextends Modelica.Icons.ExamplesPackage;\n\nend Examples;\n"
},
{
"path": "omg_grid/Examples/package.order",
"chars": 77,
"preview": "NetworkSineTest\nControlledNetworkSingleInverter\nPLL_Test\nControlledNetworkSC\n"
},
{
"path": "omg_grid/Filter/IdealFilter/L.mo",
"chars": 3124,
"preview": "within omg_grid.Filter.IdealFilter;\n\nmodel L\n parameter SI.Inductance L1 = 0.001;\n parameter SI.Inductance L2 = 0.001;"
},
{
"path": "omg_grid/Filter/IdealFilter/LC.mo",
"chars": 4819,
"preview": "within omg_grid.Filter.IdealFilter;\n\nmodel LC\n parameter SI.Capacitance C1 = 0.00001;\n parameter SI.Capacitance C2 = 0"
},
{
"path": "omg_grid/Filter/IdealFilter/LCL.mo",
"chars": 5862,
"preview": "within omg_grid.Filter.IdealFilter;\n\nmodel LCL\n parameter SI.Capacitance C1 = 0.00001;\n parameter SI.Capacitance C2 = "
},
{
"path": "omg_grid/Filter/IdealFilter/LCLC.mo",
"chars": 7407,
"preview": "within omg_grid.Filter.IdealFilter;\n\nmodel LCLC\n parameter SI.Capacitance C1 = 0.00001;\n parameter SI.Capacitance C2 ="
},
{
"path": "omg_grid/Filter/IdealFilter/PI.mo",
"chars": 6183,
"preview": "within omg_grid.Filter.IdealFilter;\n\nmodel PI\n parameter SI.Capacitance C1 = 0.00001;\n parameter SI.Capacitance C2 = 0"
},
{
"path": "omg_grid/Filter/IdealFilter/package.mo",
"chars": 62,
"preview": "within omg_grid.Filter;\n\npackage IdealFilter\nend IdealFilter;\n"
},
{
"path": "omg_grid/Filter/IdealFilter/package.order",
"chars": 17,
"preview": "LC\nLCL\nPI\nLCLC\nL\n"
},
{
"path": "omg_grid/Filter/LossesFilter/L.mo",
"chars": 4208,
"preview": "within omg_grid.Filter.LossesFilter;\n\nmodel L\n parameter SI.Inductance L1 = 0.001;\n parameter SI.Inductance L2 = 0.001"
},
{
"path": "omg_grid/Filter/LossesFilter/LC.mo",
"chars": 6935,
"preview": "within omg_grid.Filter.LossesFilter;\n\nmodel LC\n parameter SI.Capacitance C1 = 0.00001;\n parameter SI.Capacitance C2 = "
},
{
"path": "omg_grid/Filter/LossesFilter/LCL.mo",
"chars": 8972,
"preview": "within omg_grid.Filter.LossesFilter;\n\nmodel LCL\n parameter SI.Capacitance C1 = 0.00001;\n parameter SI.Capacitance C2 ="
},
{
"path": "omg_grid/Filter/LossesFilter/LCLC.mo",
"chars": 11790,
"preview": "within omg_grid.Filter.LossesFilter;\n\nmodel LCLC\n parameter SI.Capacitance C1 = 0.00001;\n parameter SI.Capacitance C2 "
},
{
"path": "omg_grid/Filter/LossesFilter/PI.mo",
"chars": 9620,
"preview": "within omg_grid.Filter.LossesFilter;\n\nmodel PI\n parameter SI.Capacitance C1 = 0.00001;\n parameter SI.Capacitance C2 = "
},
{
"path": "omg_grid/Filter/LossesFilter/package.mo",
"chars": 64,
"preview": "within omg_grid.Filter;\n\npackage LossesFilter\nend LossesFilter;\n"
},
{
"path": "omg_grid/Filter/LossesFilter/package.order",
"chars": 17,
"preview": "LC\nLCL\nPI\nLCLC\nL\n"
},
{
"path": "omg_grid/Filter/package.mo",
"chars": 1245,
"preview": "within omg_grid;\npackage Filter \nextends Modelica.Icons.Package;\n\nannotation (Icon(\n coordinateSystem(preserveAspec"
},
{
"path": "omg_grid/Filter/package.order",
"chars": 25,
"preview": "IdealFilter\nLossesFilter\n"
},
{
"path": "omg_grid/Grids/Microgrid.mo",
"chars": 6730,
"preview": "within omg_grid.Grids;\n\nmodel Microgrid\n omg_grid.Inverters.Inverter inverter1 annotation(\n Placement(visible = true"
},
{
"path": "omg_grid/Grids/Network.mo",
"chars": 5227,
"preview": "within omg_grid.Grids;\n\nmodel Network\n omg_grid.Inverters.Inverter inverter1 annotation(\n Placement(visible = true, "
},
{
"path": "omg_grid/Grids/NetworkSineTest.bak-mo",
"chars": 2343,
"preview": "within omg_grid.Grids;\n\nmodel NetworkSineTest\n\n Modelica.Blocks.Sources.Sine sine(amplitude = 230, freqHz = 50) annotat"
},
{
"path": "omg_grid/Grids/NetworkSingleInverter.mo",
"chars": 2476,
"preview": "within omg_grid.Grids;\n\nmodel NetworkSingleInverter\n omg_grid.Inverters.Inverter inverter1 annotation(\n Placement(vi"
},
{
"path": "omg_grid/Grids/PLL.bak-mo",
"chars": 6963,
"preview": "within omg_grid.Grids;\n\nmodel PLL\n Modelica.Electrical.Analog.Interfaces.Pin a annotation(\n Placement(visible = true"
},
{
"path": "omg_grid/Grids/PLL_Network.mo",
"chars": 5886,
"preview": "within omg_grid.Grids;\n\nmodel PLL_Network\n omg_grid.Inverters.Inverter inverter1 annotation(\n Placement(visible = tr"
},
{
"path": "omg_grid/Grids/RLC_Network.mo",
"chars": 5244,
"preview": "within omg_grid.Grids;\n\nmodel RLC_Network\n omg_grid.Inverters.Inverter inverter1 annotation(\n Placement(visible = tr"
},
{
"path": "omg_grid/Grids/SingleModel.mo",
"chars": 4254,
"preview": "within omg_grid.Grids;\n\nmodel SingleModel\n omg_grid.Inverters.Inverter inverter1 annotation(\n Placement(visible = tr"
},
{
"path": "omg_grid/Grids/Testbench_SC2.mo",
"chars": 2477,
"preview": "within omg_grid.Grids;\n\nmodel Testbench_SC2\n omg_grid.Inverters.Inverter inverter1(v_DC = 60) annotation(\n Placemen"
},
{
"path": "omg_grid/Grids/package.mo",
"chars": 918,
"preview": "within omg_grid;\n\npackage Grids \nextends Modelica.Icons.Package;\nannotation (Icon(coordinateSystem(preserveAspectRatio=t"
},
{
"path": "omg_grid/Grids/package.order",
"chars": 114,
"preview": "Network\nNetworkSingleInverter\nPLL_Network\nRLC_Network\nSingleModel\nMicrogrid\nTestbench_SC2\nPaper_SC\nPaper_Loadstep\n"
},
{
"path": "omg_grid/Inverters/Inverter.mo",
"chars": 4804,
"preview": "within omg_grid.Inverters;\n\nmodel Inverter\n parameter Real v_DC = 1000;\n Modelica.Electrical.Analog.Basic.Ground groun"
},
{
"path": "omg_grid/Inverters/package.mo",
"chars": 558,
"preview": "within omg_grid;\npackage Inverters \nextends Modelica.Icons.Package;\n\nannotation (Icon(graphics={Line(\n points={"
},
{
"path": "omg_grid/Inverters/package.order",
"chars": 9,
"preview": "Inverter\n"
},
{
"path": "omg_grid/Loads/C.mo",
"chars": 2448,
"preview": "within omg_grid.Loads;\n\nmodel C\n parameter SI.Capacitance C1 = 0.00001;\n parameter SI.Capacitance C2 = 0.00001;\n para"
},
{
"path": "omg_grid/Loads/L.mo",
"chars": 2510,
"preview": "within omg_grid.Loads;\n\nmodel L\n parameter SI.Inductance L1 = 0.001;\n parameter SI.Inductance L2 = 0.001;\n parameter "
},
{
"path": "omg_grid/Loads/LC.mo",
"chars": 3731,
"preview": "within omg_grid.Loads;\n\nmodel LC\n parameter SI.Capacitance C1(start = 0.00001);\n parameter SI.Capacitance C2(start = 0"
},
{
"path": "omg_grid/Loads/R.mo",
"chars": 2517,
"preview": "within omg_grid.Loads;\n\nmodel R\n parameter SI.Resistance R1 = 20;\n parameter SI.Resistance R2 = 20;\n parameter SI.Res"
},
{
"path": "omg_grid/Loads/RC.mo",
"chars": 4155,
"preview": "within omg_grid.Loads;\n\nmodel RC\n parameter SI.Resistance R1 = 20;\n parameter SI.Resistance R2 = 20;\n parameter SI.Re"
},
{
"path": "omg_grid/Loads/RL.mo",
"chars": 3539,
"preview": "within omg_grid.Loads;\n\nmodel RL\n parameter SI.Resistance R1 = 20;\n parameter SI.Resistance R2 = 20;\n parameter SI.Re"
},
{
"path": "omg_grid/Loads/RLC.mo",
"chars": 4668,
"preview": "within omg_grid.Loads;\n\nmodel RLC\n parameter SI.Resistance R1 = 20;\n parameter SI.Resistance R2 = 20;\n parameter SI.R"
},
{
"path": "omg_grid/Loads/package.mo",
"chars": 1024,
"preview": "within omg_grid;\npackage Loads \nextends Modelica.Icons.Package;\n\nannotation (Icon(coordinateSystem(preserveAspectRatio=t"
},
{
"path": "omg_grid/Loads/package.order",
"chars": 19,
"preview": "RL\nRC\nRLC\nR\nLC\nC\nL\n"
},
{
"path": "omg_grid/PLLs/Inverter.bak-mo",
"chars": 4799,
"preview": "within omg_grid.PLLs;\n\nmodel Inverter\n parameter Real v_DC = 1000;\n Modelica.Electrical.Analog.Basic.Ground ground1 an"
},
{
"path": "omg_grid/PLLs/PLL.mo",
"chars": 6962,
"preview": "within omg_grid.PLLs;\n\nmodel PLL\n Modelica.Electrical.Analog.Interfaces.Pin a annotation(\n Placement(visible = true,"
},
{
"path": "omg_grid/PLLs/PLL_DQ.mo",
"chars": 17982,
"preview": "within omg_grid.PLLs;\n\nmodel PLL_DQ\n Real Pi = 3.14159265;\n Modelica.Electrical.Analog.Interfaces.Pin a annotation(\n "
},
{
"path": "omg_grid/PLLs/package.mo",
"chars": 82,
"preview": "within omg_grid;\n\npackage PLLs \nextends Modelica.Icons.SensorsPackage;\n\nend PLLs;\n"
},
{
"path": "omg_grid/PLLs/package.order",
"chars": 11,
"preview": "PLL\nPLL_DQ\n"
},
{
"path": "omg_grid/Transformations/ABC2AlphaBeta.mo",
"chars": 4114,
"preview": "within omg_grid.Transformations;\n\nmodel ABC2AlphaBeta\n Modelica.Blocks.Interfaces.RealInput a annotation(\n Placement"
},
{
"path": "omg_grid/Transformations/ABC2DQ_Currents.mo",
"chars": 13872,
"preview": "within omg_grid.Transformations;\n\nmodel ABC2DQ_Currents\n Real Pi = 3.14159265;\n Modelica.Electrical.Analog.Interfaces."
},
{
"path": "omg_grid/Transformations/DQ2ABC.mo",
"chars": 9919,
"preview": "within omg_grid.Transformations;\n\nmodel DQ2ABC\n Real pi = 2 * Modelica.Math.asin(1.0);\n Modelica.Blocks.Interfaces.Rea"
},
{
"path": "omg_grid/Transformations/package.mo",
"chars": 791,
"preview": "within omg_grid;\npackage Transformations \nextends Modelica.Icons.Package;\n\nannotation (Icon(graphics={\n Rectangle"
},
{
"path": "omg_grid/Transformations/package.order",
"chars": 37,
"preview": "ABC2AlphaBeta\nDQ2ABC\nABC2DQ_Currents\n"
},
{
"path": "omg_grid/UsersGuide/Contact.mo",
"chars": 682,
"preview": "within omg_grid.UsersGuide;\nmodel Contact \"Contact\"\n extends Modelica.Icons.Contact;\n\n annotation (\n Documentation("
},
{
"path": "omg_grid/UsersGuide/ModelicaLicense2.mo",
"chars": 31834,
"preview": "within omg_grid.UsersGuide;\nclass ModelicaLicense2 \"Modelica License 2\"\n extends Modelica.Icons.Information;\n\n annotat"
},
{
"path": "omg_grid/UsersGuide/package.mo",
"chars": 286,
"preview": "within omg_grid;\npackage UsersGuide \"User's guide\"\n extends Modelica.Icons.Information;\n\n\n\n annotation (DocumentationC"
},
{
"path": "omg_grid/UsersGuide/package.order",
"chars": 25,
"preview": "Contact\nModelicaLicense2\n"
},
{
"path": "omg_grid/create_fmu.mos",
"chars": 426,
"preview": "OpenModelica.Scripting.loadFile(\"grid.mo\"); getErrorString();\nsetCommandLineOptions(\"-d=newInst\"); getErrorString();\nset"
},
{
"path": "omg_grid/grid.mo",
"chars": 164783,
"preview": "package grid\n import SI = Modelica.SIunits;\n\n package filter\n model pi\n parameter SI.Capacitance C1 = 0.00001;"
},
{
"path": "omg_grid/merge_fmus.py",
"chars": 3005,
"preview": "from os import listdir, walk, chdir\nfrom os.path import join, basename, isdir\nfrom shutil import copytree\nfrom tempfile "
},
{
"path": "omg_grid/package.mo",
"chars": 1776,
"preview": "within ;\npackage omg_grid\n extends Modelica.Icons.Package;\n // Import mathematical constants and functions\n import SI"
},
{
"path": "omg_grid/package.order",
"chars": 93,
"preview": "UsersGuide\nFilter\nLoads\nActiveLoads\nInverters\nPLLs\nTransformations\nComponents\nGrids\nExamples\n"
},
{
"path": "openmodelica_microgrid_gym/__init__.py",
"chars": 623,
"preview": "import logging\n\nfrom gym.envs.registration import register\n\nfrom openmodelica_microgrid_gym.agents import Agent\nfrom ope"
},
{
"path": "openmodelica_microgrid_gym/agents/__init__.py",
"chars": 260,
"preview": "from openmodelica_microgrid_gym.agents.agent import Agent\nfrom openmodelica_microgrid_gym.agents.safeopt import SafeOptA"
},
{
"path": "openmodelica_microgrid_gym/agents/agent.py",
"chars": 3431,
"preview": "from typing import List, Union\n\nimport numpy as np\nfrom matplotlib.figure import Figure\n\nfrom openmodelica_microgrid_gym"
},
{
"path": "openmodelica_microgrid_gym/agents/episodic.py",
"chars": 604,
"preview": "from openmodelica_microgrid_gym.agents import Agent\n\n\nclass EpisodicLearnerAgent(Agent):\n\n def observe(self, reward: "
},
{
"path": "openmodelica_microgrid_gym/agents/safeopt.py",
"chars": 11577,
"preview": "import importlib\nimport logging\nfrom typing import Dict, Union, Any, List, Mapping\n\nimport GPy\nimport matplotlib.pyplot "
},
{
"path": "openmodelica_microgrid_gym/agents/staticctrl.py",
"chars": 4970,
"preview": "from itertools import chain\nfrom typing import List, Mapping, Union\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym"
},
{
"path": "openmodelica_microgrid_gym/agents/util.py",
"chars": 1688,
"preview": "from typing import Sequence\n\n\nclass MutableFloat:\n def __init__(self, f: float):\n \"\"\"\n Wrapper object t"
},
{
"path": "openmodelica_microgrid_gym/aux_ctl/__init__.py",
"chars": 373,
"preview": "from openmodelica_microgrid_gym.aux_ctl.inverter_controllers import *\nfrom openmodelica_microgrid_gym.aux_ctl.params imp"
},
{
"path": "openmodelica_microgrid_gym/aux_ctl/base.py",
"chars": 4582,
"preview": "from typing import Tuple\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.aux_ctl.params import PLLParams\nfrom openm"
},
{
"path": "openmodelica_microgrid_gym/aux_ctl/droop_controllers.py",
"chars": 2702,
"preview": "from openmodelica_microgrid_gym.aux_ctl.filter import PT1Filter\nfrom openmodelica_microgrid_gym.aux_ctl.params import In"
},
{
"path": "openmodelica_microgrid_gym/aux_ctl/filter.py",
"chars": 1222,
"preview": "from openmodelica_microgrid_gym.aux_ctl.params import DroopParams\n\n\nclass Filter:\n \"\"\"\n An empty Filter defining a"
},
{
"path": "openmodelica_microgrid_gym/aux_ctl/inverter_controllers.py",
"chars": 19706,
"preview": "import logging\nfrom typing import List\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.util import SingleHistory, E"
},
{
"path": "openmodelica_microgrid_gym/aux_ctl/observers.py",
"chars": 1461,
"preview": "import numpy as np\n\nN_PHASE = 3\n\n\nclass Observer():\n def __init__(self):\n pass\n\n def cal_estimate(self, y, "
},
{
"path": "openmodelica_microgrid_gym/aux_ctl/params.py",
"chars": 4571,
"preview": "\"\"\"\nThe parameter classes wrap controller parameters.\nThe fields are wrapped into properties in order to allow transpare"
},
{
"path": "openmodelica_microgrid_gym/aux_ctl/pi_controllers.py",
"chars": 3316,
"preview": "import logging\nfrom typing import Optional\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.aux_ctl.params import PI"
},
{
"path": "openmodelica_microgrid_gym/env/__init__.py",
"chars": 100,
"preview": "from .modelica import ModelicaEnv\nfrom .plot import PlotTmpl\n\n__all__ = ['ModelicaEnv', 'PlotTmpl']\n"
},
{
"path": "openmodelica_microgrid_gym/env/modelica.py",
"chars": 19799,
"preview": "import logging\nimport re\nfrom fnmatch import translate\nfrom functools import partial\nfrom typing import Sequence, Callab"
},
{
"path": "openmodelica_microgrid_gym/env/plot.py",
"chars": 4046,
"preview": "from typing import List, Union, Callable, Optional\n\nfrom matplotlib.figure import Figure\nimport matplotlib.pyplot as plt"
},
{
"path": "openmodelica_microgrid_gym/env/plotmanager.py",
"chars": 3729,
"preview": "import os.path as p\n\nimport matplotlib.pyplot as plt\n\nfrom openmodelica_microgrid_gym.agents import SafeOptAgent\n\n\nclass"
},
{
"path": "openmodelica_microgrid_gym/env/pyfmi.py",
"chars": 2980,
"preview": "import logging\nfrom datetime import datetime\nfrom os.path import basename\nfrom typing import Dict, Callable\n\nimport nump"
},
{
"path": "openmodelica_microgrid_gym/execution/__init__.py",
"chars": 165,
"preview": "from openmodelica_microgrid_gym.execution.callbacks import Callback\nfrom openmodelica_microgrid_gym.execution.runner imp"
},
{
"path": "openmodelica_microgrid_gym/execution/callbacks.py",
"chars": 135,
"preview": "from abc import ABC\n\n\nclass Callback(ABC):\n def reset(self):\n pass\n\n def __call__(self, *args, **kwargs):\n "
},
{
"path": "openmodelica_microgrid_gym/execution/runner.py",
"chars": 2945,
"preview": "from typing import Dict, Any, Optional\n\nfrom tqdm import tqdm\n\nfrom openmodelica_microgrid_gym.agents import Agent\nfrom "
},
{
"path": "openmodelica_microgrid_gym/net/__init__.py",
"chars": 202,
"preview": "from .components import MasterInverter, SlaveInverter, MasterInverterCurrentSourcing\nfrom .base import Network\n\n__all__ "
},
{
"path": "openmodelica_microgrid_gym/net/base.py",
"chars": 11531,
"preview": "from importlib import import_module\nfrom typing import List, Dict, Optional, Union, Tuple\n\nimport numexpr as ne\nimport n"
},
{
"path": "openmodelica_microgrid_gym/net/components.py",
"chars": 7189,
"preview": "from functools import partial\nfrom typing import Optional\n\nimport numpy as np\n\nfrom openmodelica_microgrid_gym.aux_ctl i"
},
{
"path": "openmodelica_microgrid_gym/util/__init__.py",
"chars": 862,
"preview": "from .fastqueue import Fastqueue\nfrom .itertools_ import nested_map, fill_params, nested_depth, flatten, flatten_togethe"
},
{
"path": "openmodelica_microgrid_gym/util/fastqueue.py",
"chars": 1149,
"preview": "from typing import Optional\n\nimport numpy as np\n\n\nclass Fastqueue:\n def __init__(self, size: int, dim: Optional[int] "
},
{
"path": "openmodelica_microgrid_gym/util/itertools_.py",
"chars": 5004,
"preview": "from typing import Callable, Mapping, Union, Any, List\n\nimport numpy as np\nimport pandas as pd\nfrom more_itertools impor"
},
{
"path": "openmodelica_microgrid_gym/util/obs_template.py",
"chars": 2277,
"preview": "from typing import List, Union, Optional\nimport numpy as np\nfrom openmodelica_microgrid_gym.agents.util import MutablePa"
},
{
"path": "openmodelica_microgrid_gym/util/randproc.py",
"chars": 2264,
"preview": "from typing import Type\n\nimport numpy as np\nfrom stochastic.processes import DiffusionProcess\nfrom stochastic.processes."
},
{
"path": "openmodelica_microgrid_gym/util/recorder.py",
"chars": 3063,
"preview": "from typing import Sequence, List, Optional, Union\n\nimport pandas as pd\n\nfrom openmodelica_microgrid_gym.util.itertools_"
},
{
"path": "openmodelica_microgrid_gym/util/transforms.py",
"chars": 5700,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Fri Jan 24 16:19:01 2020\n\n@author: jarren\n\nCommon static transforms used commonly"
}
]
// ... and 42 more files (download for full content)
About this extraction
This page contains the full source code of the upb-lea/openmodelica-microgrid-gym GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 242 files (977.8 KB), approximately 290.4k tokens, and a symbol index with 484 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.