Python

Installing the python sdf readers

To install the python sdf readers you need to have an installation of python (2 or 3) with the numpy library. The automated plotting library requires the matplotlib library. Both numpy and matplotlib are available through most system package managers or are installable through pip.

Once you have a working python install, just go into one of the epoch directories (epoch1d, epoch2d or epoch3d) and type

make sdfutils

This will build the SDF python library and install the sdf_helper wrapper and utility layer.

Using the sdf_helper wrapper layer

The low level python SDF library is not user friendly, so a wrapper layer called sdf_helper has been written. This wrapper layer simplifies loading SDF files and provides simple plotting routines using matplotlib.

Importing sdf_helper

Importing sdf_helper is as simple as

import sdf_helper

In these examples, the numpy and matplotlib libraries are usually loaded too, and an alias is created for sdf_helper, so the boilerplate code looks like

import sdf_helper as sh
import numpy as np
import matplotlib.pyplot as plt

Loading an sdf file using sdf_helper

To load a file, use the getdata function. This function takes either a string which it loads as a filename, so to load the file Data/0010.df you would run

import sdf_helper as sh
data=sh.getdata('Data/0010.sdf')

or it takes a number which is the dump number, and optionally a second parameter which is the directory name as a string, so you would run

import sdf_helper as sh
data=sh.getdata(10, 'Data')

Because memory is only allocated when needed in the SDF python reader there is no way of specifying which variables to load using getdata. All variables are available when the file is first loaded, and memory is allocated when the variable is first used.

Listing the available variables in an sdf file

To see what variables are available use the list_variables method

import sdf_helper as sh
data=sh.getdata('Data/0010.sdf')
sh.list_variables(data)

This produces an output that looks something like

CPUs_Current_rank <class 'sdf.BlockPlainVariable'> [0]
CPUs_Original_rank <class 'sdf.BlockPlainVariable'> [2]
Current_Jx <class 'sdf.BlockPlainVariable'> [400]
Derived_Charge_Density <class 'sdf.BlockPlainVariable'> [400]
Derived_Number_Density <class 'sdf.BlockPlainVariable'> [400]
Derived_Number_Density_Left <class 'sdf.BlockPlainVariable'> [400]
Derived_Number_Density_Right <class 'sdf.BlockPlainVariable'> [400]
Electric_Field_Ex <class 'sdf.BlockPlainVariable'> [400]
Grid_CPUs_Original_rank <class 'sdf.BlockPlainMesh'> [3]
Grid_CPUs_Original_rank_mid <class 'sdf.BlockPlainMesh'> [2]
Grid_Grid <class 'sdf.BlockPlainMesh'> [401]
Grid_Grid_mid <class 'sdf.BlockPlainMesh'> [400]
Grid_x_px_Left <class 'sdf.BlockPlainMesh'> [400, 200]
Grid_x_px_Left_mid <class 'sdf.BlockPlainMesh'> [399, 199]
Grid_x_px_Right <class 'sdf.BlockPlainMesh'> [400, 200]
Grid_x_px_Right_mid <class 'sdf.BlockPlainMesh'> [399, 199]
Wall_time <class 'sdf.BlockConstant'> [1]
dist_fn_x_px_Left <class 'sdf.BlockPlainVariable'> [400, 200]
dist_fn_x_px_Right <class 'sdf.BlockPlainVariable'> [400, 200]

These are the names of the variables in the data structure. This example is taken from the supplied two_stream.deck example in 1D.

Working with the data in an SDF file

You can access the underlying data using the names obtained from list_variables

variable = data.Electric_Field_Ex

This returns an instance of either sdf.BlockPlainVariable or sdf.BlockPointVariable depending on whether you have requested a grid variable (such as Ex, Ey or a distribution function) or a particle variable (such as particle momentum or weight). The raw contents of the variable is a numpy array. It is then available using the data element of these objects.

import numpy as np
variable = data.Electric_Field_Ex
raw = variable.data
print(type(raw))
print(np.mean(raw))

produces the output

<type 'numpy.ndarray'>
-1.27980874427008e-06

Plotting using sdf_helper

The sdf_helper wrapper script comes with some plotting routines. They are incomplete currently, but aim to provide as close as possible to press ready figures in a single command. You need the matplotlib library to use these routines, and they are only available for 1D and 2D data at present. To plot data, simply provide an sdf.BlockPlainVariable object to the routine plot_auto. An example of plotting a 1D variable, using the two_stream.deck example deck to generate the figures would be

import sdf_helper as sh
import matplotlib.pyplot as plt

plt.ion()
data=sh.getdata('Data/0010.sdf')
sh.plot_auto(data.Current_Jx)

This will produce a window similar to the image shown here, with slight difference depending on your version of matplotlib and your operating system. The code plt.ion() sets matplotlib to interactive mode, so control will be returned to you as soon as the plot has finished drawing.

Example 1D plot generated by sdf_helper.plot_auto

Plotting a 2D function is the same basic idea, and the code

import sdf_helper as sh
import matplotlib.pyplot as plt

