Using the xraylib API - tschoonj/xraylib Wiki

Using the C library with pkg-config

The source code contains a pkg-config file that will facilitate the use of xraylib in combination with your own software, written in C, C++ or Objective-C. For example, you can compile program.c as follows:

gcc `pkg-config --cflags libxrl` program.c `pkg-config --libs libxrl`

This approach makes sure that the preprocessor finds the right headers and that the linker finds the library. Include the xraylib headers with:

#include <xraylib.h>

Depending on the installation location of the library, it may be necessary to change the LD_LIBRARY_PATH and/or DYLD_LIBRARY_PATH environment variables. On some Linux systems it is also possible to add a file containing the xraylib library location to the /etc/ld.so.conf.d directory and running ldconfig if linking errors appear at runtime. If xraylib was installed using our RPM or DEB packages, the required pkg-config file will only be available if the development package was also installed. Use the PKG_CONFIG_PATH variable if the pkg-config file is not picked up: this will probably be the case when compiling from source and using the default installation prefix.

pkg-config may also be used on the Windows platform, provided xraylib is compiled from source (usually with the MinGW compiler or using Cygwin).

Using the C library in Visual Studio (Windows)

Open the property pages of your project. Go to Configuration PropertiesC/C++General. Add the Include folder of your xraylib installation to the Additional IncludeDirectories key. With this, you should have access to the headers of xraylib when adding the following line to your C/C++ program:

#include <xraylib.h>

In order to have successful linking to the xraylib dll, go in the property pages of your project to Configuration PropertiesLinkerGeneral, and add the location of the Lib folder of your xraylib installation to the Additional Library Directories key. The library itself can be specified from Configuration PropertiesLinkerInput: add libxrl-11.lib to Additional Dependencies.

Using the framework in macOS

After installing the framework in /Library/Frameworks, you may link to it from within your Xcode application, or from the commandline:

clang -framework xraylib -o test test.c

Include the header as:

#import <xraylib/xraylib.h>

Error handling

This section covers how xraylib deals with error handling in C, C++ and Objective-C. If you do not plan on coding in these languages, consult the section devoted to your language of choice for an explanation on how errors/exceptions are dealt with.

xraylib 4.0.0 introduced a brand new error handling mechanism, based on the GError struct that is heavily used by GLib and the entire GNOME ecosystem, and if used correctly, will allow error reporting in a thread-safe manner, which was not possible in earlier releases.

Fundamental in this approach is the xrl_error datatype, which has been defined as:

typedef struct _xrl_error xrl_error;

struct _xrl_error
{
  xrl_error_code code;
  char *message;
};

The struct has two member variables code and message, which contain the type of error that occurred (an enum), and a string with a concise description of the error. The xrl_error_code enum is defined as:

typedef enum {
	XRL_ERROR_MEMORY, /* set in case of a memory allocation problem */
	XRL_ERROR_INVALID_ARGUMENT, /* set in case an invalid argument gets passed to a routine */
	XRL_ERROR_IO, /* set in case an error involving input/output occurred */
	XRL_ERROR_TYPE, /* set in case an error involving type conversion occurred */
	XRL_ERROR_UNSUPPORTED, /* set in case an unsupported feature has been requested */
	XRL_ERROR_RUNTIME /* set in case an unexpected runtime error occurred */
} xrl_error_code;

Usage of the xrl_error struct in the C/C++/ObjC API happens through the last argument of all functions:


double AtomicWeight(int Z, xrl_error **error);

xrl_error *error = NULL;

double weight = AtomicWeight(-5, &error); // this would certainly result in an error!

if (error != NULL) {
	// an error occurred!
	fprintf(stderr, "error message: %s\n", error->message);
	xrl_clear_error(&error); 
	...
}

This example demonstrates what is required to make use of this feature:

  1. Declare a pointer to a xrl_error variable, and assign NULL to it. This last action is essential, as the variable will otherwise contain garbage, which will probably lead to a segmentation fault later on.
  2. Pass a reference to this variable to the xraylib function.
  3. After returning, check the value of the variable: if it is non-NULL, an error has occurred, and the contents of the struct can be examined to figure out what went wrong.
  4. When the error variable is no longer useful, free its memory and assign NULL to it using xrl_clear_error.

