Example : Intermittency detection in decaying plasma turbulence simulation

For more details about the data, see Ha et al. (2024).

This example script can be found at aweSOM/examples/plasma-turbulence/plasma.ipynb.

To download the datasets used in this example, go to https://doi.org/10.5281/zenodo.13909411.

Single aweSOM realization

The example hdf5 file, features_2j1b1e0r_5000_jasym.h5, contains 4 features, \(j_{\parallel}\), \(j_{\rm asym}\), \(b_{\perp}\), and \(e_{\parallel}\). Place the hdf5 file inside the examples/plasma-turbulence/ directory and run the script.

To run a single SOM realization, use the examples/plasma-turbulence/run_plasma_som.py script with only one value for each argument.

cd examples/plasma-turbulence
python run_plasma_som.py --ratio 0.6 --alpha_0 0.1 --train 2097152

This will train a SOM realization with an initial learning rate \(\alpha_0 = 0.1\), a lattice ratio \(H = 0.6\), and \(N = 2097152\) training steps (the entire simulation domain, \(L^3 = 128^3\)).

Optional toggles inside the script can be modified as needed. file_name points to the hdf5 file containing the simulation snapshot, sampling_type can be either “uniform” (random initial weights between -1 and 1) or “sampling” (random initial weights sampled from the data), and merge_threshold sets the threshold of cost for merging two cluster centroids.

Two files will be generated: som_object.*.pkl and labels.*.npy. The former contains the trained SOM object and the latter contains the cluster IDs for each data point. You can visualize the results following this example Jupyter notebook.

Also included on Zenodo is the fiducial SOM realization shown in Ha et al. (2024) under som_object.fiducial-5000-64-37-0.1-2097152-1u.pkl.

Load object to analyze

[1]:
import aweSOM
from aweSOM import Lattice
import numpy as np
import matplotlib.pyplot as plt
import pickle
import os

path_to_object = aweSOM.__path__[0] + '/../../examples/plasma-turbulence/'
object_name = 'som_object.fiducial-5000-64-37-0.1-2097152-1u.pkl'

with open(path_to_object + object_name, 'rb') as f:
    map = pickle.load(f)

xdim = map.xdim
ydim = map.ydim

Visualize the U-matrix

[2]:
map.umat = map.compute_umat(smoothing=1)
unique_centroids = map.get_unique_centroids(map.compute_centroids())

unique_centroids['position_x'] = [x+0.5 for x in unique_centroids['position_x']]
unique_centroids['position_y'] = [y+0.5 for y in unique_centroids['position_y']]

X,Y = np.meshgrid(np.arange(xdim)+0.5, np.arange(ydim)+0.5)

plt.figure(dpi=250)
plt.pcolormesh(map.umat.T, cmap='viridis')
plt.scatter(unique_centroids['position_x'],unique_centroids['position_y'], color='red', s=10)
plt.colorbar(fraction=0.02)
plt.contour(X, Y, map.umat.T, levels=np.linspace(np.min(map.umat),np.max(map.umat), 20), colors='black', alpha=0.5)
plt.gca().set_aspect("equal")
plt.title(rf'UMatrix for {xdim}x{ydim} SOM')

print('Number of centroids:', len(unique_centroids['position_x']))
Number of centroids: 23
../_images/notebooks_plasma_7_1.png

U-matrix history

[3]:
lattice_history = np.array(map.lattice_history)
umat_history = np.array(map.umat_history)
steps = np.linspace(0, lattice_history.shape[0] * map.save_frequency, lattice_history.shape[0], endpoint=False, dtype=int)

xdim = map.xdim
ydim = map.ydim
alpha_0 = map.alpha_0
train = map.train
X,Y = np.meshgrid(np.arange(xdim)+0.5, np.arange(ydim)+0.5)