plt.ion()
data=sh.getdata('Data/0010.sdf')
sh.plot_auto(data.dist_fn_x_px_Right, iso=0)

 thumb \| 200px \| Example 2D plot generated bysdf_helper.plot_auto

will produce the figure on the right. The procedure for variables from EPOCH2D data is exactly the same.

Changing colour tables

The easiest solution to changing colour tables is to set the global colour table. This is done by

import matplotlib.pyplot as plt
plt.set_cmap(tablename)

where tablename is a string describing the colour table to be used. The available strings are given here

Some bugs in matplotlib

There are some bugs in matplotlib which can mean that sometimes the 2D images don’t render properly. If you get incorrect rendering, please try updating matplotlib to the latest version for your platform. If that doesn’t work then pass the parameter compatibility=True to the plot_auto routine. This may make the plot slightly less pretty, but tends to work on more platforms.

Core Python library

The SDF python reader allows you to read any SDF file and access any information within the file. It has very few user friendly features to assist working with the files. Some of the methods listed in the section on sdf_helper (notably list_variables) are not available when using the core library. Loading an sdf file with the core library has the following syntax

import sdf
data=sdf.read(filename)

where filename is a string containing the name of the file to be loaded. This returns an sdf.BlockList object

The sdf.BlockList object

The list_variables routine is added by the sdf_helper wrapper, but you can check what elements are in the file by simply typing

data.__dict__

Which will produce an output like the following example from EPOCH2D

{'Header': {'filename': '/Users/phsiav/dev/epoch/epoch2d/Data/0005.sdf', 'file_version': 1, 'file_revision': 4, 'code_name': 'Epoch2d', 'step': 53, 'time': 2.5293132385759517e-14, 'jobid1': 1552896563, 'jobid2': 376, 'code_io_version': 1, 'restart_flag': False, 'other_domains': False, 'station_file': False}, 'Wall_time': <sdf.BlockConstant object at 0x11a012318>, 'Electric_Field_Ex': <sdf.BlockPlainVariable object at 0x11a012220>, 'Electric_Field_Ey': <sdf.BlockPlainVariable object at 0x11a012128>, 'Electric_Field_Ez': <sdf.BlockPlainVariable object at 0x11a012030>, 'Magnetic_Field_Bx': <sdf.BlockPlainVariable object at 0x117b2ceb8>, 'Magnetic_Field_By': <sdf.BlockPlainVariable object at 0x117b2cdc0>, 'Magnetic_Field_Bz': <sdf.BlockPlainVariable object at 0x117b2ccc8>, 'Grid_Grid': <sdf.BlockPlainMesh object at 0x117b2cbd0>, 'Grid_Grid_mid': <sdf.BlockPlainMesh object at 0x117b2cad8>, 'Grid_CPUs_Original_rank': <sdf.BlockPlainMesh object at 0x117b2c9e0>, 'Grid_CPUs_Original_rank_mid': <sdf.BlockPlainMesh object at 0x117b2c8e8>, 'CPUs_Original_rank': <sdf.BlockPlainVariable object at 0x117b2c7f0>, 'CPUs_Current_rank': <sdf.BlockPlainVariable object at 0x11a015128>}

The sdf.BlockPlainVariable object

These objects represent the variables in the SDF file. It does not fully implement the dict property, so to inspect it’s contents you must use

dir(data.Electric_Field_Ey)

which produces an output like

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'blocklist', 'data', 'data_length', 'datatype', 'dims', 'grid', 'grid_id', 'grid_mid', 'id', 'mult', 'name', 'stagger', 'units']

The key elements are data which contains the raw data for the variable stored as a numpy array, dims which is an array containing the number of elements in each dimension of the array and grid and grid_mid which refer to sdf.BlockPlainMesh objects that represent the grid axes that the variable is to be plotted against. Grid and grid_mid do similar but different things. Grid is an array of points corresponding to the edges of the computational cells, grid_mid to the midpoints. This means that all of the arrays in grid are one element longer than the arrays in grid_mid. To identify whether to use grid or grid_mid you must compare the sizes of the variable dims array to the sizes of the grid and grid_mid sizes and for each axis use the element of grid or grid_mid that has the same number of elements.

Important note! - 2D SDF data is loaded into Python rotated by 90 degrees compared to the original Fortran code that generated it.

The sdf.BlockPlainMesh object

Once again you have to use the dir command to output the information about an sdf.BlockPlainMesh object, for example in EPOCH

dir(data.Grid_Grid)

Which produces output like

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'blocklist', 'data', 'data_length', 'datatype', 'dims', 'extents', 'geometry', 'id', 'labels', 'mult', 'name', 'units']

The important element of this block is data which is a tuple of 1D numpy arrays corresponding to each coordinate axis of the grid.

Plotting a variable using raw SDF and raw matplotlib

  • Warning - This is not our recommended suggestion for plotting. We recommend using our helper routines in sdf_helper*
import matplotlib.pyplot as plt
import sdf

data=sdf.read('Data/0005.sdf')
ey = data.Electric_Field_Ey
plt.pcolormesh(ey.grid_mid.data[0], ey.grid_mid.data[1], ey.data.T)
plt.show()
Previous