If you are not interested in the error message, feel free to simply pass NULL, and rely on the return value of the function, since 0.0 (or NULL in some cases) is returned when an error occurred:

double weight = AtomicWeight(-2, NULL);

if (weight == 0.0) {
	// an error occurred!
	...
}

This is perfectly fine, and is fact recommended if speed execution speed is paramount. Do keep in mind that some functions may return 0.0 as a valid result!

The use of the xrl_error argument is only relevant for those coding in C, C++, objC or Fortran. All other language bindings, as well as the Java implementation, provide an API that do not use this argument. Instead the xraylib authors have ensured that the error message gets translated into an exception, or similar depending on how the language deals with error handling. Instructions on how to exploit this feature can be found on a per language basis in the following sections.

C++ bindings

The xraylib C++ bindings consist of a single header whose methods wrap their counterparts from the core C library, and are mostly meant to introduce C++ exception handling instead of having to use the xrl_error based API, even though that works just fine from C++ as well.

Compilation of your software that uses these bindings can be done through:

g++ `pkg-config --cflags libxrl` program.cpp `pkg-config --libs libxrl`

On Visual Studio, you will need to add the location of the header to the Additional IncludeDirectories key in your project configuration.

The C++ header is included in source code using:

#include <xraylib++.h>

Here is a short example that shows how to use these bindings:

#include "xraylib++.h"
#include <cmath>
#include <cassert>

int main(int argc, char **argv) {
	double width;

	width = xrlpp::AtomicLevelWidth(26, K_SHELL);
	assert(fabs(width - 1.19E-3) < 1E-6);
	
	width = xrlpp::AtomicLevelWidth(92, N7_SHELL);
	assert(fabs(width - 0.31E-3) < 1E-8);
	
	try {
		width = xrlpp::AtomicLevelWidth(185, K_SHELL);
		abort();
	}
	catch (std::invalid_argument &e) {
                // do nothing, exception is expected
	}

        return 0;
}

Fortran bindings

In order to compile the xraylib Fortran bindings, it is necessary that the configure script finds a Fortran compiler on the system that supports the C interoperability features of the Fortran 2003 standard. Compilers known to do the job include: gfortran (starting with 4.4) , Intel Fortran and Sun Studio 12 . Similar to the C library, it is possible to link in the Fortran bindings using a pkg-config file. Use the following syntax:

gfortran `pkg-config --cflags libxrlf03` program.f90 `pkg-config --libs libxrlf03`

In order to gain access to the xraylib module, include the following line in your Fortran source code:

USE :: xraylib

Error handling occurs through an OPTIONAL argument to the xraylib functions:

PROGRAM test_atomicweight

USE, INTRINSIC :: ISO_C_BINDING
USE, INTRINSIC :: ISO_FORTRAN_ENV
USE :: xraylib
USE :: libtest
IMPLICIT NONE

TYPE(xrl_error), POINTER :: error => NULL()
REAL (C_DOUBLE) :: weight

weight = AtomicWeight(-5, error) ! weight will be 0.0 and error will be associated

IF (ASSOCIATED(error)) THEN
	! deal with error
	WRITE (error_unit, '(A,A)') 'Error message: ', TRIM(error%message)
	...
	! deallocate error when no longer required
	! this command also nullifies the pointer, so it can be reused
	DEALLOCATE(error)
ENDIF

ENDPROGRAM test_atomicweight

If not interested in the error message, you can rely on the return value instead to determine if an error occurred or not:

PROGRAM test_atomicweight2

USE, INTRINSIC :: ISO_C_BINDING
USE, INTRINSIC :: ISO_FORTRAN_ENV
USE :: xraylib
USE :: libtest
IMPLICIT NONE

REAL (C_DOUBLE) :: weight

weight = AtomicWeight(-5) ! weight will be 0.0

IF (weight == 0.0_C_DOUBLE) THEN
	! deal with error
	...
ENDIF

ENDPROGRAM test_atomicweight2

Python bindings

To include the xraylib python module in your own scripts add the following line:

import xraylib

xraylib 3.1.0 introduced an alternative implementation of the Python bindings, in which the arguments and return values are in fact numpy arrays. This is recommended whenever a particular xraylib functions needs to be called a lot and performance becomes critical. This module, generated with Cython may be loaded from your script with:

import xraylib_np

Our testresults have shown that using this module one can obtain virtually identical performance as when using native C-code, or around 60 % faster compared to the default SWIG-generated bindings. The numpy based module does not export all functions from xraylib: those using strings and structs are not covered.

Both the default (SWIG generated) and the numpy (Cython generated) modules can be used alongside each other.

Depending on the install location of xraylib, it may be necessary to adjust the PYTHONPATH environment variable.

Error handling in the SWIG generated python module is done through python's exception handling mechanism:


import xraylib as xrl

try:
	weight = xrl.AtomicWeight(-5)
except ValueError as e:
	print("Exception was raised: {}".format(str(e)))

try:
	weight = xrl.AtomicWeight("Fe")
except TypeError as e:
	print("Exception was raised: {}".format(str(e)))

The Cython generated python module does not throw exceptions when the numpy arrays contain invalid values (like a negative atomic number Z). Instead the array result will contain a corresponding 0.0.

Perl bindings (Linux and macOS)

To include the xraylib perl module in your own scripts add the following line:

use xraylib;

Depending on the install location of xraylib, it may be necessary to adjust the PERL5LIB environment variable.

Error handling is accomplished through eval blocks, or if preferred using the Try::Tiny module:

use xraylib;

eval {
	xraylib::AtomicWeight(-5);
};
if ([email protected]) {
	... # handle error
}

use Try::Tiny;

try {
	xraylib::AtomicWeight(-5);
} catch {
	warn "caught error: $_"; # not [email protected]
};

Lua bindings (Linux and macOS)

In order to use the Lua bindings, update the LUA_CPATH environment variable to include ${libdir}/lua/x.y (with x.y corresponding to your version of lua), and include the following line in your lua code:

require("xraylib")

Error handling in Lua is done using the pcall function (protected call) to encapsulate code that may fail:

require("xraylib")

status, result = pcall(xraylib.AtomicWeight, -5)
if status then
	-- no errors occurred -> result contains the weight
else
	-- an error occurred -> result contains the error message
end

Java implementation

To import the xraylib jar, include the lines

import com.github.tschoonj.xraylib.Xraylib;

in your java code. You may have to update the CLASSPATH environment variable to make sure that it points to the location of the xraylib class variables at compile and runtime.

In case of an error when calling an xraylib function (e.g. with invalid arguments) a Java RunTimeException will be thrown instead of returning 0.0, almost always an IllegalArgumentException:

import com.github.tschoonj.xraylib.Xraylib;

try {
	double weight = Xraylib.AtomicWeight(185);
} catch (IllegalArgumentException e) {
	// an error occurred!
	System.out.println(e.getMessage());
}

The diffraction API can be used in two ways: using static methods (identical to the other bindings) or using an object oriented API.

.NET bindings (Windows only)

The .NET Framework can be installed on computers running Microsoft Windows operating systems. It supports multiple programming languages, including C#, VB.NET, C++/CLI, Pascal, Fortran and includes a large class library for that solves many common programming problems. Microsoft offers free versions of its Express Edition compilers from http://www.microsoft.com/express/ These were compiled using Visual Studio 2008 Standard and was built against .NET Framework Version 2. The binding consists of a single, mixed-mode assembly XrayLib.NET.dll written in C++/CLI. The assembly provides the interop between a managed XrayLib class and the native functions and types exposed by libxrl-11.dll. This combines the language interoperability of .NET with the performance of the native underlying functions.

A pre-built Release version of the assembly and an example program can be found in the bin folder together with a HTML Help v1 file.

To use XrayLib.NET.dll in Visual Studio:

  1. Right-click on your project in the Solution Explorer
  2. Click the References… menu item
  3. Click the Add New Reference… button in the dialog box
  4. Click the Browse tab of the Add Reference dialog box
  5. Navigate to the xraylib Lib folder and select the XrayLib.NET.dll file
  6. Click the OK buttons to add the assembly reference and close the dialog boxes
  7. Assuming you are using C#, add the following line to the top of your source code file
using Science;
  1. To create a instance of the XrayLib class that provides access to the XrayLib functionality, use the following call
XrayLib xl = XrayLib.Instance;

The class uses the static property Instance to implement a singleton pattern so that only a single instance of the XrayLib class is created and can be used throughout your program.

Errors are handled through C# try/catch blocks.

Ruby bindings (Linux and macOS)

To include the xraylib ruby module in your own scripts add the following line:

require 'xraylib'

Depending on the install location of xraylib, it may be necessary to adjust the RUBYLIB environment variable.

Errors when calling xraylib will be handled through the Ruby exception mechanism:

require 'xraylib'

begin
	weight = Xraylib.AtomicWeight(-5)
rescue Exception => e
	print "Exception caught: " + e
end

PHP bindings (Linux and macOS)

To include the xraylib PHP module in your own scripts add the following line:

include("xraylib.php");

Furthermore, you may have to modify your php.ini file by updating it with a new extension:

extension=<path_to_xraylib.so>/xraylib.so

and by modifying the include_path:

include_path=<path_to_xraylib.php>

The PHP bindings have been used to generate the online calculator.

Error handling is done through the PHP exception mechanism:

include("xraylib.php");

try {
	$weight = AtomicWeight(-5);
} catch (Exception &e) {
	echo "The exception message is: " . $e->getMessage();
}

IDL bindings

The IDL bindings have been deprecated and should not be used in new code. They will eventually be deleted from the xraylib codebase. Maintenance and testing of these bindings is minimal

To use the IDL bindings, update the IDL_PATH and IDL_DLM_PATH environment variables to include ${prefix}/pro and ${prefix}/dlm respectively. If you want to take advantage of the xraylib constants, add the line

@xraylib

to your IDL scripts or your IDL startup script .

There is no error handling in the IDL bindings: whenever an error occurs, 0.0 will be returned by the calling function.

Matlab bindings

These instructions have not been tested with xraylib 4.0.0 and will most likely not work.

It has been reported that xraylib can be called from Matlab on macOS, Linux and Windows by defining functions as in the following examples. Since the developers do not have access to a Matlab installation, these bindings are currently not officially supported.

Linux:

function A = atomicweight(Z)
    if libisloaded('libxrl') == 0
    loadlibrary('/usr/local/lib/libxrl.so',...
                '/usr/local/include/xraylib/xraylib.h')
    end
    A = calllib('libxrl','AtomicWeight',Z);
end

macOS:

function A = atomicweight(Z)
    if libisloaded('libxrl') == 0
    loadlibrary('/usr/local/lib/libxrl.dylib',...
                '/usr/local/include/xraylib/xraylib.h')
    end
    A = calllib('libxrl','AtomicWeight',Z);
end

Windows

function A = atomicweight(Z)
    if libisloaded('libxrl') == 0
    loadlibrary('C:\Windows\System32\libxrl-7.dll',...
                'C:\Program Files\xraylib 64-bit\Include/xraylib.h',...
                'alias','libxrl')
    end
    A = calllib('libxrl','AtomicWeight',Z);
end

One can query the available functions using:

libfunctions('libxrl', '-full')

xraylib can be unloaded using:

unloadlibrary('libxrl')

Obviously, the exact path to the xraylib library and the header will depend on the installation.

Mathematica bindings

These instructions have not been tested with xraylib 4.0.0 and will most likely not work.

The Mathematica bindings are currently not officially supported. xraylib has been reported to be callable from Mathematica (on Windows) after executing the following series of commands:

Needs["NETLink`"];
UninstallNET[];
InstallNET["Force32Bit"->True];
dllLocation="C:\\SomeFolder\\PathToDLL\\libxrl-7.dll";

To load an xraylib function one needs to define each function separately, according to the prototypes defined in the xraylib API

CSPhoto = DefineDLLFunction["CS_Photo", dllLocation, "double", {"int", "double"}];
CSFluorLineKissel = DefineDLLFunction["CS_FluorLine_Kissel", dllLocation,"double", {"int", "int", "double"}];
FluorYield = DefineDLLFunction["FluorYield", dllLocation,"double", {"int", "int"}];

These functions may then be called as:

CSPhoto[33, 15.00];
CSFluorLineKissel[33, 0, 15.00];
FluorYield[33, 0];

Using xraylib: examples

Examples demonstrating the usage of the C library and the bindings can be found in the example folder of the xraylib package. These examples will be compiled and executed when make check is invoked after make during the installation.

Two complete typical usage examples written in Fortran and Ruby can be found here