Skip to content

Migrating python code from HELICS v2.x.x to HELICS v3.x.x

This document will describe the path to migrate your python code from HELICS version 2.x.x to version 3.x.x.

pyhelics is backward and forward compatible. So you won’t need to make any of these changes. The following document is for reference only.

See this commit for a diff of all the changes.

You can also see the tests folder on github for more examples on how to use the various functions.

See the Pythonic Interface for all the new functionality that is available in pyhelics.

Functions

Related to HelicsMessage

Previously you may have add code to deal with messages like this:

# helics v2
m = h.helicsEndpointGetMessage(endpoint)
assert m.source == "port1"
assert m.original_source == "port1"
assert m.destination == "port2"
assert m.data == len(data)
assert m.time == 2.5

The return object of helicsEndpointGetMessage(endpoint) is now a opaque object and you will have to use helics* functions to get the various attributes of that object.

# helics v3
m = h.helicsEndpointGetMessage(endpoint)
assert h.helicsMessageGetSource(m) == "port1"
assert h.helicsMessageGetOriginalSource(m) == "port1"
assert h.helicsMessageGetDestination(m) == "port2"
assert h.helicsMessageGetRawDataSize(m) == len(data)
assert h.helicsMessageGetTime(m) == 2.5

Functions named with MessageObject are renamed to use Message. This is all handled transparently in pyhelics, so you don’t need to deal with it.

This function has been renamed: helicsMessageCheckFlag -> helicsMessageGetFlagOption to be consistent with helicsMessageSetFlagOption.

Related to raw data

Additionally, any function that takes bytes as an input must be passed in a python bytestring. If you have a regular python unicode string, as the user yoou are responsible for converting to a byte string with an encoding of your choice.

To convert a python unicode string to a byte string, you can do "hello world".encode(). You can even specify the encoding: "hello world".encode().

Similarly, any helics function that returns a byte string must be handled correctly on the user end. As a user, you are at the moment required to know the encoding of the message in order to convert it to the correct string.

All functions that have Raw in the name are replaced with functions that have Bytes in the name.

e.g.

helicsMessageGetRawData -> helicsMessageGetBytes

Related to complex numbers

Previously, you would need to pass real and imaginary components of complex numbers as separate floating point values.

# helics v2
h.helicsInputSetDefaultComplex(sub, -9.9, 2.5)

Now you can pass them as a complex number.

# helics v3
h.helicsInputSetDefaultComplex(sub, -9.9 + 2.5j)

These are the full list of functions:

  • helicsInputGetComplex
  • helicsPublicationPublishComplex
  • helicsInputSetDefaultComplex

that are affected by this change.

Related to endpoints

The following functions have been renamed and the argument order has been updated:

  • helicsEndpointSendMessageRaw -> helicsEndpointSendBytesTo
  • helicsEndpointSendEventRaw -> helicsEndpointSendBytesToAt

The following functions have been renamed:

  • helicsFederatePendingMessages -> helicsFederatePendingMessageCount
  • helicsEndpointPendingMessages -> helicsEndpointPendingMessageCount

Enumerations

Enumerations are constants now, and are represented by upper case variables:

# helics v2
h.helicsFederateInfoSetIntegerProperty(fedInfo1, h.helics_property_int_log_level, 1)
h.helicsFederateInfoSetIntegerProperty(fedInfo1, h.helics_property_int_max_iterations, 100)
# helics v3
h.helicsFederateInfoSetIntegerProperty(fedInfo1, h.HELICS_PROPERTY_INT_LOG_LEVEL, 1)
h.helicsFederateInfoSetIntegerProperty(fedInfo1, h.HELICS_PROPERTY_INT_MAX_ITERATIONS, 100)

New functionality

If you would like to use callbacks you can do the following in HELICS v3.x.x:

@h.ffi.callback("void logger(HelicsMessage, void* userData)")
def filterFunc1(mess, user_data):
    time = h.helicsMessageGetTime(mess)
    user_data = h.ffi.from_handle(user_data)

    h.helicsMessageSetTime(mess, time + 2.5)
    user_data += 1


class UserData(object):
    def __init__(self, x):
        self.x = x


...

user_data = UserData(5)
handle = h.ffi.new_handle(user_data)
h.helicsFilterSetCustomCallback(f1, filterFunc1, handle)

...

Find the signature of the function that you’d like to call, use h.ffi.callback as a python decorator for that function.

In HELICS v3,

@h.ffi.callback("void logger(HelicsMessage, void* userData)")

In HELICS v2.x.x, use helics_message_object instead.

@h.ffi.callback("void logger(helics_message_object, void* userData)")

Then, you can pass the variable that contains a reference to this C function to a helics callback function. The user is responsible for managing memory with relation to these objects. See cffi’s documentation for more information.