helita

Helita documentation can be found here (the installation section is taken from this link):

http://helita.readthedocs.io/en/latest/index.html

Helita is a Python library for solar physics focused on interfacing with code and projects from the Institute of Theoretical Astrophysics (ITA) at the University of Oslo. The name comes from Helios + ITA. Currently, the library is a loose collection of different scripts and classes with varying degrees of portability and usefulness.


Installation

Pre-requisites

To make use of helita you need a Fortran compiler (GFortran is recommended), because some modules are compiled from Fortran. The packages in the left column should be installed before installing helita. The right column contains recommended packages that allow the user to take advantage of all of the features.

Required Recommended

Helita will install without the recommended packages, but functionality will be limited. All of these packages are available through Anaconda, and that is the recommended way of setting up your Python distribution.

Cloning git repository

The easiest way is to use git to clone the repository. To grab the latest version of helita and install it:

$ git clone https://github.com/ITA-solar/helita.git
$ cd helita
$ python setup.py install

Note

The majority of the most updated versions of bifrost.py, ebysus.py, and all things bifrost related are in the https://github.com/jumasy/helita.git fork.

Non-root install

If you don’t have write permission to your Python packages directory, use the following option with setup.py:

$ python setup.py install --user

This will install helita under your home directory (typically ~/.local)

Developer install

If you want to install helita but also actively change the code or contribute to its development, it is recommended that you do a developer install instead:

$ python setup.py develop

This will set up the package, such as the source files used, from the git repository that you cloned (only a link to it is placed on the Python packages directory). Can also be combined with the -user flag for local installs:

$ python setup.py develop --user

Installing with different C or Fortran compilers

The procedure above will compile the C and Fortran modules using the default gcc/gfortran compilers. It will fail if these are not available in the system. If you want to use a different compiler, please use setup.py with the -compiler=xxx and/or -fcompiler=yyy options, where xxx, yyy are C and Fortran compiler families (names depend on system). To check which Fortran compilers are available in your system, you can run:

$ python setup.py build --help-fcompiler

and to check which C compilers are available:

$ python setup.py build --help-compiler

bifrost.py

bifrost.py is a set of tools to read and interact with output from Bifrost simulations. LMSAL has also contributed to this.

BifrostData class

bifrost.py includes the BifrostData class (among others). The following allows to create the class for snapshot number 430 from simulation ‘cb10f’ from directory ‘/net/opal/Volumes/Amnesia/mpi3drun/Granflux’ (a network local to LMSAL):

[1]:
from helita.sim import bifrost as br
[2]:
dd = br.BifrostData('cb10f', snap = 430, fdir = '/net/opal/Volumes/Amnesia/mpi3drun/Granflux', verbose = False)

The snapshot(s) being read can be defined when creating the object, or set/changed anytime later. Snaps can be ints, arrays, or lists.

Getting variables & quantities

get_var and get_varTime can be used to read variables as well as to calculate quantities (with a call to _get_quantity). iix, iiy, and iiz are axis positions, or grid points. They can be specified to slice the return array (they can be ints, arrays, or lists). If iix, iiy, or iiz is not specified, get_var will read all the numerical domain along the x, y, or z axis, respectively. If no snapshot is specified, the current snap value will be used (either the initialized one, which is the first in the series, or the most recent snap used in set_snap or in a call to get_var). To get variable ‘r’ at snapshot 430 (a specific timestep) with only values at iiy = 200, iiz = 5, and iiz = 7:

[3]:
var1 = dd.get_var('r', snap = 430, iiy = 200, iiz = [5, 7])
WARNING: cstagger use has been turned off, turn it back on with "dd.cstagop = True"

get_varTime can be used in the same fashion, with the added option of reading a specific variable or quantity from many snapshots at once. Its return arrays have an added dimension for time.

Several of the class’s parameters can be found in the dictionary params. This dictionary contains most of the parameters in .idl files. It includes information about time (t and dt) and the axes (x, y, z, dx, dy, and dz) among other things. When snap contains more than one snapshot, many of the parameters in the dictionary contain one entry for each snap. To get time:

[4]:
time = dd.params['t']

To view all available keys:

