Skip to content

Plugins#

Power Balance Models (PBM) supports extensions or "plugins" which act as wrappers preparing parameters and arguments before execution of the core API. These plugins may add additional commands to the CLI, or create the requirement input files to initialise a run.

Managing Plugins#

Plugin are installed by running:

powerbalance plugins install <plugin-directory>

and removed using the command:

powerbalance plugins remove <plugin-name>

you can view all currently installed plugins by running:

powerbalance plugins list

which will display the plugin name (used to remove plugins), and the path of the plugin relative to the power_balance/plugins internal directory.

Plugin Development#

Plugins are placed in the power_balance/plugins folder in order to be recognised by the central API, this process is automated by the powerbalance plugins install command when it is run on the plugin root directory. The command will search for the metadata file, plugin.toml, the location of which is taken to be the root of the plugin structure, and then create a new directory using the name of the plugin, as defined within the metadata file (e.g. if the key name in the plugin.toml file is "My Project", the plugin would be installed to power_balance/plugins/my_project). All files within the root location and descending directories are copied during the installation.

The plugin root directory must be importable, containing a __init__.py file:

.
├── commands.py
├── plugin_functions.py
├── __init__.py
├── plugin.toml
├── my_plugin_tab_template.jinja
└── tests

Verify your plugin is recognised by running powerbalance plugins to list those available. For configuration options see the relevant section here.

The plugin.toml File#

Plugins are defined within a TOML file which is placed in the root directory for that plugin. An example file may look like the following:

name = "My Plugin"
commands = [
    "commands:convert_data"
]

pre_run_script = "plugin_setup:prepare_args"

[options.run.select_myplugin_mode]
param_decls = ["--myplugin-mode"]
help = "Run mode for My Plugin"

[options.run.verbose]
param_decls = ["--verbose/--not-verbose"]
help = "Run in higher verbosity"

[options.run.myplugin_outfile]
param_decls = ["--myplugin-out", "-b", "mpout"]
help = "Output name for my plugin"
Key Description
name Name of the plugin.
commands List of additional click commmands to attach to the CLI (see below)
pre_run_script Script to run before pbm_main, the PBM main function call.
options A dictionary of additional options to append to the main powerbalance subcommands, currently only modifications to run is supported.

Appending Commands#

To append additional commands to the powerbalance command group you must provide the path to the commands as a list the format module_path:command_function.

In the example above the commands are defined in a file power_balance/plugins/my_plugin/commands.py:

import click
import os

from .plugin_commands import demo_func

@click.command()
@click.argument("input_file")
@click.option(
    "--format",
    "-f",
    "fmt",
    default="toml",
    help="Output file format: yaml, pickle, json, toml",
)
@click.option(
    "--output-dir", "-o", default=os.getcwd(), help="Output directory"
)
def convert_data(fmt: str, output_dir: str) -> None:
    demo_func(fmt, output_dir)
the plugin.toml statement:
commands = [
    "commands:convert_data"
]
then pointing to this click command.

Appending Options#

Further options can be added to the powerbalance run command, under the options.run key. These are defined with the same arguments given to click.option, where param_decls defines the flags and variable name for an option:

[options.run.myplugin_outfile]
param_decls = ["--myplugin-out", "-b", "mpout"]
help = "Output name for my plugin"

Modifying Parameters Before Setup#

If your plugin prepares parameters for a PBM run you will want to modify the inputs given during powerbalance run to use those prepared instead. The key pre_run_script can be given the path of a function.

This function must take a dictionary as an argument, this dictionary being the arguments given by the user to the CLI and thus the cli.session:pbm_main function. Your function should then update/add to this dictionary, and hence modify the inputs as required.

Argument Modification Clashes

Be very careful when modifying inputs, remember if running a simulation with more than one plugin each of these will modify the arguments. This may mean your plugin is not receiving the inputs you expect.

Displaying Plugin Outputs#

Plugins can themselves have displays, these are shown as additional tabs within the PBM browser. Displays are created as additional HTML content held within a Jinja template file. To get the correct expected name for your plugin template file consider loading your plugin.toml file into a variable to ensure the same name is used:

plugins/my_plugin/__init__.py

import toml
import path

PLUGIN_METADATA = toml.load(
    os.path.join(os.path.dirname(__file__), "plugin.toml")
)

plugins/my_plugin/results.py

...

_display_file = get_plugin_display_filename(PLUGIN_METADATA["name"])

with open(_display_file, 'w') as out_html:
    out_html.write(plugin_html_str)

...

If your display requires extra HTML files make sure they have a name unique to the plugin and copy them to the correct location using the PLUGIN_DISPLAY_DIR variable:

import shutil
from power_balance.plugins import get_plugin_display_filename, PLUGIN_DISPLAY_DIR

...

my_plugin_extra_html = "my_plugin_extras.html"
component_file = os.path.join(
    PLUGIN_DISPLAY_DIR,
    my_plugin_extra_html
)
shutil.copy(my_plugin_extra_html, component_file)

...

Last update: February 23, 2022