num_plots = 6
fig, axs = plt.subplots(6, 1, figsize=(10, 25), sharex=True)
fig.subplots_adjust(hspace=0.2)
for i, k in enumerate(range(0, umat_history.shape[0], len(steps)//num_plots+1)):
    umat = umat_history[k]

    axs[i].title.set_text(f'Epoch = {steps[k]}')
    mesh = axs[i].pcolormesh(umat.T, cmap='viridis')
    axs[i].contour(X, Y, umat.T, levels=np.linspace(np.min(umat),np.max(umat), 20), colors='black', alpha=0.5, linewidths=0.7)
    axs[i].set_aspect("equal")

../_images/notebooks_plasma_9_0.png

Spider plots to show the relative importance of each feature on the centroids of the identified clusters

[4]:
plt.rcParams['font.size'] = 18
def plot_centroid_spider(loaded_som_object : Lattice, smoothing : float = None, merge_cost : float = 0.2):
    lattice_values = loaded_som_object.lattice

    # normalize the lattice values around its mean
    lattice_values = np.abs(lattice_values - lattice_values.mean(axis=0))

    mod_lattice_values = np.hstack((lattice_values, lattice_values[:,0].reshape(-1,1)))
    n_features = lattice_values.shape[1]
    features = loaded_som_object.features_names

    dict_lookup = {"j_mag" : r"$j$", "j_par" : r"$j_{\parallel}$", "j_perp" : r"$j_{\perp}$", "j_sym" : r"$j_{\rm s}$", "j_asym" : r"$j_{\rm a}$", "bz" : r"$b_z$", "b_perp" : r"$b_{\perp}$", "e_par" : r"$e_{\parallel}$", "e_perp" : r"$e_{\perp}$"}

    angles = [n / float(n_features) * 2 * np.pi for n in range(n_features)]
    angles += angles[:1]

    loaded_som_object.umat = loaded_som_object.compute_umat(smoothing=smoothing)
    naive_centroids = loaded_som_object.compute_centroids()
    merged_centroids = loaded_som_object.merge_similar_centroids(naive_centroids, threshold=merge_cost)
    unique_centroids = loaded_som_object.get_unique_centroids(merged_centroids)

    unique_centroids_coord = {}

    unique_centroids_coord['position_x'] = [x+0.5 for x in unique_centroids['position_x']]
    unique_centroids_coord['position_y'] = [y+0.5 for y in unique_centroids['position_y']]

    clusters = loaded_som_object.assign_cluster_to_lattice(smoothing=None,merge_cost=merge_cost)
    # numbering_clusters = list(range(len(unique_centroids['position_x'])))

    X,Y = np.meshgrid(np.arange(0,clusters.shape[0],1)+0.5, np.arange(0,clusters.shape[1],1)+0.5)
    dx=10/3
    umat = loaded_som_object.umat

    fig, ax1 = plt.subplots(dpi=300,)
    # ax1.axis('off')
    plt.pcolormesh(clusters.T, cmap='tab10', clim=[0,10], rasterized=True)
    # ax1.pcolormesh(umat.T, cmap='viridis')
    plt.scatter(unique_centroids_coord['position_x'],unique_centroids_coord['position_y'], color='white', s=18)
    # [plt.text(unique_centroids['position_x'][t]+1, unique_centroids['position_y'][t], numbering_clusters[t], color='k', fontsize=10) for t in range(len(numbering_clusters))]
    ax1.contour(X, Y, umat.T, levels=np.linspace(np.min(umat),(np.mean(umat)+np.min(umat))/2, 4), alpha=1, linewidths=0.7, colors='dimgray', linestyles='dotted')
    ax1.contour(X, Y, umat.T, levels=np.linspace((np.mean(umat)+np.min(umat))/2,np.mean(umat), 4), alpha=1, linewidths=0.7, colors='dimgray', linestyles='dashdot')
    ax1.contour(X, Y, umat.T, levels=np.linspace(np.mean(umat),(np.max(umat)+np.mean(umat))/2, 4), alpha=1, linewidths=0.7, colors='dimgray', linestyles='dashed')
    ax1.contour(X, Y, umat.T, levels=np.linspace((np.max(umat)+np.mean(umat))/2,np.max(umat), 4), alpha=1, linewidths=0.7, colors='dimgray', linestyles='solid')
    ax1.set_aspect("equal")
    ax1.set_title(f'{loaded_som_object.xdim}-{loaded_som_object.ydim}-{loaded_som_object.alpha_0}-{loaded_som_object.train} SOM')
    # ax1.set_xticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    # ax1.set_yticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    ax1.set_xlabel('X (node)')
    ax1.set_ylabel('Y (node)')

    for centroid_id in range(len(unique_centroids['position_x'])):
        row_values_of_centroids = [loaded_som_object.rowix(unique_centroids['position_x'][centroid_id], unique_centroids['position_y'][centroid_id])]

        # nrow = 2
        ncol = 3
        pos_x = [0.07, 0.37, 0.67]
        pos_y = [-0.25, -0.65, -1.05, -1.45, -1.85, -2.25]
        # pos_x = [1.1, 1.4, 1.7]
        # pos_y = [0.8, 0.3]
        position_of_subplot = pos_x[centroid_id % ncol], pos_y[centroid_id // ncol], 0.25, 0.25
        if centroid_id == 6:
            position_of_subplot = 0.3, 0.4, 0.25, 0.25
        ax2 = fig.add_axes(position_of_subplot, polar=True)
        ax2.set_xticks(angles[:-1], [dict_lookup[feature] for feature in features], color='black', size=17)
        ax2.set_rlabel_position(0)
        ax2.set_yticks([0, 0.6, 1.2], ["0", "0.6", "1.2"], color="dimgray", size=5)
        ax2.set_ylim(0.0,mod_lattice_values.max())
        # ax2.set_yscale('log')
        ax2.plot(angles, mod_lattice_values[row_values_of_centroids[0]], linewidth=1.5, linestyle='solid', color='black')
        ax2.fill(angles, mod_lattice_values[row_values_of_centroids[0]], 'k', alpha=0.3)
        ax2.set_facecolor(f'C{centroid_id}')

    plt.show()
[5]:
merge_cost = 0.25
smoothing = None # a number or None (cannot be 0)
plot_centroid_spider(map, smoothing=smoothing, merge_cost=merge_cost)
Centroid A: (np.int64(39), np.int64(26)), count: 190
Centroid B: (np.int64(37), np.int64(23)), count: 376
Merging...
Centroid A: (np.int64(44), np.int64(35)), count: 40
Centroid B: (np.int64(39), np.int64(35)), count: 39
Merging...
Centroid A: (np.int64(25), np.int64(19)), count: 47
Centroid B: (np.int64(18), np.int64(18)), count: 400
Merging...
Centroid A: (np.int64(56), np.int64(18)), count: 100
Centroid B: (np.int64(63), np.int64(17)), count: 133
Merging...
Centroid A: (np.int64(35), np.int64(0)), count: 97
Centroid B: (np.int64(26), np.int64(0)), count: 55
Merging...
Centroid A: (np.int64(29), np.int64(10)), count: 259
Centroid B: (np.int64(35), np.int64(0)), count: 152
Merging...
Centroid A: (np.int64(49), np.int64(20)), count: 92
Centroid B: (np.int64(37), np.int64(23)), count: 566
Merging...
Centroid A: (np.int64(29), np.int64(10)), count: 411
Centroid B: (np.int64(37), np.int64(23)), count: 658
Merging...
Centroid A: (np.int64(44), np.int64(35)), count: 79
Centroid B: (np.int64(37), np.int64(23)), count: 1069
Merging...
Centroid A: (np.int64(18), np.int64(18)), count: 447
Centroid B: (np.int64(37), np.int64(23)), count: 1148
Merging...
Centroid A: (np.int64(48), np.int64(0)), count: 12
Centroid B: (np.int64(55), np.int64(0)), count: 36
Merging...
Centroid A: (np.int64(30), np.int64(35)), count: 56
Centroid B: (np.int64(37), np.int64(23)), count: 1595
Merging...
Centroid A: (np.int64(52), np.int64(10)), count: 72
Centroid B: (np.int64(63), np.int64(17)), count: 233
Merging...
Centroid A: (np.int64(0), np.int64(26)), count: 51
Centroid B: (np.int64(0), np.int64(19)), count: 22
Merging...
Centroid A: (np.int64(63), np.int64(0)), count: 19
Centroid B: (np.int64(55), np.int64(0)), count: 48
Merging...
Centroid A: (np.int64(63), np.int64(17)), count: 305
Centroid B: (np.int64(37), np.int64(23)), count: 1651
Merging...
Centroid A: (np.int64(53), np.int64(36)), count: 42
Centroid B: (np.int64(37), np.int64(23)), count: 1956
Merging...
Centroid A: (np.int64(0), np.int64(36)), count: 37
Centroid B: (np.int64(0), np.int64(26)), count: 73
Merging...
Centroid A: (np.int64(17), np.int64(0)), count: 74
Centroid B: (np.int64(37), np.int64(23)), count: 1998
Merging...
Centroid A: (np.int64(19), np.int64(36)), count: 26
Centroid B: (np.int64(10), np.int64(35)), count: 22
Merging...
Number of unique centroids:  7
Minimum cost between centroids:  0.2846310205754125
Centroid A: (np.int64(39), np.int64(26)), count: 190
Centroid B: (np.int64(37), np.int64(23)), count: 376
Merging...
Centroid A: (np.int64(44), np.int64(35)), count: 40
Centroid B: (np.int64(39), np.int64(35)), count: 39
Merging...
Centroid A: (np.int64(25), np.int64(19)), count: 47
Centroid B: (np.int64(18), np.int64(18)), count: 400
Merging...
Centroid A: (np.int64(56), np.int64(18)), count: 100
Centroid B: (np.int64(63), np.int64(17)), count: 133
Merging...
Centroid A: (np.int64(35), np.int64(0)), count: 97
Centroid B: (np.int64(26), np.int64(0)), count: 55
Merging...
Centroid A: (np.int64(29), np.int64(10)), count: 259
Centroid B: (np.int64(35), np.int64(0)), count: 152
Merging...
Centroid A: (np.int64(49), np.int64(20)), count: 92
Centroid B: (np.int64(37), np.int64(23)), count: 566
Merging...
Centroid A: (np.int64(29), np.int64(10)), count: 411
Centroid B: (np.int64(37), np.int64(23)), count: 658
Merging...
Centroid A: (np.int64(44), np.int64(35)), count: 79
Centroid B: (np.int64(37), np.int64(23)), count: 1069
Merging...
Centroid A: (np.int64(18), np.int64(18)), count: 447
Centroid B: (np.int64(37), np.int64(23)), count: 1148
Merging...
Centroid A: (np.int64(48), np.int64(0)), count: 12
Centroid B: (np.int64(55), np.int64(0)), count: 36
Merging...
Centroid A: (np.int64(30), np.int64(35)), count: 56
Centroid B: (np.int64(37), np.int64(23)), count: 1595
Merging...
Centroid A: (np.int64(52), np.int64(10)), count: 72
Centroid B: (np.int64(63), np.int64(17)), count: 233
Merging...
Centroid A: (np.int64(0), np.int64(26)), count: 51
Centroid B: (np.int64(0), np.int64(19)), count: 22
Merging...
Centroid A: (np.int64(63), np.int64(0)), count: 19
Centroid B: (np.int64(55), np.int64(0)), count: 48
Merging...
Centroid A: (np.int64(63), np.int64(17)), count: 305
Centroid B: (np.int64(37), np.int64(23)), count: 1651
Merging...
Centroid A: (np.int64(53), np.int64(36)), count: 42
Centroid B: (np.int64(37), np.int64(23)), count: 1956
Merging...
Centroid A: (np.int64(0), np.int64(36)), count: 37
Centroid B: (np.int64(0), np.int64(26)), count: 73
Merging...
Centroid A: (np.int64(17), np.int64(0)), count: 74
Centroid B: (np.int64(37), np.int64(23)), count: 1998
Merging...
Centroid A: (np.int64(19), np.int64(36)), count: 26
Centroid B: (np.int64(10), np.int64(35)), count: 22
Merging...
Number of unique centroids:  7
Minimum cost between centroids:  0.2846310205754125
Number of clusters : 7
Centroids:  [(np.int64(0), np.int64(26)), (np.int64(0), np.int64(10)), (np.int64(0), np.int64(0)), (np.int64(19), np.int64(36)), (np.int64(55), np.int64(0)), (np.int64(63), np.int64(36)), (np.int64(37), np.int64(23))]
../_images/notebooks_plasma_12_1.png

Map SOM result to data

[6]:
final_clusters = map.assign_cluster_to_lattice(smoothing=smoothing, merge_cost=merge_cost)
som_labels = map.assign_cluster_to_data(map.projection_2d, final_clusters)
Centroid A: (np.int64(39), np.int64(26)), count: 190
Centroid B: (np.int64(37), np.int64(23)), count: 376
Merging...
Centroid A: (np.int64(44), np.int64(35)), count: 40
Centroid B: (np.int64(39), np.int64(35)), count: 39
Merging...
Centroid A: (np.int64(25), np.int64(19)), count: 47
Centroid B: (np.int64(18), np.int64(18)), count: 400
Merging...
Centroid A: (np.int64(56), np.int64(18)), count: 100
Centroid B: (np.int64(63), np.int64(17)), count: 133
Merging...
Centroid A: (np.int64(35), np.int64(0)), count: 97
Centroid B: (np.int64(26), np.int64(0)), count: 55
Merging...
Centroid A: (np.int64(29), np.int64(10)), count: 259
Centroid B: (np.int64(35), np.int64(0)), count: 152
Merging...
Centroid A: (np.int64(49), np.int64(20)), count: 92
Centroid B: (np.int64(37), np.int64(23)), count: 566
Merging...
Centroid A: (np.int64(29), np.int64(10)), count: 411
Centroid B: (np.int64(37), np.int64(23)), count: 658
Merging...
Centroid A: (np.int64(44), np.int64(35)), count: 79
Centroid B: (np.int64(37), np.int64(23)), count: 1069
Merging...
Centroid A: (np.int64(18), np.int64(18)), count: 447
Centroid B: (np.int64(37), np.int64(23)), count: 1148
Merging...
Centroid A: (np.int64(48), np.int64(0)), count: 12
Centroid B: (np.int64(55), np.int64(0)), count: 36
Merging...
Centroid A: (np.int64(30), np.int64(35)), count: 56
Centroid B: (np.int64(37), np.int64(23)), count: 1595
Merging...
Centroid A: (np.int64(52), np.int64(10)), count: 72
Centroid B: (np.int64(63), np.int64(17)), count: 233
Merging...
Centroid A: (np.int64(0), np.int64(26)), count: 51
Centroid B: (np.int64(0), np.int64(19)), count: 22
Merging...
Centroid A: (np.int64(63), np.int64(0)), count: 19
Centroid B: (np.int64(55), np.int64(0)), count: 48
Merging...
Centroid A: (np.int64(63), np.int64(17)), count: 305
Centroid B: (np.int64(37), np.int64(23)), count: 1651
Merging...
Centroid A: (np.int64(53), np.int64(36)), count: 42
Centroid B: (np.int64(37), np.int64(23)), count: 1956
Merging...
Centroid A: (np.int64(0), np.int64(36)), count: 37
Centroid B: (np.int64(0), np.int64(26)), count: 73
Merging...
Centroid A: (np.int64(17), np.int64(0)), count: 74
Centroid B: (np.int64(37), np.int64(23)), count: 1998
Merging...
Centroid A: (np.int64(19), np.int64(36)), count: 26
Centroid B: (np.int64(10), np.int64(35)), count: 22
Merging...
Number of unique centroids:  7
Minimum cost between centroids:  0.2846310205754125
Number of clusters : 7
Centroids:  [(np.int64(0), np.int64(26)), (np.int64(0), np.int64(10)), (np.int64(0), np.int64(0)), (np.int64(19), np.int64(36)), (np.int64(55), np.int64(0)), (np.int64(63), np.int64(36)), (np.int64(37), np.int64(23))]

Load simulation data to compare

You need two files for this step. First is the original hdf5 snapshot of the simulation, flds_5000.h5, second is the configuration file to read the snapshot, d3x128s10.ini. Put the .ini file inside aweSOM/examples/plasma-turbulence/, and the .h5 file inside a subfolder d3x128s10/.

Download the files from the Zenodo archive, then provide the path to the files with path.

[7]:
original_path = os.getcwd()
print('Current working directory:', original_path)
Current working directory: /mnt/home/tha10/git_repos/aweSOM/examples/plasma-turbulence
[8]:
import read_turbulence_data as rtd
conf = "d3x128s10.ini"
path = '/mnt/home/tha10/ceph/sim_snapshots/'# "/Users/tvh0021/Documents/Archive/"
filename = "flds_5000.h5"
lap = 5000

decay_turb = rtd.GetH5Data(path, filename, conf, lap)
# get the data
dx = 10./3.
decay_turb.return_basic_j_fields(dx)
decay_turb.return_all_other_fields()
/mnt/home/tha10/git_repos/aweSOM/examples/plasma-turbulence/initialize_turbulence.py:175: RuntimeWarning: divide by zero encountered in scalar divide
  A = 0.1 * self.g0 / self.gammarad**2  # definition we use in Runko
d3x128s10
plotting j_par
flds

Visualize the SOM result vs. a xy-slice of the \(j_{\parallel}\).

[9]:
bx = decay_turb.bx
by = decay_turb.by
j_par = decay_turb.j_par
j_par_rms = np.sqrt(np.mean(j_par**2))
nx, ny, nz = decay_turb.nx, decay_turb.ny, decay_turb.nz
som_labels_transformed = som_labels.reshape((nz, ny, nx))

plt.rcParams.update({'font.size': 16})

slice_numbers = [27]

for slice_number in slice_numbers:
    fig, ax = plt.subplots(2, 1, figsize=(10, 10), dpi=250, sharex=True)
    fig.subplots_adjust(hspace=0.1)

    jmag_plot = ax[0].pcolormesh(j_par[slice_number], cmap='RdBu', clim=[-1.5,1.5], rasterized=True)
    fig.colorbar(jmag_plot, ax=ax[0], fraction=0.042, pad=0.05, label=r'$j_{\parallel}$', ticks=[-1.5, -1, -0.5, 0, 0.5, 1, 1.5], location="right")
    # ax[0].contour(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, j_par[slice_number,:,:], levels=[-2.*j_par_rms,2.*j_par_rms], colors='green', linewidths=1, linestyles='solid')
    ax[0].streamplot(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, bx[slice_number], by[slice_number], color='black', linewidth=0.25, broken_streamlines=False, density=1., integration_direction='both', arrowstyle='-|>', arrowsize=0.6, )
    # fig.colorbar(eperp_plot, ax=ax[0], fraction=0.042, pad=0.04, label=r'$E_{\perp}$', ticks=[0, 0.1, 0.2, 0.3])
    ax[0].set_aspect('equal', adjustable='box')
    ax[0].grid()
    # ax[0].set_xticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    ax[0].set_yticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    # ax[0].set_xlabel(r'x ($c/\omega_p$)')
    ax[0].set_ylabel(r'y ($c/\omega_{\rm p}$)')
    # ax[0].set_title(f'z = {slice_number*dx:.0f} '+ r'$c/\omega_{\rm p}$')

    cluster_plot = ax[1].pcolormesh(som_labels_transformed[slice_number], cmap='tab10', clim=[0,10], rasterized=True)
    cbar = fig.colorbar(cluster_plot, ax=ax[1], fraction=0.042, pad=0.05, label='Cluster', ticks=np.arange(0, len(set(som_labels)))+0.5, boundaries=np.arange(0, len(set(som_labels))+1), location="right")
    cbar.ax.set_yticklabels(np.arange(0, len(set(som_labels))))
    ax[1].contour(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, j_par[slice_number,:,:], levels=[-2.*j_par_rms,2.*j_par_rms], colors=['red','blue'], linewidths=1, linestyles='solid')
    # ax[1].streamplot(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, bx[slice_number], by[slice_number], color='black', linewidth=0.25, broken_streamlines=False, density=1, integration_direction='both', arrowstyle='-|>', arrowsize=0.6, )
    ax[1].set_aspect('equal', adjustable='box')
    ax[1].grid()
    ax[1].set_xticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    ax[1].set_yticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    ax[1].set_xlabel(r'x ($c/\omega_{\rm p}$)')
    ax[1].set_ylabel(r'y ($c/\omega_{\rm p}$)')

    plt.show()
../_images/notebooks_plasma_19_0.png

Visualize the different centroids and nodes by mapping the nodes value back into feature space

Obtain unique centroids after merger

[10]:
naive_centroids = map.compute_centroids()
merged_centroids = map.merge_similar_centroids(naive_centroids, threshold=merge_cost)
unique_centroids = map.get_unique_centroids(merged_centroids)
Centroid A: (np.int64(39), np.int64(26)), count: 190
Centroid B: (np.int64(37), np.int64(23)), count: 376
Merging...
Centroid A: (np.int64(44), np.int64(35)), count: 40
Centroid B: (np.int64(39), np.int64(35)), count: 39
Merging...
Centroid A: (np.int64(25), np.int64(19)), count: 47
Centroid B: (np.int64(18), np.int64(18)), count: 400
Merging...
Centroid A: (np.int64(56), np.int64(18)), count: 100
Centroid B: (np.int64(63), np.int64(17)), count: 133
Merging...
Centroid A: (np.int64(35), np.int64(0)), count: 97
Centroid B: (np.int64(26), np.int64(0)), count: 55
Merging...
Centroid A: (np.int64(29), np.int64(10)), count: 259
Centroid B: (np.int64(35), np.int64(0)), count: 152
Merging...
Centroid A: (np.int64(49), np.int64(20)), count: 92
Centroid B: (np.int64(37), np.int64(23)), count: 566
Merging...
Centroid A: (np.int64(29), np.int64(10)), count: 411
Centroid B: (np.int64(37), np.int64(23)), count: 658
Merging...
Centroid A: (np.int64(44), np.int64(35)), count: 79
Centroid B: (np.int64(37), np.int64(23)), count: 1069
Merging...
Centroid A: (np.int64(18), np.int64(18)), count: 447
Centroid B: (np.int64(37), np.int64(23)), count: 1148
Merging...
Centroid A: (np.int64(48), np.int64(0)), count: 12
Centroid B: (np.int64(55), np.int64(0)), count: 36
Merging...
Centroid A: (np.int64(30), np.int64(35)), count: 56
Centroid B: (np.int64(37), np.int64(23)), count: 1595
Merging...
Centroid A: (np.int64(52), np.int64(10)), count: 72
Centroid B: (np.int64(63), np.int64(17)), count: 233
Merging...
Centroid A: (np.int64(0), np.int64(26)), count: 51
Centroid B: (np.int64(0), np.int64(19)), count: 22
Merging...
Centroid A: (np.int64(63), np.int64(0)), count: 19
Centroid B: (np.int64(55), np.int64(0)), count: 48
Merging...
Centroid A: (np.int64(63), np.int64(17)), count: 305
Centroid B: (np.int64(37), np.int64(23)), count: 1651
Merging...
Centroid A: (np.int64(53), np.int64(36)), count: 42
Centroid B: (np.int64(37), np.int64(23)), count: 1956
Merging...
Centroid A: (np.int64(0), np.int64(36)), count: 37
Centroid B: (np.int64(0), np.int64(26)), count: 73
Merging...
Centroid A: (np.int64(17), np.int64(0)), count: 74
Centroid B: (np.int64(37), np.int64(23)), count: 1998
Merging...
Centroid A: (np.int64(19), np.int64(36)), count: 26
Centroid B: (np.int64(10), np.int64(35)), count: 22
Merging...
Number of unique centroids:  7
Minimum cost between centroids:  0.2846310205754125

Get the 1d position of the centroids with respect to a list of nodes

[11]:
centroid_rowids = np.empty((len(unique_centroids['position_x']), 2), dtype=int)

for centroid_id in range(len(unique_centroids['position_x'])):
        row_values_of_centroids = [map.rowix(unique_centroids['position_x'][centroid_id], unique_centroids['position_y'][centroid_id])]
        centroid_rowids[centroid_id,:] = [centroid_id,row_values_of_centroids[0]]

Get the clusters associated with each node in the lattice

[12]:
clusters = map.assign_cluster_to_lattice(smoothing=None,merge_cost=merge_cost)
Centroid A: (np.int64(39), np.int64(26)), count: 190
Centroid B: (np.int64(37), np.int64(23)), count: 376
Merging...
Centroid A: (np.int64(44), np.int64(35)), count: 40
Centroid B: (np.int64(39), np.int64(35)), count: 39
Merging...
Centroid A: (np.int64(25), np.int64(19)), count: 47
Centroid B: (np.int64(18), np.int64(18)), count: 400
Merging...
Centroid A: (np.int64(56), np.int64(18)), count: 100
Centroid B: (np.int64(63), np.int64(17)), count: 133
Merging...
Centroid A: (np.int64(35), np.int64(0)), count: 97
Centroid B: (np.int64(26), np.int64(0)), count: 55
Merging...
Centroid A: (np.int64(29), np.int64(10)), count: 259
Centroid B: (np.int64(35), np.int64(0)), count: 152
Merging...
Centroid A: (np.int64(49), np.int64(20)), count: 92
Centroid B: (np.int64(37), np.int64(23)), count: 566
Merging...
Centroid A: (np.int64(29), np.int64(10)), count: 411
Centroid B: (np.int64(37), np.int64(23)), count: 658
Merging...
Centroid A: (np.int64(44), np.int64(35)), count: 79
Centroid B: (np.int64(37), np.int64(23)), count: 1069
Merging...
Centroid A: (np.int64(18), np.int64(18)), count: 447
Centroid B: (np.int64(37), np.int64(23)), count: 1148
Merging...
Centroid A: (np.int64(48), np.int64(0)), count: 12
Centroid B: (np.int64(55), np.int64(0)), count: 36
Merging...
Centroid A: (np.int64(30), np.int64(35)), count: 56
Centroid B: (np.int64(37), np.int64(23)), count: 1595
Merging...
Centroid A: (np.int64(52), np.int64(10)), count: 72
Centroid B: (np.int64(63), np.int64(17)), count: 233
Merging...
Centroid A: (np.int64(0), np.int64(26)), count: 51
Centroid B: (np.int64(0), np.int64(19)), count: 22
Merging...
Centroid A: (np.int64(63), np.int64(0)), count: 19
Centroid B: (np.int64(55), np.int64(0)), count: 48
Merging...
Centroid A: (np.int64(63), np.int64(17)), count: 305
Centroid B: (np.int64(37), np.int64(23)), count: 1651
Merging...
Centroid A: (np.int64(53), np.int64(36)), count: 42
Centroid B: (np.int64(37), np.int64(23)), count: 1956
Merging...
Centroid A: (np.int64(0), np.int64(36)), count: 37
Centroid B: (np.int64(0), np.int64(26)), count: 73
Merging...
Centroid A: (np.int64(17), np.int64(0)), count: 74
Centroid B: (np.int64(37), np.int64(23)), count: 1998
Merging...
Centroid A: (np.int64(19), np.int64(36)), count: 26
Centroid B: (np.int64(10), np.int64(35)), count: 22
Merging...
Number of unique centroids:  7
Minimum cost between centroids:  0.2846310205754125
Number of clusters : 7
Centroids:  [(np.int64(0), np.int64(26)), (np.int64(0), np.int64(10)), (np.int64(0), np.int64(0)), (np.int64(19), np.int64(36)), (np.int64(55), np.int64(0)), (np.int64(63), np.int64(36)), (np.int64(37), np.int64(23))]

Get the 1d position of the clusters with respect to a list of nodes

[13]:
clusters_rowix = np.empty((clusters.shape[0]*clusters.shape[1]), dtype=int)
for x in range(0, clusters.shape[0]):
    for y in range(0, clusters.shape[1]):
        clusters_rowix[map.rowix(x,y)] = clusters[x,y]

Plot the nodes on a 2d projection of normalized features

[14]:
plot_dimensions = (2,3) # 2d plot of the first two features
fig, ax = plt.subplots(1, 1, figsize=(10, 10), dpi=250)
fig.subplots_adjust(hspace=0.1)
# masked_labels = np.ma.masked_where(som_labels == 6, som_labels)
# scatter = ax.scatter(map.data_array[:, plot_dimensions[0]], map.data_array[:, plot_dimensions[1]],
#                      c=masked_labels, cmap='tab10', s=1, clim=[0, 10], rasterized=True, alpha=0.3)
ax.scatter(map.lattice[:, plot_dimensions[0]], map.lattice[:, plot_dimensions[1]], c=clusters_rowix, cmap="tab10", clim=[0,10], s=20, marker='o')
ax.scatter(map.lattice[centroid_rowids[:,1], plot_dimensions[0]], map.lattice[centroid_rowids[:,1], plot_dimensions[1]], c=centroid_rowids[:,0], cmap='tab10', clim=[0,10], s=80, edgecolors='black', linewidths=1, marker='*')
ax.set_xlabel(map.features_names[plot_dimensions[0]])
ax.set_ylabel(map.features_names[plot_dimensions[1]])
plt.show()
../_images/notebooks_plasma_30_0.png

Convert the lattice values from a 2D (X*Y, f) array to a 3D array (X, Y, f)

[15]:
def make_2d_lattice(this_map: Lattice, lattice: np.ndarray, xdim: int, ydim: int) -> np.ndarray:
    twod_lattice = np.empty((xdim, ydim, lattice.shape[1]))
    for x in range(xdim):
        for y in range(ydim):
            twod_lattice[x,y,:] = lattice[this_map.rowix(x,y)]
    return twod_lattice
[20]:
steps = np.linspace(0, lattice_history.shape[0] * map.save_frequency, lattice_history.shape[0], endpoint=False, dtype=int)
dict_lookup = {"j_mag" : r"$j$", "j_par" : r"$j_{\parallel}$", "j_perp" : r"$j_{\perp}$", "j_sym" : r"$j_{\rm s}$", "j_asym" : r"$j_{\rm a}$", "bz" : r"$b_z$", "b_perp" : r"$b_{\perp}$", "e_par" : r"$e_{\parallel}$", "e_perp" : r"$e_{\perp}$"}


num_plots = 9
fig, ax = plt.subplots(3, 3, figsize=(20, 20), sharex=True, sharey=True)
i = 0
for k in range(0, umat_history.shape[0], len(steps)//num_plots+1):

    x,y = i // 3, i % 3
    i += 1

    twod_lattice = make_2d_lattice(map, map.lattice_history[k], map.xdim, map.ydim)

    plot_dimensions = (0,1) # 2d plot of the first two features

    for row in range(map.ydim):
        ax[x,y].plot(twod_lattice[:,row,plot_dimensions[0]], twod_lattice[:,row,plot_dimensions[1]], color='k', marker=None, linewidth=0.5)

    for column in range(map.xdim):
        ax[x,y].plot(twod_lattice[column,:,plot_dimensions[0]], twod_lattice[column,:,plot_dimensions[1]], color='k', marker='o', markerfacecolor='r', markersize=2, linewidth=0.5)

    # ax[x,y].text(0.9, 0.97, f'Epoch = {steps[k]}', horizontalalignment='center', verticalalignment='center', transform=ax[x,y].transAxes, fontsize=10)
    ax[x,y].set_title(f'Epoch = {steps[k]}')
    # ax.scatter(map.lattice[:, plot_dimensions[0]], map.lattice[:, plot_dimensions[1]], c=clusters_rowix, cmap="tab10", clim=[0,10], s=20, marker='o')
    # ax.scatter(map.lattice[centroid_rowids[:,1], plot_dimensions[0]], map.lattice[centroid_rowids[:,1], plot_dimensions[1]], c=centroid_rowids[:,0], cmap='tab10', clim=[0,10], s=80, edgecolors='black', linewidths=1, marker='*')
    if x == 2:
        ax[x,y].set_xlabel(dict_lookup[map.features_names[plot_dimensions[0]]])
    if y == 0:
        ax[x,y].set_ylabel(dict_lookup[map.features_names[plot_dimensions[1]]])
    # plt.show()
../_images/notebooks_plasma_33_0.png

Triangle plots

[ ]:
import matplotlib.cm as cm
import matplotlib.patches as mpatches

colors_x = cm.plasma(np.linspace(0, 1, map.ydim))
colors_y = cm.copper(np.linspace(0, 1, map.xdim))

n_sample = 100000
sample_choice = np.random.choice(map.data_array.shape[0], n_sample, replace=False)
sample_data = map.data_array[sample_choice]
sample_label = som_labels[sample_choice]

exclude_cluster = 6
sample_data = sample_data[sample_label != exclude_cluster]
sample_label = sample_label[sample_label != exclude_cluster]


print("visualizing manual thr data")
fig2 = plt.figure(2, figsize=(20,20), dpi=300)
fig2.clear()

ic = 0
iplots = 0
nfea = len(map.features_names)
for jcomp in range(nfea):
        for icomp in range(nfea):
                ic += 1
                # print("i {} j {}".format(icomp, jcomp))

                #skip covariance with itself
                if icomp == jcomp:
                        continue

                #skip upper triangle
                if icomp > jcomp:
                        continue

                iplots += 1

                ax = fig2.add_subplot(nfea, nfea, ic)


                for row in range(map.ydim):
                        if row == 0 or row == map.ydim-1:
                                ax.plot(twod_lattice[:,row,icomp], twod_lattice[:,row,jcomp], color='k', marker=None, linewidth=1.5, zorder=2.5)
                        else:
                                ax.plot(twod_lattice[:,row,icomp], twod_lattice[:,row,jcomp], color='grey', marker=None, linewidth=0.5, zorder=2)

                # for column in range(map.xdim):
                #         if column == 0 or column == map.xdim-1 or column == map.xdim//2:
                #                 ax.plot(twod_lattice[column,:,icomp], twod_lattice[column,:,jcomp], color=colors_y[column], linestyle='-.', marker=None, linewidth=1)

                scatter = ax.scatter(map.lattice[:,icomp], map.lattice[:,jcomp], c=clusters_rowix, cmap="tab10", clim=[0,10], s=3, marker='s')
                ax.scatter(map.lattice[centroid_rowids[:,1], icomp], map.lattice[centroid_rowids[:,1], jcomp], c=centroid_rowids[:,0], cmap='tab10', clim=[0,10], s=50, edgecolors='black', linewidths=1, marker='*', zorder=3)

                ax.scatter(sample_data[:,icomp], sample_data[:,jcomp], c=sample_label, cmap="tab10", s=2, marker='o', alpha=0.3, clim=[0,10], edgecolors='none')
                ax.set_xlim(-2.5,2.5)
                ax.set_ylim(-2.5,2.5)
                ax.set_xticks([-2,-1,0,1,2])
                # ax.set_xticklabels([-2,-1,0,1,2])
                # ax.grid()

                ax.axhline(0, color='black', linestyle='--', linewidth=0.5)
                ax.axvline(0, color='black', linestyle='--', linewidth=0.5)

                if jcomp == nfea-1:
                        ax.set_xlabel('{}'.format(dict_lookup[map.features_names[icomp]]))
                else:
                        ax.set_xticks([])

                if icomp == 0:
                        ax.set_ylabel('{}'.format(dict_lookup[map.features_names[jcomp]]))
                else:
                        ax.set_yticks([])

fig2.suptitle(f"Triangle plots of SOM projections", x=0.4, y=0.7, fontsize=20)

# fig2.text(0.7, 0.7, '* : cluster centroids', ha='right', va='top', fontsize=12, color='black', transform=fig2.transFigure)


fig2.subplots_adjust(right=0.88)
cbar_ax = fig2.add_axes([0.7, 0.1, 0.02, 0.6])
cbar = fig2.colorbar(scatter, cax=cbar_ax, ticks=np.arange(0, len(set(som_labels)))+0.5, boundaries=np.arange(0, len(set(som_labels))+1))
cbar.ax.set_yticklabels(np.arange(0, len(set(som_labels))))
cbar.set_label("Cluster ID")


# fig2.savefig('/mnt/home/tha10/jupyter-notebooks/som_triangle.pdf')
visualizing manual thr data
../_images/notebooks_plasma_35_1.png

Stacking multiple aweSOM realizations for SCE analysis

To run a set of SOM realizations, use the examples/plasma-turbulence/run_plasma_som.py script with multiple values for each argument. run_plasma_som.py uses the multiprocessing package, so the more cores available, the faster it should run.

You will also need the file features_2j1b1e0r_5000_jasym.h5 from the Zenodo archive.

cd examples/plasma-turbulence
python run_plasma_som.py --ratio 0.6 0.8 1. --alpha_0 0.1 0.2 0.4 --train 1000000 2097152 4194304

Refer to examples/slurm_scripts/submit_plasma.slurm for an example of the SLURM submit script to run the parameter scan on a set of aweSOM realizations.

The result of this run is a list of files with names labels.* and som_object.*. The labels are final clustering results, while the objects are pickle files of the entire trained lattice that can be modified. Move all labels.* files into a subfolder named som_results.

Alternatively, you can use a set of pre-trained aweSOM realizations, which can be retrieved from the Zenodo archive, under plasma-som-realizations.tar. Unpack the files inside aweSOM/examples/plasma-turbulence/som_results/.

[ ]:
original_path = os.getcwd()
print('Current working directory:', original_path)
Current working directory: /mnt/home/tha10/git_repos/aweSOM/examples/plasma-turbulence
[ ]:
import read_turbulence_data as rtd
conf = "d3x128s10.ini"
path = '/mnt/home/tha10/ceph/sim_snapshots/'# "/Users/tvh0021/Documents/Archive/"
filename = "flds_5000.h5"
lap = 5000

decay_turb = rtd.GetH5Data(path, filename, conf, lap)
# get the data
dx = 10./3.
decay_turb.return_basic_j_fields(dx)
decay_turb.return_all_other_fields()
d3x128s10
plotting j_par
flds
/mnt/home/tha10/git_repos/aweSOM/examples/plasma-turbulence/initialize_turbulence.py:175: RuntimeWarning: divide by zero encountered in scalar divide
  A = 0.1 * self.g0 / self.gammarad**2  # definition we use in Runko

To run the SCE clustering, import cuda to use GPU acceleration. GPU is not necessary for the Iris dataset but is absolutely necessary here (\(N_{\rm iris} = 150\), \(N_{\rm plasma} = 128^3\)).

Run the SCE \(G_{\rm sum}\) calculation with:

cd som_results
python [path_to_aweSOM]/aweSOM/src/aweSOM/sce.py --subfolder SCE --dims 2097152

Make sure that the first line of output is Using JAX for GPU computation.

Refer to examples/slurm_scripts/submit.gpu for an example of the SLURM submit script to run the SCE analysis on a set of aweSOM realizations.

Using one NVIDIA V-100 GPU, this should not take more than 3 minutes to complete. All cluster masks and multimap_mappings.txt are saved in SCE/

Now visualize \(G_{\rm sum}\)

[ ]:
os.chdir(original_path) # recover the original path

file_path = 'som_results/SCE/'
file_name = 'multimap_mappings.txt'

from aweSOM.make_sce_clusters import get_gsum_values, plot_gsum_values

ranked_gsum_list, map_list = get_gsum_values(file_path+file_name)

plot_gsum_values(ranked_gsum_list)
../_images/notebooks_plasma_43_0.png

In the simplest case, add all gsum values together to obtain final SCE clustering result

[ ]:
number_of_points = int(128**3)

sce_sum = np.zeros((number_of_points))

for i in range(len(ranked_gsum_list)):
    current_cluster_mask = np.load(f"{file_path}/mask-{map_list[i][2]}-id{map_list[i][1]}.npy")
    sce_sum += current_cluster_mask

Visualize the SCE stacking result

[ ]:
sce_sum_transformed = sce_sum.reshape((128,128,128))

slice_numbers = [27]

for slice_number in slice_numbers:
    fig, ax = plt.subplots(2, 1, figsize=(10, 10), dpi=250, sharex=True)
    fig.subplots_adjust(hspace=0.1)

    jmag_plot = ax[0].pcolormesh(j_par[slice_number], cmap='RdBu', clim=[-1.5,1.5], rasterized=True)
    fig.colorbar(jmag_plot, ax=ax[0], fraction=0.042, pad=0.05, label=r'$j_{\parallel}$', ticks=[-1.5, -1, -0.5, 0, 0.5, 1, 1.5], location="right")
    # ax[0].contour(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, j_par[slice_number,:,:], levels=[-2.*j_par_rms,2.*j_par_rms], colors='green', linewidths=1, linestyles='solid')
    ax[0].streamplot(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, bx[slice_number], by[slice_number], color='black', linewidth=0.25, broken_streamlines=False, density=1., integration_direction='both', arrowstyle='-|>', arrowsize=0.6, )
    # fig.colorbar(eperp_plot, ax=ax[0], fraction=0.042, pad=0.04, label=r'$E_{\perp}$', ticks=[0, 0.1, 0.2, 0.3])
    ax[0].set_aspect('equal', adjustable='box')
    ax[0].grid()
    # ax[0].set_xticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    ax[0].set_yticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    # ax[0].set_xlabel(r'x ($c/\omega_p$)')
    ax[0].set_ylabel(r'y ($c/\omega_{\rm p}$)')
    # ax[0].set_title(f'z = {slice_number*dx:.0f} '+ r'$c/\omega_{\rm p}$')

    cluster_plot = ax[1].pcolormesh(sce_sum_transformed[slice_number], cmap='Greys_r', rasterized=True)
    cbar = fig.colorbar(cluster_plot, ax=ax[1], fraction=0.042, pad=0.05, label='SCE signal strength', location="right")
    # cbar.ax.set_yticklabels(np.arange(0, len(set(som_labels))))
    ax[1].contour(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, j_par[slice_number,:,:], levels=[-2.*j_par_rms,2.*j_par_rms], colors=['red','blue'], linewidths=1, linestyles='solid')
    # ax[1].streamplot(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, bx[slice_number], by[slice_number], color='black', linewidth=0.25, broken_streamlines=False, density=1, integration_direction='both', arrowstyle='-|>', arrowsize=0.6, )
    ax[1].set_aspect('equal', adjustable='box')
    ax[1].grid()
    ax[1].set_xticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    ax[1].set_yticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    ax[1].set_xlabel(r'x ($c/\omega_{\rm p}$)')
    ax[1].set_ylabel(r'y ($c/\omega_{\rm p}$)')

    plt.show()
../_images/notebooks_plasma_47_0.png

Let’s visualize the distribution of these SCE signal strengths

[ ]:
plt.rcParams.update({'font.size': 14})
plt.figure(dpi=200)
plt.hist(sce_sum, bins=20, color='r', label='SCE Cluster Sum', histtype='step', linewidth=2)
plt.xlabel('SCE Cluster Sum')
plt.ylabel('Frequency')
plt.yscale('log')
plt.show()
../_images/notebooks_plasma_49_0.png

Set a cutoff

[ ]:
signal_cutoff = [3500, 6000, 10000]

sce_clusters = np.zeros(number_of_points, dtype=int)
for i in range(len(sce_sum)):
    if sce_sum[i] < signal_cutoff[0]:
        sce_clusters[i] = 0
    elif sce_sum[i] < signal_cutoff[1]:
        sce_clusters[i] = 1
    elif sce_sum[i] < signal_cutoff[2]:
        sce_clusters[i] = 2
    else:
        sce_clusters[i] = 3
[ ]:
sce_clusters_transformed = sce_clusters.reshape((128,128,128))

plt.rcParams.update({'font.size': 16})

slice_numbers = [27]

for slice_number in slice_numbers:
    fig, ax = plt.subplots(2, 1, figsize=(10, 10), dpi=250, sharex=True)
    fig.subplots_adjust(hspace=0.1)

    jmag_plot = ax[0].pcolormesh(j_par[slice_number], cmap='RdBu', clim=[-1.5,1.5], rasterized=True)
    fig.colorbar(jmag_plot, ax=ax[0], fraction=0.042, pad=0.05, label=r'$j_{\parallel}$', ticks=[-1.5, -1, -0.5, 0, 0.5, 1, 1.5], location="right")
    # ax[0].contour(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, j_par[slice_number,:,:], levels=[-2.*j_par_rms,2.*j_par_rms], colors='green', linewidths=1, linestyles='solid')
    ax[0].streamplot(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, bx[slice_number], by[slice_number], color='black', linewidth=0.25, broken_streamlines=False, density=1., integration_direction='both', arrowstyle='-|>', arrowsize=0.6, )
    # fig.colorbar(eperp_plot, ax=ax[0], fraction=0.042, pad=0.04, label=r'$E_{\perp}$', ticks=[0, 0.1, 0.2, 0.3])
    ax[0].set_aspect('equal', adjustable='box')
    ax[0].grid()
    # ax[0].set_xticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    ax[0].set_yticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    # ax[0].set_xlabel(r'x ($c/\omega_p$)')
    ax[0].set_ylabel(r'y ($c/\omega_{\rm p}$)')
    # ax[0].set_title(f'z = {slice_number*dx:.0f} '+ r'$c/\omega_{\rm p}$')

    cluster_plot = ax[1].pcolormesh(sce_clusters_transformed[slice_number], cmap='tab10', clim=[0,10], rasterized=True)
    cbar = fig.colorbar(cluster_plot, ax=ax[1], fraction=0.042, pad=0.05, label='Cluster', ticks=np.arange(0, len(signal_cutoff)+1)+0.5, boundaries=np.arange(0, len(signal_cutoff)+2), location="right")
    cbar.ax.set_yticklabels(np.arange(0, len(signal_cutoff)+1))
    # ax[1].contour(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, j_par[slice_number,:,:], levels=[-2.*j_par_rms,2.*j_par_rms], colors=['red','blue'], linewidths=1, linestyles='solid')
    # ax[1].streamplot(np.arange(0, nx)+0.5, np.arange(0, ny)+0.5, bx[slice_number], by[slice_number], color='black', linewidth=0.25, broken_streamlines=False, density=1, integration_direction='both', arrowstyle='-|>', arrowsize=0.6, )
    ax[1].set_aspect('equal', adjustable='box')
    ax[1].grid()
    ax[1].set_xticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    ax[1].set_yticks(np.arange(0, 128, 30), np.arange(0, 128*dx, 30*dx, dtype=int))
    ax[1].set_xlabel(r'x ($c/\omega_{\rm p}$)')
    ax[1].set_ylabel(r'y ($c/\omega_{\rm p}$)')

    plt.show()
../_images/notebooks_plasma_52_0.png

Optional - Generate different sets of features

Under Construction