[5]:
dd.params.keys()
[5]:
dict_keys(['mx', 'my', 'mz', 'mb', 'nstep', 'nstepstart', 'debug', 'periodic_x', 'periodic_y', 'periodic_z', 'ndim', 'u_l', 'u_t', 'u_r', 'u_p', 'u_u', 'u_kr', 'u_ee', 'u_e', 'u_te', 'u_tg', 'u_b', 'meshfile', 'dx', 'dy', 'dz', 'cdt', 'dt', 't', 'timestepdebug', 'nu1', 'nu2', 'nu3', 'nu_r_xy', 'nu_r_xy_k', 'nu_r', 'nu_r_min', 'nu_r_k', 'nu_ee_xy', 'nu_ee', 'grav', 'eta3', 'ca_max', 'mhddebug', 'do_mhd', 'mhdclean', 'one_file', 'snapname', 'isnap', 'large_memory', 'nsnap', 'nscr', 'aux', 'dtsnap', 'newaux', 'dtscr', 'tsnap', 'tscr', 'boundarychk', 'max_r', 'smooth_r', 'qmax', 'noneq', 'do_hion', 'gamma', 'tabinputfile', 'do_rad', 'dtrad', 'quadrature', 'zrefine', 'maxiter', 'taustream', 'accuracy', 'strictint', 'linear', 'monotonic', 'minbin', 'maxbin', 'dualsweep', 'teff', 'timing', 'spitzer', 'debug_spitzer', 'info_spitzer', 'spitzer_amp', 'theta_mg', 'dtgerr', 'ntest_mg', 'tgb0', 'tgb1', 'tau_tg', 'fix_grad_tg', 'niter_mg', 'bmin', 'do_genrad', 'genradfile', 'debug_genrad', 'incrad_detail', 'incrad_quad', 'dtincrad', 'debug_incrad', 'bctypelower', 'tau_bcl', 'tau_ee_bcl', 'tau_d2_bcl', 'tau_d5_bcl', 'tau_d6_bcl', 'tau_d7_bcl', 'tau_d8_bcl', 's0', 'e0', 'r0', 'cs0', 'p0', 'nsmooth_bcl', 'naver_bcl', 'nclean_bcl', 'nclean_lbl', 'nclean_ubl', 't_bdry', 'rbot', 'ebot', 'bx0', 'by0', 'x0_bcu', 'x1_bcu', 'y0_bcu', 'y1_bcu', 'uz_bcu', 'strtb', 'rtb', 'xotb', 'zotb', 'qtb', 'nclean_bcu'])
Variables
Simple r, px, py, pz, e, bx, by, bz, p, tg, i1, i4, qjoule, qspitz
Composite ux, uy, uz, ee, s
Calculated Quantities
Derivatives dxup, dyup, dzup, dxdn, dydn, dzdn
Centers vector quantity in cells xc, yc, zc
Module of vector ‘mod’ + root letter of varname (eg. modb)
Divergence of vector ‘div’ + root letter of varname (eg. divb)
Squared module root letter of varname + ‘2’ (eg. u2)
Ratio of two vars var1 + ‘rat’ + var2 (eg. rratpx)
Eostab (unit conversion to SI) ne, tg, pg, kr, eps, opa, temt
Magnitude of vector components // or ⟂ root letter of v1 + ‘par’ or ‘per’ + root letter of v2 (eg. uparb)
Current/Vorticity ix, iy, iz, wx, wy, wz
Flux pfx, pfy, pfz, pfex, pfey, pfez, pfwx, hx, hy, hz, kx, ky, kz
Plasma beta, s, ke, mn, man, hp
Wave speeds alf, fast, long, va, cs, vax, vay

Small Demo

Here, we plot dz versus z, illustrating the non-uniform gaps in the z axis.

[6]:
from helita.sim import bifrost as br
import matplotlib.pyplot as plt
import numpy as np

rootname='l2d90x40r_it' #this is for the 2D case
fdir='/net/opal/Volumes/Amnesia/mpi3drun/2Druns/genohm/rain/l2d90x40r'
dd=br.BifrostData(rootname,fdir=fdir, verbose = False) #this loads the structure
var=dd.get_var('r',305) #this reads the density (r) for the instant 305

# gets the z coordinates of data points and makes an empty array of the same length for values of dz
zarr = dd.z
length = zarr.shape[0]
dzarr = np.empty([length])

# iterates through zarr and sets each entry in dzarr as the difference between the next and current values of z
# for the final value of dzarr, sets it to be the same as the previous value of dzarr
for i, val in enumerate(zarr):
    if i < length - 1 :
        dzarr[i] = zarr[i + 1] - val
        i = i + 1

    else :
        dzarr[i] = dzarr[i - 1]

# plots z vs dz and labels the axes
plt.plot(zarr, dzarr)
plt.xlabel('Z')
plt.ylabel('dZ')
plt.show()


WARNING: cstagger use has been turned off, turn it back on with "dd.cstagop = True"
_images/helita_12_1.png

In this example, we interpolate temperature data from the simulation to conform to a uniform z axis and plot the data with both the original, and the even, z axes.

[7]:
import scipy.interpolate as sp
# bifrost already imported and dd initialized after previous example

