Parameter Handling#

Parameter handling in OpenMS and pyOpenMS is usually implemented through inheritance from DefaultParamHandler and allow access to parameters through the Param object. This means, the classes implement the methods getDefaults, getParameters and setParameters, to access to the default parameters, the current parameters and to set new parameters, respectively. The class TheoreticalSpectrumGenerator is just one example of many which makes use of parameter handling via DefaultParamHandler.

The Param object is the central data structure here. It can be manipulated through the setValue() and getValue() methods. The exists() method can be used to check for existence of a key and should always be used if a param value might be missing, since accessing a missing value via getValue() will result in a RuntimeError exception.

Using the getDescription() method, it is possible to get a descriptive help for each parameter value in an interactive session without consulting the documentation.

 1import pyopenms as oms
 2
 3p = oms.Param()
 4p.setValue("param1", 4.0, "This is value 1")
 5p.setValue("param2", 5.0, "This is value 2")
 6p.setValue(
 7    "param3",
 8    [b"H:+:0.6", b"Na:+:0.2", b"K:+:0.2"],
 9    "This is value 3 (StringList)",
10)
11print(p[b"param1"])
12p[b"param1"] += 3  # add three to the parameter value
13print(p[b"param1"])
14print(p[b"param3"])

The parameters can also be accessed as

>>> p.asDict()
{'param2': 4.0, 'param1': 7.0}
>>> p.values()
[4.0, 7.0]
>>> p.keys()
['param1', 'param2']
>>> p.items()
[('param1', 7.0), ('param2', 4.0)]
>>> p.exists("param1")
True

The param object can be copied and merged into another param object:

 1# print the key and value pairs stored in a Param object
 2def printParamKeyAndValues(p):
 3    if p.size():
 4        for i in p.keys():
 5            print("Key:", i, "Value:", p[i])
 6    else:
 7        print("no data available")
 8
 9
10new_p = p  # new deep copy of p
11
12# we will add 4 more keys to the new_p
13new_p.setValue("param2", 9.0, "This is value 9")
14new_p.setValue("example1", 6.0, "This is value 6")
15new_p.setValue("example2", 8.0, "This is value 8")
16new_p.setValue("example3", 10.0, "This is value 10")
17
18# names "example1", "example2" , "example3" keys will added to p, but "param2" will update the value
19p.merge(new_p)
20print(" print the key and values pairs stored in a Param object p ")
21printParamKeyAndValues(p)

In a param object, the keys can be removed by key name or prefix:

 1# We now call the remove method with the key of the entry we want to delete ("example3")
 2new_p.remove("example3")
 3print("Key and value pairs after removing the entry with key: example3")
 4printParamKeyAndValues(new_p)
 5
 6# We now want to delete all keys with prefix "exam"
 7new_p.removeAll("exam")
 8print(
 9    "Key and value pairs after removing all entries with keys starting with: exam"
10)
11printParamKeyAndValues(new_p)
12
13# we can compare Param objects for identical content
14if p == new_p:  # check p is equal to new_p
15    new_p.clear()  # Example: delete all keys from new_p
16
17print("Keys and values after deleting all entries.")
18printParamKeyAndValues(new_p)  # All keys of new_p deleted

For the algorithms that inherit from DefaultParamHandler, you can list all parameters along with their description by using, for instance, the following simple function.

 1# print all parameters with description
 2def printParams(p):
 3    if p.size():
 4        for i in p.keys():
 5            print(
 6                "Param:", i, "Value:", p[i], "Description:", p.getDescription(i)
 7            )
 8    else:
 9        print("no data available")
10
11# print all parameters in GaussFilter class
12gf = oms.GaussFilter()
13printParams(gf.getParameters())
Param: b'gaussian_width' Value: 0.2 Description: Use a gaussian filter width which has approximately the same width as your mass peaks (FWHM in m/z).
Param: b'ppm_tolerance' Value: 10.0 Description: Gaussian width, depending on the m/z position.
The higher the value, the wider the peak and therefore the wider the gaussian.
Param: b'use_ppm_tolerance' Value: False Description: If true, instead of the gaussian_width value, the ppm_tolerance is used. The gaussian is calculated in each step anew, so this is much slower.
Param: b'write_log_messages' Value: False Description: true: Warn if no signal was found by the Gauss filter algorithm.

To print a simple key-value list, you can use asDict(), as shown above:

1gf = oms.GaussFilter()
2gf.getParameters().asDict()

Types of Parameter Values#

A Param object can hold many parameters of mixed value type. Above, we have seen floating point values, e.g.

1new_p.setValue("param2", 9.0, "This is value 9")

Other possible values include int, float, bytes, str, List[int], List[float], List[bytes] (aka StringList). E.g.

1p = oms.Param()
2p.setValue("p_float", 4.0, "This is a float")
3p.setValue("p_int", 5, "This is an integer")
4p.setValue("p_string", "myvalue", "This is a string")
5p.setValue("p_stringlist", [b"H:+:0.6", b"Na:+:0.2", b"K:+:0.2"], "This is a StringList")
6p.setValue("p_floatlist", [1.0, 2.0, 3.0], "This is a list of floats")
7p.setValue("p_intlist", [1, 2, 3], "This is a list of integers")

Restrictions(=Validity) of Parameter Values#

Boolean parameters#

OpenMS has no boolean value type. A boolean parameter is a string parameter restricted to exactly the two values 'true' and 'false'; isBool() is the definition, and it does not care about the order of the two restrictions or about the current value. From Python such a parameter is a plain bool everywhere:

>>> gf = oms.GaussFilter()
>>> p = gf.getParameters()
>>> p["use_ppm_tolerance"]
False
>>> if not p["use_ppm_tolerance"]:      # works as you would expect
...     p["use_ppm_tolerance"] = True
>>> p["use_ppm_tolerance"]
True
>>> gf.setParameters(p)

Assigning a bool to a key that does not exist yet defines a boolean parameter – it stores the value and the true/false restrictions – so the parameter survives to_dict()/from_dict(), repr() and INI round trips as a boolean. (INI and CTD files still store it as a true/false string with those restrictions, not as a dedicated flag type.) Assigning a new value keeps the parameter’s description and tags:

>>> p = oms.Param()
>>> p["flag"] = True
>>> p.isBool("flag"), p["flag"]
(True, True)
>>> oms.Param(p.to_dict())["flag"]
True

Nothing is guessed from a value’s content, which means two things worth knowing:

  • A plain string that happens to read 'true' is not a boolean parameter, and neither is a parameter that allows further values (auto,true,false, as used by OpenPepXLAlgorithm’s algorithm:deisotope). Those stay str on every path. Use isBool() or isinstance(value, bool) to tell them apart.

  • Assigning a bool to an existing parameter that is not boolean raises a TypeError rather than silently redefining its type. Call setValidStrings(key, ["true", "false"]) first if that is really what you want.

Because the value is a bool, a boolean parameter has no string restrictions to hand out: getValidStrings() and ParamEntry.valid_strings raise a TypeError for one. getValueType() still reports STRING_VALUE, since that is how the value is stored.

Note

Meta values are a different data structure with no restrictions attached, so the round trip there is asymmetric: setMetaValue(key, True) stores the canonical string (the same thing OpenMS writes in C++), and getMetaValue(key) returns 'true' or 'false' as a string. Compare those explicitly – do not use them in an if, because 'false' is truthy in Python.

For certain types of values, pyOpenMS supports restrictions, e.g. for single strings only a restricted set of values may be allowed. Also, for floats/ints only a restricted interval of numbers may be valid.

Usually, these restrictions are set by the OpenMS algorithm/class which hands out the parameters. Then, if you provide invalid values via setParameters, the algorithm will throw an exception.

It is usually interesting to inspect the restrictions to know what methods a class supports, e.g. see below for an example using a GaussFilter and the Normalizer.

In theory, you can create your own restrictions. Usually this is done when defining the algorithm in C++ and is out of scope here.

E.g.

 1gf = oms.GaussFilter()
 2gfp = gf.getParameters()
 3gfp.isBool("use_ppm_tolerance")           ## True -- it is a boolean parameter,
 4                                          ## so getValidStrings() raises for it
 5
 6gfp.setValue(b"use_ppm_tolerance", "maybe") ## is invalid but setValue does not complain
 7##  ... until you actually set the parameters:
 8try:
 9  gf.setParameters(gfp)   ## --> throws a RuntimeError
10except RuntimeError as e:
11  print(f"RuntimeError: {str(e)}")
12  ## prints `GaussFilter: Invalid string parameter value 'maybe' for parameter 'use_ppm_tolerance' given! Valid values are: 'true,false'.`
13
14
15nor = oms.Normalizer()
16norp = nor.getParameters()
17norp.getValidStrings("method")  ## yields [b'to_one', b'to_TIC']
18norp.setValue("method", "to_TIC") ## pick the 'to_TIC' method
19nor.setParameters(norp)
20# ... now run the Normalizer ...

Unfortunately, it is not possible to retrieve the valid ranges for floats and ints, if they have been set via the pyOpenMS API (yet). However, one can look at either the documentation of the class in pyOpenMS docs. There will be a link to the C++ version which contains the restrictions (if any) of all parameters of a class. Alternatively, you can simply write the parameters to an INI file (also called ParamXMLFile), which is a special XML file format which OpenMS uses to store parameters.

E.g.

1pphr = oms.PeakPickerHiRes()
2
3px = oms.ParamXMLFile()
4px.store("tmp.ini", pphr.getParameters())  ## store PeakPickerHiRes params (or any Param object you like)
5
6## either look at the file in Python, or open it in an Editor of your choice
7print(open('tmp.ini').read())

The INI file looks something like this (shortened):

1<?xml version="1.0" encoding="ISO-8859-1"?>
2<PARAMETERS version="1.7.0" xsi:noNamespaceSchemaLocation="https://raw.g.../Param_1_7_0.xsd" xmlns:xsi="...">
3  <ITEM name="signal_to_noise" value="0.0" type="double" description="Minimal signal... SNT estimation!)" required="false" advanced="false" restrictions="0.0:" />
4  <ITEM name="spacing_difference_gap" value="4.0" type="double" description="The extension ... chromatograms." required="false" advanced="true" restrictions="0.0:" />
5...

Any parameter which has restrictions on its value (strings, ints and floats) will have a restrictions attribute. In the above example, the restriction on the signal_to_noise parameter are restrictions="0.0:", i.e. only the lower bound is restricted to 0.0. The upper bound can be any value larger than 0.