Sandbox/python/0618TokRegime/Golem workshop.py


# coding: utf-8

# # Golem Workshop Kiten 2018 - Data Processing
# 
# This is a notebook for data processing of the Golem operation domain scan.

# In[1]:

get_ipython().magic('matplotlib inline')

# At first, load some basic libraries.

import numpy as np
from urllib.request import urlopen
import shutil
import os 
from matplotlib import pyplot as plt
from scipy.interpolate import interp1d

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm

# Plot settings, can be changed to globally influence the appearance

plt.rc('text', usetex=True)
plt.rc('font', family='sans-serif')
plt.rc('font', size=22)          # controls default text sizes
plt.rc('axes', titlesize=22)     # fontsize of the axes title
plt.rc('axes', labelsize=20)    # fontsize of the x and y labels
plt.rc('xtick', labelsize=20)    # fontsize of the tick labels
plt.rc('ytick', labelsize=20)    # fontsize of the tick labels
plt.rc('legend', fontsize=22)    # legend fontsize
plt.rc('figure', titlesize=22)  # fontsize of the figure title


# ## Golem Database Access
# 
# Thanks to Golem staff for the golem_data function.
# 
# This should not be changed :).

# In[2]:

def golem_data(shot, diagn):
    """
     Simple interface for GOLEM database
      Use:
	obj = golem_data(10011, 'loop_voltage')
	plot(obj.tvec, obj.data)

	d - object containing all availible informations
	d.tvec  - time vector Nx1
	d.data - data matrix  NxM

      Example:
      from golem_data import golem_data
      from matplotlib.pyplot import *
      obj1 = golem_data(10689 , 'electron_temperature')
      obj2 = golem_data(10689 , 'spectrometr:temperature')
      plot(obj1.tvec,obj1.data,  label=obj1.labels)  %
      errorbar(obj2.tvec, obj2.data, xerr=obj2.tvec_err, yerr=[obj2.data_err[:,0], obj2.data_err[:,1]],  label=obj2.labels )
      xlabel(obj2.ax_labels[0])
      ylabel(obj2.ax_labels[1])
      legend([obj1.name, obj2.name])
      axis([obj1.plasma_start, obj1.plasma_end, 0, None])
      title(obj1.info)
      show()
    """
      
    remote_file = "http://golem.fjfi.cvut.cz/utils/data/"+str(shot)+"/"+diagn+'.npz'
    
    gfile = np.DataSource().open(remote_file, mode='rb')  # remote file
    try:
        d =  dict(np.load(gfile, encoding='bytes', fix_imports=True))
    except IOError:
        raise IOError('Missing diagnostic ' + str(diagn) + ' in shot ' + str(shot))
    
    if not 'tvec' in d and 't_start' in d:
        d['tvec'] = np.linspace( d.pop('t_start'), d.pop('t_end'), len(d['data']) )
    
    try:
        if 'scale' in d:
            d['data'] = np.double(d['data']) * d.pop('scale')  # rescale data 
    except:
        pass
    return Data( **d )


class Data(object):
    """ Simple data handling object"""
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
    def __repr__(self):
        return "Data object, keys:\n" + str(self.__dict__.keys())
    def __getitem__(self, key):
        return getattr(self, key)
	


# ## Some Useful Scripts
# 
# ### Validate
# Excludes bad shots from a shot list.

# In[3]:

def Validate(shot_list, bad_shots):
    valid = [];
    for shot_no in shot_list:
        try:
            if not(shot_no in bad_shots): 
                b_v = golem_data(shot_no, 'breakdown_voltage').data
                valid.append(shot_no)
        except Exception:
            pass
    return valid;


# ### GetValAtTime
# 
# Takes a diagnostic obtained by `golem_data` and interpolates its value for a specific time in the shot.
# 
# Calling with `plot_progress = True` will show the interpolation on the graph.

# In[4]:

def GetValAtTime(diag, time, plot_progress = False):
    f = interp1d(diag.tvec, diag.data)
    # change to true if you want to see the plots
    if plot_progress:
        fig = plt.figure()
        plt.plot(diag.tvec, diag.data, linewidth=2)
        t = np.linspace(min(diag.tvec), max(diag.tvec), 50)
        plt.plot(t, f(t), linewidth=1)
        plt.plot([time, time], [min(diag.data), max(diag.data)], '--')
        plt.plot(time, f(time), 'o')
        plt.xlabel('$t$ [s]')
        plt.ylabel('$B_\mathrm{t}$ [T]')
        plt.grid(True, axis='both')

        plt.show()
        plt.close()
    return f(time)


# ### Load
# 
# Loads all specified diagnostics for a specified shot number.
# 
# Any newly requested diagnostics should be added here.
# 
# **To use the diagnostic in the scan, one point should be selected (e.g. by interpolation for a specific time).** Toroidal field is now treated like this.

# In[5]:

# Diagnostics should be added here (according to the sample fro)
def Load(shot_no):
    data_dict = {
        'shot_no': shot_no,
        'pressure_request': golem_data(shot_no, 'pressure_request').data,
        'pressure': golem_data(shot_no, 'pressure').data,
        'ucd': golem_data(shot_no, 'ucd').data,
        'electron_temperature': golem_data(shot_no, 'electron_temperature_max').data,
        'breakdown_voltage': golem_data(shot_no, 'breakdown_voltage').data,
        'toroidal_field': GetValAtTime(golem_data(shot_no, 'toroidal_field'), 
                                       golem_data(shot_no, 'plasma_start').data)
        }
    return data_dict


# ### GetScan1, GetScan2
# 
# Sorts the data for further processing. Poorly written, should be somewhat more refined (to allow for arbitrary N-dimensional scans).

# In[6]:

def GetScan1(data_list, scan_var, diag_var):
    d_l = sorted(data_list, key=lambda d: d[scan_var])
    return {
            'x': [d[scan_var] for d in d_l],
            'val': [d[diag_var] for d in d_l],
            }

def GetScan2(data_list, scan_var, diag_var):
    d_l = sorted(data_list, key=lambda d: (d[scan_var[0]], d[scan_var[1]]))
    return {
            'x': [d[scan_var[0]] for d in d_l],
            'y': [d[scan_var[1]] for d in d_l],
            'val': [d[diag_var] for d in d_l],
            }


# ### ScanPlot2D
# 
# Plots a 2D scan (scaled colormap).

# In[7]:

def ScanPlot2D(golem_scan, scan_id, scan_description):
    fig = plt.figure(figsize=(8, 6), dpi=80)

    plt.tricontourf(golem_scan[scan_id]['x'], golem_scan[scan_id]['y'], 
                    golem_scan[scan_id]['val'])
    plt.plot(golem_scan[scan_id]['x'], golem_scan[scan_id]['y'], 'ko', ms=3)
    plt.xlabel('$p$ [mPa]')
    plt.ylabel('$U_\mathrm{CD}$ [V]')
    c = plt.colorbar()
    c.set_label(scan_description)
    return fig


# ### ScanPlotLines
# 
# Plots a 2D scan as a series of lines grouped by one of scan variables (can be selected by setting `group_by` to either `x` or `y`.

# In[8]:

def ScanPlotLines(golem_scan, group_by, scan_id, scan_description):
    fig = plt.figure(figsize=(8, 6), dpi=80)

    x_axis = 'x' if group_by == 'y' else 'y';
    
    x_label = '$p$ [mPa]' if x_axis == 'x' else '$U_\mathrm{CD}$ [V]'
    lines_label = 'mPa' if x_axis == 'y' else 'V'
    
    unique = set(golem_scan[scan_id][group_by])
    unique = sorted(list(unique))
    for u_v in unique:
        data_x = []
        data_y = []
        for i in range(len(golem_scan[scan_id][x_axis])):
            if golem_scan[scan_id][group_by][i] == u_v:
                data_x.append(golem_scan[scan_id][x_axis][i])
                data_y.append(golem_scan[scan_id]['val'][i])
        
        plt.plot(data_x, data_y, 'o--', label = '{0} {1}'.format(u_v, lines_label))
    
    plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
    plt.xlabel(x_label)
    plt.ylabel(scan_description)
    plt.grid()
    
    return fig