temp = dd.get_var('tg', 300)

zarr = dd.z
xarr = dd.x

# at instant 305 -  x is uniform, y is kept constant, and z is non uniform

# converts the 3d array with width of 1 to 2d array (and transposes because otherwise image is sidewise)
temp2d = np.transpose(temp[:,0])

f = sp.interp2d(xarr, zarr, temp2d)

# makes a new uniform z, x is already uniform
zlength = zarr.shape[0]
zstart = zarr[0]
zend = zarr[zlength - 1]
newz = np.linspace(zstart, zend, zlength)

# makes the interpolated data with the previously created interpolation function acting on the new uniform axes
uniform_a = f(xarr, newz)

fig = plt.figure(figsize = (15, 3))

# optional plotting of the non uniform data to show the difference
ax0 = fig.add_subplot(1, 2, 1)
im0 = ax0.imshow(temp2d, cmap = 'hot', extent = (xarr[0], xarr[xarr.shape[0] - 1], zend, zstart), aspect = 'equal')
ax0.set_title('Non-Uniform, Temperature at t = 300')
ax0.set_xlabel('x')
ax0.set_ylabel('z')
c0 = fig.colorbar(im0, ax = ax0)
c0.set_label('tg')

ax1 = fig.add_subplot(1, 2, 2)
im1 = ax1.imshow(uniform_a, cmap = 'hot', extent = (xarr[0], xarr[xarr.shape[0] - 1], zend, zstart), aspect = 'equal')
ax1.set_title('Uniform, Temperature at t = 300')
ax1.set_xlabel('x')
ax1.set_ylabel('z')
c1 = fig.colorbar(im1, ax = ax1)
c1.set_label('tg')
plt.tight_layout()
plt.show()

_images/helita_14_0.png

create_new_br_files class

bifrost_units class

Rhoeetab class

Opatab class

FFTData class

This class can be found within bifrost_fft.py. It performs operations on Bifrost simulation data in its native format. After creating a class for a specific snap root name and directory (much like with BifrostData), one can get a dictionary of the frequency and amplitude of the Fourier Transform for a certain quantity over a range of snapshots.

We have defined 3 variables that allow us to decompose the velocity in Alfvenic, fast mode and longitudinal component (‘alf’, ‘fast’, and ‘long’). Here, we show the transformation of ‘alf’,

[8]:
from helita.sim import bifrost_fft as brft
[9]:
dd = brft.FFTData(file_root = 'cb10f', fdir = '/net/opal/Volumes/Amnesia/mpi3drun/Granflux')
[10]:
transformed = dd.get_fft('alf', snap = [430, 431, 432], iix = 5, iiy = 20)
WARNING: cstagger use has been turned off, turn it back on with "dd.cstagop = True"
[11]:
transformed.keys()
[11]:
dict_keys(['freq', 'ftCube'])

Depending on the number of snaps and the size of the cube, using cuda or python multiprocessing may speed up the calculation.

1. Using cuda

If pycuda is available, the code imports reikna (a python library that contains fft functions using pycuda). In order to make use of the GPU, use the function run_gpu(). The default is to not use the GPU, even if there is one available.

[12]:
dd.run_gpu() # to use GPU
dd.run_gpu(False) # to stop use of GPU

When get_fft() is called, the GPU will be used in accordance with the last call to run_gpu(). If the GPU has limited memory, the user can specify numBlocks in the call to get_fft(). This will send the calculation over to the GPU in several blocks as opposed to all at once. To use 5 different blocks, a call would look like this:

[13]:
usingBlocks = dd.get_fft('bx', snap = [400, 401, 402], numBlocks = 5)

2. Using python multiprocessing

This can be used whether or not pycuda is available, as multiprocessing is a library that comes with python. It makes use of threading on the CPU. In order to use a multiprocessing threadpool when calculating the Fourier Transform, specify numThreads with a number greater than 1, when calling get_fft():

[14]:
usingThreads = dd.get_fft('bx', snap = [400, 401, 402], numThreads = 10)

FFT demo #1

This first demo tests the get_fft() method with standard functions: a sine wave, a gaussian curve, and y = 0. It pre-sets dd.preTransform and

[15]:
import numpy as np
import helita.sim.cstagger
from helita.sim.bifrost import BifrostData, Rhoeetab, read_idl_ascii
from helita.sim.bifrost_fft import FFTData
import matplotlib.pyplot as plt

# note: this calls bifrost_fft from user, not /sanhome

dd = FFTData(file_root='cb10f',
             fdir='/net/opal/Volumes/Amnesia/mpi3drun/Granflux')

