Most of the Ground Motion Prediction Equations (GMPEs) in hazardlib are
classes that can be instantiated without arguments, however there is
now a growing number of exceptions to the rule. Here I will describe
some of the parametric GMPEs we have, as well as give some guidance for
authors wanting to implement a parametric GMPE.

The call to super().__init__ will set a self.kwargs attribute
and perform a few checks, like raising a warning if the GMPE is experimental.
For nonparametric GMPEs)``self.kwargs`` is the empty dictionary, but in general
is non-empty and it can be arbitrarily nested, with only one limitation:
it must be a dictionary of literal Python
objects so that it admits a TOML representation.

TOML is a simple format
similar to the .ini format but hierarchical that is described here
https://github.com/toml-lang/toml#user-content-example and it is used
by lots of people in the IT world. The advantage of TOML is that it is
a lot more readable than JSON and XML and simpler than YAML: moreover,
it is perfect for serializing into text literal Python objects like
dictionaries and lists. The serialization feature is essential for the
engine since the GMPEs are read from the GMPE logic tree file which is a
text file, and because the GMPEs are saved into the datastore as a text,
in the dataset full_lt/gsim_lt.

Historically, the first parametric GMPE was the GMPETable, introduced many
years ago to support the Canada model. The GMPETable class has a single
parameter, called gmpe_table, which is a (relative) pathname to an
.hdf5 file with a fixed format, containing a tabular representation of
the GMPE, numeric rather than analytic.

You can find an example of use of GMPETables in the test
openquake/qa_tests_data/case_18, which contains three tables in its
logic tree:

This is a legacy syntax, which is still supported and will likely be supported
forever, but we recommend you to use the new TOML-based syntax, which is
more general. The old syntax has the limitation of being non-hierarchic,
making it impossible to define MultiGMPEs involving parametric GMPEs:
this is why we switched to TOML.

It is possible to define other GMPEs taking one or more filenames as parameters.
Everything will work provided you respect the following rules:

there is a naming convention on the file parameters, that must end with
the suffix _file or _table

the files must be read at GMPE initialization time
(i.e. in the __init__ method)

they must be read with the GMPE.open method, NOT with the
open builtin;

in the gsim logic tree file you must use relative path names

The constraint on the argument names makes it possible for the engine
to collect all the files required by the GMPEs; moreover, since the path names
are relative, the oq zip command can work making it easy to ship runnable
calculations. The engine also stores
in the datastore a copy of all of the required input files. Without the copy,
it would not be possible from the datastore
to reconstruct the inputs, thus making it impossible to dump and restore
calculations from a server to a different machine.

The constraint about reading at initialization time makes it possible
for the engine to work on a cluster. The issue is that GMPEs are
instantiated in the controller and used in the worker nodes, which
do not have access to the same filesystem.
If the files are read after instantiation, you will get a file not
found error when running on a cluster.

The reason why you cannot use the standard open builtin to read the
files is that the engine must be able to read the GMPE inputs from the
datastore copies (think of the case when the calc_XXX.hdf5 has been
copied to a different machine). In order to do that, there is some
magic based on the naming convention. For instance, if your GMPE must
read a text file with argument name text_file you should write
the following code:

would work but it is discouraged. It is best to keep the **kwargs
signature so that the call to super().__init__(**kwargs) will
work out-of-the-box even in future subclasses of GMPEWithTextFile
with different parameters (in case somebody decided to develop
such subclasses; this is defensive programming).

The second example of parametric GMPE is the MultiGMPE class. A MultiGMPE
is a dictionary of GMPEs, keyed by Intensity Measure Type. It is useful
in geotechnical applications and in general in any situation where you
have GMPEs depending on the IMTs. You can find an example in our test
openquake/qa_tests_data/classical/case_1:

Here the engine will use the GMPE AkkarBommer2010 for PGA and
SadighEtAl1997 for SA(0.1). The .kwargs passed to the
MultiGMPE class will have the form:

{'PGA':{'AkkarBommer2010':{}},'SA(0.1)':{'SadighEtAl1997':{}}}

The beauty of the TOML format is that it is hierarchic, so if we wanted
to use parametric GMPEs in a MultiGMPE we could. Here is an example
using the GMPETable Wcrust_low_rhypo.hdf5 for PGA and
Wcrust_med_rhypo.hdf5 for SA(0.1) (the example has no physical
meaning, it is just an example):

In engine 3.4 we introduced a GMPE that manages a range of spectral
accelerations and acts in terms of an average spectral acceleration.
You can find an example of use in openquake/qa_tests/data/classical/case_34:

As you see, the format is quite convenient when there are several arguments
of different types: here we have two strings (gmpe_name and
corr_func) and a list of floats (avg_periods). The dictionary
passed to the underlying class will be