# ## Data Load
# 
# Let's load the data from selected shots.

# In[9]:

# Kiten scan
shot_list = range(27454, 27491, 1)
bad_shots = [27481, 27482, 27483]

# Second scan (Prague 28/06)
shot_list = [27617, 27618, 27619, 27620, 27621, 27622, 27623, 27624, 27625, 27626, 27627, 27628, 27629, 27630, 
             27633, 27634, 27635, 27636, 27637, 27638, 27639, 27640, 27641, 27642, 27643, 27644, 27645, 27646, 
             27647, 27648, 27649, 27650, 27651, 27652, 27653, 27654, 27655, 27656, 27657, 27658, 27659, 27660, 
             27661, 27662, 27663, 27664, 27665, 27666, 27667, 27668, 27669, 27670, 27671, 27672, 27673, 27674]
bad_shots = []

# Do the loading
valid_shots = Validate(shot_list, bad_shots)
data_list = [Load(s_no) for s_no in valid_shots]


# ## Data Processing
# 
# Three scans variables (`['breakdown_voltage', 'electron_temperature', 'toroidal_field']`) are selected and a scans in `pressure` and `ucd` (current drive voltage) are generated.
# 
# A second scan set is generated for requested pressure instead of real pressure.

# In[10]:

scan_vars = ['breakdown_voltage', 'electron_temperature', 'toroidal_field']

# the main scan
golem_scan = {};
for s_id in scan_vars:
    golem_scan[s_id] = GetScan2(data_list, ['pressure', 'ucd'], s_id)
    
# the auxiliary scan
golem_scan_request = {};
for s_id in scan_vars:
    golem_scan_request[s_id] = GetScan2(data_list, ['pressure_request', 'ucd'], s_id)


# ### Breakdown voltage
# We should see some minima on the Paschen curves.

# In[11]:

fig = ScanPlotLines(golem_scan, 'y', 'breakdown_voltage', '$U_\mathrm{b}$ [V]');
fig.savefig("grouped-p-U_bd.pdf", bbox_inches="tight");
fig.savefig("grouped-p-U_bd.png", bbox_inches="tight");


# ### 2D plots
# 
# * Breakdown voltage
# * Electron temperature
# * Toroidal field at the beginning of the discharge (plasma)

# In[12]:

fig = ScanPlot2D(golem_scan, 'breakdown_voltage', '$U_\mathrm{b}$ [V]');
fig.savefig("scan2d-p-U_cd-U_bd.pdf", bbox_inches="tight");
fig.savefig("scan2d-p-U_cd-U_bd.png", bbox_inches="tight");


# In[13]:

fig = ScanPlot2D(golem_scan, 'electron_temperature', '$T_\mathrm{e}$ [eV]');
fig.savefig("scan2d-p-U_cd-T_e.pdf", bbox_inches="tight");
fig.savefig("scan2d-p-U_cd-T_e.png", bbox_inches="tight");


# In[14]:

fig = ScanPlot2D(golem_scan, 'toroidal_field', '$B_\mathrm{t}$ [T]');
fig.savefig("scan2d-p-U_cd-B_t.pdf", bbox_inches="tight");
fig.savefig("scan2d-p-U_cd-B_t.png", bbox_inches="tight");


# ### Toroidal field 
# 
# Grouped by the requested pressure.

# In[15]:

fig = ScanPlotLines(golem_scan_request, 'x', 'toroidal_field', '$B_\mathrm{t}$ [T]');
fig.savefig("grouped-p-B_t.pdf", bbox_inches="tight");
fig.savefig("grouped-p-B_t.png", bbox_inches="tight");


# In[ ]: