Base Example Co-Simulation

The Base Example walks through a simple HELICS co-simulation between two python federates. This example also serves as the recommended defaults for setting up a co-simulation. The base example described here will go into detail about the necessary components of a HELICS program. Subsequent examples in the Fundamental Examples section will change small components of the system.

The Base Example tutorial is organized as follows:

Example files

All files necessary to run the Base Example can be found in the Fundamental examples repository:

The files include:

  • Python program and configuration JSON for Battery federate

  • Python program and configuration JSON for Charger federate

  • HELICS runner JSON to enable execution of the co-simulation

Default Setup

The default setup, used in the Base Example, integrates the federate configurations with external JSON files. The message and communication configurations are publications and subscriptions. This section introduces federate configuration of publications (pubs) and subscriptions (subs) with JSON files and how to launch the co-simulation with the HELICS runner.

Messages and Communication: pub/sub

In the Base Example, the information being passed between the Battery.py federate and the Charger.py federate is the voltage applied to the battery, and the current measured across the battery and fed back to the charger. Voltage and current are both physical quantities, meaning that unless we act on these quantities to change them, they will retain their values. For this reason, in HELICS, physical quantities are called values. Values are sent via publication and subscription – a federate can publish its value(s), and another federate can subscribe this value(s).

When configuring the communication passage between federates, it is important to connect the federate to the correct interface. In the image below, we have a Battery federate and a Charger federate. Each federate has a publication interface (red) and a subscription interface (yellow). The publication interface is also called the output, and the subscription interface the input. How are values passed between federates with pubs and subs?

We have named the publication interface for the Battery federate EV_current to indicate the information being broadcast – we can also call the publication interface the named output. This is what the publication is doing – we are telling the Battery federate that we want to publish the EV_current. The full interface designation for the current is Battery/EV_current (within the JSON, this is also called the key).

How does the current value get from the Battery federate’s publication to the Charger federate? The Charger must subscribe to this publication interface – the Charger will subscribe to Battery/EV_current. The Charger subscription interface has not been given a name (e.g., Charger/EV_current), but it will receive input – the Charger subscription is a defined unnamed input with a targeted publication. In this example, we configure the target of the Charger subscription in the JSON to the publication interface name Battery/EV_current.

Thus far we have established that the Battery is publishing its current from the named interface Battery/EV_current and the Charger is subscribing to this named interface. The Charger is also sending information about values. The Charger federate will be publishing the voltage value from the Charger/EV_voltage interface (a named output).

In order to guarantee that the Battery federate receives the voltage value from the Charger federate, the Battery will have an unnamed input subscription which targets the Charger/EV_voltage interface.

With a better understanding of how we want to configure the pubs and subs, we can now move on to the mechanics of integrating the two simulators.

Simulator Integration: External JSON

Configuration of federates may be done with JSON files. Each federate will have its own configuration (“config”) file. It’s good practice to mirror the name of the federate with the config file. For example, the Battery.py federate will have a config file named BatteryConfig.json.

There are extensive ways to configure federates in HELICS. The BatteryConfig.json file contains the most common as defaults:

{
  "name": "Battery",
  "log_level": 1,
  "core_type": "zmq",
  "period": 60,
  "uninterruptible": false,
  "terminate_on_error": true,
  "wait_for_current_time_update": true,
  "publications": [],
  "subscriptions": []
}

In this configuration, we have named the federate Battery, set the log_level to 1 (what do loglevels mean and which one do I want?), and set the core_type to zmq (the most common). The next four options control timing for this federate. The final options are for message passing.

This federate is configured with pubs and subs, so it will need an option to indicate the publication and the subscription configurations (for brevity, only the first pub and sub are printed below):

"publications":[
  {
    "key":"Battery/EV1_current",
    "type":"double",
    "unit":"A",
    "global": true
  },
],
"subscriptions":[
  {
    "key":"Charger/EV1_voltage",
    "type":"double",
    "unit":"V",
    "global": true
  },
]

This pub and sub configuration is telling us that the Battery.py federate is publishing in units of amps (A) for current from the named interface (key) Battery/EV1_current. This federate is also subscribing to information from the Charger.py federate. It has subscribed to a value in units of volts (V) at the named interface (key) Charger/EV1_voltage.

As discussed in “Simulator Integration: External JSON””, the federate registration and configuration with JSON files in the python federate is done with one line of code:

fed = h.helicsCreateValueFederateFromConfig("BatteryConfig.json")

Recall that federate registration and configuration is typically done before entering execution mode.

Co-simulation Execution

At this point in setting up the Base Example co-simulation, we have:

  1. Placed the necessary HELICS components in each federate program

  2. Written the configuration JSON files for each federate

It’s now time to launch the co-simulation with the HELICS runner. This is accomplished by creating a runner JSON file. The HELICS runner allows the user to launch multiple simulations in one command line, which otherwise would have required multiple terminals.

The runner JSON for the Base Example is called fundamental_default_runner.json:

{
  "name": "fundamental_default",
  "broker": true,
  "federates": [
    {
      "directory": ".",
      "exec": "python -u Charger.py 1",
      "host": "localhost",
      "name": "Charger"
    },
    {
      "directory": ".",
      "exec": "python -u Battery.py 1",
      "host": "localhost",
      "name": "Battery"
    }
  ]
}

This runner tells helics_broker that there are three federates and to take a specific action for each federate:

  1. Launch helics_broker in the current directory: helics_broker -f 2 --loglevel=7

  2. Launch the Charger.py federate in the current directory: python -u Charger.py 1

  3. Launch the Battery.py federate in the current directory: python -u Battery.py 1

The final step is to launch our Base Example with the HELICS runner from the command line (making sure you’ve installed the HELICS cli extension):

helics run --path=fundamental_default_runner.json

If all goes well, this will reward us with two figures:

We can see the state of charge of each battery over the duration of the co-simulation in the first figure, and the aggregated instantaneous power draw in the second. As the engineer tasked with assessing the power needs for this charging garage, do you think you have enough information at this stage? If not, how would you change the co-simulation to better model the research needs?

Questions and Help

Do you have questions about HELICS or need help?

  1. Come to office hours!

  2. Post on the gitter!

  3. Place your question on the github forum!