A short tutorial on using the Python SWIG interface to LALSuite.
Install the relevant packages, e.g.:
conda install python-lal
pip install lalsuite
apt install python3-lal
yum install python3-lal
If you instead wish to build LALSuite from source, see the LALSuite README for instructions.
ImportError: .../lal/_lal.so: undefined symbol: XLALSomeFunction
, that could indicate that:XLALSomeFunction()
to a LALSuite C header (.h
) file, but failed to add the corresponding definition of XLALSomeFunction()
in a LALSuite C source (.c
) file, or failed to list a new C source file in the appropriate Makefile.am
file.lal.CreateREAL4TimeSeries()
, lalVoltUnit becomes lal.VoltUnit
. (As usual in Python, all functions, constants, classes, etc. are under the lal
namespace unless you specifically import
them as different names, e.g. from lal import VoltUnit as Volts
.)ts
) do not need to be explicitly declared in Python, unlike in C.123456789.0
) without needing to specifically create a LIGOTimeGPS class instance. If you need greater control, you can also use a specific LIGOTimeGPS constructor, e.g. LIGOTimeGPS(123456789, 321)
to specify exact nanosecond values.XLAL_CHECK()
in LALSuite C code. If the call to lal.CreateREAL4TimeSeries()
fails, a Python exception will be raised instead.del
, e.g.: Most LALSuite C function arguments have obvious translations to Python, e.g. string, integer and floating-point numbers. Some less obvious/exceptional cases are:
int/INT*
, float/REAL*
, are treated as output-only scalar arguments by default. For example: REAL4TimeSeries**
, are also treated as output-only arguments by default. For examples: If the above default rules aren't correct for a particular function, you will need to adjust the behaviour of the interface by adding SWIGLAL()
directives to the LALSuite C header. For example, the following function:
is wrapped as
See the documentation for Interface SWIGCommon.i for more SWIGLAL()
directives.
(In some cases, you may have to refactor the LALSuite C function so that it can be wrapped by the SWIG interface. For example, the SWIG interface does not recognise C array pointers (e.g. REAL4 *data
) as function arguments; instead use a LALSuite array type such as REAL4Vector to represent such arguments.)
XLALCreate...()
which is used to create a new instance of that struct, including allocating any memory the struct needs to point to. The equivalent Python classes can also be instantiated used the same lal.Create...()
constructor functions.A few LALSuite C structs are handled specially: LIGOTimeGPS and LALUnit. These structs act more like primitive types, e.g. a LIGOTimeGPS acts like a number which can be added, subtracted, etc. The equivalent Python classes for LIGOTimeGPS and LALUnit behave more closely to how one would expect a native Python class to behave:
lal.LIGOTimeGPS()
constructor, which takes either floating-point, integer seconds/nanoseconds, or string arguments;Some example usage:
See the documentation for Interface SWIGLALOmega.i for complete documentation of LIGOTimeGPS and LALUnit.
gsl_vector
or gsl_matrix
, can be substituted with a NumPy array of the appropriate size and type. (A REAL4TimeSeries, however, cannot be substituted with a NumPy array, as REAL4TimeSeries includes additional metadata e.g. the starting GPS time and sampling frequency of the time series.)lal.REAL4Vector
with a read-only length
attribute, and a data
attribute which is a NumPy array of length
elements with dtype=float32
.MultiSFTVector
contains an array over detectors of SFTVector
elements, which are themselves arrays over time of SFTtype
elements, which represent SFTs (Short Fourier Transforms) of Fourier-domain gravitational wave data. The equivalent Python MultiSFTVector
class contains a NumPy array of SFTVector
classes, each of which contains a NumPy array of SFTtype
classes.Wherever possible, NumPy arrays in C structs serve the underlying data as a view, i.e. each element of the NumPy array directly accesses the representation of the element in the C array (as opposed to copying the entire array from LALSuite/C memory into Python memory). This is always possible for NumPy arrays of primitive types (e.g. floating-point numbers in a REAL4Vector) but not for NumPy arrays whose elements are Python classes representing C structs (e.g. a SFTVector
class which contains an array of SFTtype
elements).
To avoid modifying the C struct data, copy the NumPy array using copy.copy()
:
REAL4
argument should accept any scalar of type float
, np.float
, np.float32
, etc.The SWIG interface has a concept of memory ownership: which Python class instance owners the underlying C memory in LALSuite, and therefore which Python class instance calls XLALFree() to free the C memory when it is no longer needed.
Generally speaking, you shouldn't need to worry about memory ownership, as the SWIG interface takes care to ensure that C memory is not freed until it can no longer be accessed. In this sense Python classes wrapping LALSuite C structs should behave like ordinary Python classes. There are some advanced circumstances however where care with memory ownership semantics needs to be taken:
The first rule is that a C struct owns any memory it points to through a pointer field. For example, given the following code:
the ts
variable in Python will own the C memory of the REAL4TimeSeries
struct, the C memory of the REAL4Vector
struct, and the C memory pointed to by the REAL4 *data
field.
The SWIG interface tracks the memory ownership when Python class attributes are accessed, and ensures that C memory is freed only once it can no longer be accessed. Consider the following example:
The second rule is that memory ownership is transferred when a pointer is assigned to a field in another struct, e.g.
The equivalent code should work in Python, provided that the Python class attribute (C struct field) being assigned to is a null pointer, e.g.:
If, however, ts.data
was not a null pointer in the above example, this would lead to a memory leak (consistent with the behaviour of the equivalent code in C).
XLALDestroy...()
function which correctly frees any memory assigned to that pointer field.