# test 1: ft of y = sin(8x)
x = np.linspace(-np.pi, np.pi, 201)
dd.preTransform = np.sin(8 * x)
dd.freq = np.fft.fftshift(np.fft.fftfreq(np.size(x)))
dd.run_gpu(False)
# preTransform is already set
tester = dd.get_fft('not a real var', snap='test')
fig = plt.figure(figsize=(15,10))

numC = 3
numR = 2

# plotting original sin signal
ax0 = fig.add_subplot(numC, numR, 1)
ax0.plot(x, dd.preTransform)
ax0.set_title('original signal' + '\n\nsine wave')

# plotting transformation sin signal
ax1 = fig.add_subplot(numC, numR, 2)
ax1.plot(tester['freq'], tester['ftCube'])
ax1.set_title('bifrost_fft get_fft() of signal' + '\n\n ft of sine wave')
ax1.set_xlim(-.2, .2)

# test 2: ft of gaussian curve
n = 30000  # Number of data points
dx = .01  # Sampling period (in meters)
x = dx*np.linspace(-n/2, n/2, n)  # x coordinates

stanD = 2  # standard deviation
dd.preTransform = np.exp(-0.5 * (x/stanD)**2)

# plotting original gaussian signal
ax2 = fig.add_subplot(numC, numR, 3)
ax2.plot(x, dd.preTransform)
ax2.set_xlim(-25, 25)
ax2.set_title('gaussian curve')

# plotting transformation of gaussian signal
dd.freq = np.fft.fftshift(np.fft.fftfreq(np.size(x)))
ft = dd.get_fft('not a real var', snap='test')  # preTransform is already set
ax3 = fig.add_subplot(numC, numR, 4)
ax3.plot(ft['freq'], ft['ftCube'])
ax3.set_xlim(-.03, .03)
ax3.set_title('ft of gaussian curve')

# test 3: ft of y = 0
# plotting original horizontal line
x = np.linspace(-20, 20, 50)
dd.preTransform = [0] * 50
ax4 = fig.add_subplot(numC, numR, 5)
ax4.plot(x, dd.preTransform)
ax4.set_title('y = 0')

# plotting transformed signal
dd.freq = np.fft.fftshift(np.fft.fftfreq(np.size(x)))
ft = dd.get_fft('not a real var', snap='test')  # preTransform is already set
ax5 = fig.add_subplot(numC, numR, 6)
ax5.plot(ft['freq'], ft['ftCube'])
ax5.set_title('ft of y = 0')

plt.tight_layout()
plt.show()
_images/helita_28_0.png

FFT demo #2

Here, we use get_fft() to find the transformation result for bx at each z position (from a local network containing 2d simulations).

[22]:
import numpy as np
import helita.sim.cstagger
from helita.sim.bifrost import BifrostData, Rhoeetab, read_idl_ascii
from helita.sim.bifrost_fft import FFTData
import matplotlib.pyplot as plt

snaps = np.arange(280, 360)
v = 'bx'

dd = FFTData(file_root='l2d90x40r_it',
             fdir='/net/opal/Volumes/Amnesia/mpi3drun/2Druns/genohm/rain/l2d90x40r/')

# getting ft
transformed = dd.get_fft(v, snaps)
ft = transformed['ftCube']
freq = transformed['freq']
zaxis = dd.z

# making empty array to later contain the avergaes for each z position
zstack = np.empty([np.size(freq), np.shape(ft)[1]])
# filling ztack with average ft for each (x,y) in each z level
for k in range(0, np.shape(ft)[1]):
    avg = np.average(ft[:, k, :], axis=(0))
    zstack[:, k] = avg

# preparing plots
fig = plt.figure(figsize = (15, 3))
numC = 1
numR = 2

# ploting freq vs amp with multiple lines (1 for each z position)
ax0 = fig.add_subplot(numC, numR, 1)
ax0.plot(freq, zstack)
ax0.set_xlabel('Frequency')
ax0.set_ylabel('Amplitude')
ax0.set_title(
    'Average Amplitude of FT Frequencies at Different Z Positions (1)')
ax0.set_aspect('auto')

# plotting amp at different freq & z with image
ax1 = fig.add_subplot(numC, numR, 2)
im1 = ax1.imshow(zstack.transpose(), extent=[freq[0], freq[-1], zaxis[0], zaxis[-1]])
ax1.set_xlabel('Frequency')
ax1.set_ylabel('Z Position')
ax1.set_title(
    'Average Amplitude of FT Frequencies at Different Z Positions (2)')
ax1.set_aspect('auto')
c1 = fig.colorbar(im1, ax = ax1)
c1.set_label('Amplitude')
plt.tight_layout()
plt.show()
WARNING: cstagger use has been turned off, turn it back on with "dd.cstagop = True"
_images/helita_30_1.png