{ "cells": [ { "cell_type": "markdown", "metadata": { "golem": {} }, "source": [ "# Double rake probe spotlight\n", "\n", "The following scripts introduces basic aspects of the double rake probe measurements.\n", "\n", "\n", "## The double rake probe diagnostic\n", "\n", "This is the double rake probe:\n", "\n", "\n", "\n", "Its wiki page is located [here](http://golem.fjfi.cvut.cz/wiki/Diagnostics/ParticleFlux/DoubleRakeProbe/index). The double rake probe has two rows of pins (\"rakes\"), out of which the first 6 in each row are connected to the data acquisition system. The pin signals are called `DRP-R1` to `DRP-R6` and `DRP-L1` to `DRP-L6`.\n", "\n", "Like any Langmuir probe, the rake probe pins can be electrically insulated (measuring the floating potential $V_f$), biased to a negative voltage -100 V (measuring the ion saturated current $I_{sat}$), their biasing voltage may be swept (measuring $V_f$, $I_{sat}$ *and* the electron temperature $T_e$ if the $I$-$V$ characteristic is fitted with an exponential function) or they can perform other measurements. During this campaign, all the pins measure the **ion saturated current $I_{sat}$**. This can be processed to gain information about the plasma turbulent transport.\n", "\n", "\n", "## Import basic libraries\n", "\n", "To visualise and process the double rake probe data, first we import basic libraries, Numpy and Matplotlib. The line `%matplotlib notebook` enables the drawn figures to be interactive." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import numpy as np\n", "import matplotlib.pyplot as pl\n", "\n", "#Number of the test discharge; 0 means the last one\n", "shot_no = 36353 #35041 #test discharge\n", "shot = shot_no" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Load double rake probe data\n", "\n", "The data directory of the double rake probe is `http://golem.fjfi.cvut.cz/shots/{shot}/Diagnostics/DoubleRakeProbe/`, where `{shot}` stands for the discharge number. Note that in different sessions, the data may be saved elsewhere and it might be needed to update the `URL` variable in the following text. The data directory may be located from the individual shot homepage, and tends to be the same within a single session.\n", "\n", "In the following, we write a function to download the rake probe data." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from urllib.error import HTTPError # recognise the error stemming from missing data\n", "import pandas as pd # for reading csv files\n", "\n", "#Define an exception which will be raised if the data is missing and stop the notebook execution\n", "class StopExecution(Exception):\n", " def _render_traceback_(self):\n", " pass\n", "\n", "def get_data(shot, identifier):\n", " URL = \"http://golem.fjfi.cvut.cz/shots/{shot}/Diagnostics/DoubleRakeProbe/{identifier}.csv\"\n", " url = URL.format(shot=shot, identifier=identifier)\n", " try:\n", " df = pd.read_csv(url, names=['time', identifier], index_col='time')\n", " except HTTPError:\n", " print('File not found at %s . Aborting notebook execution.' % url)\n", " raise StopExecution\n", " t = np.array(df.index)\n", " data = np.transpose(np.array(df))[0]\n", " return t, data/46.7" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that in the last line, the data values are divided by 46.7. This is the constant which converts the raw DAS signal to the ion saturated current in A. Its physical meaning is the resistance of the measuring resistor in the \"silver box\".\n", "\n", "## Load the discharge beginning and end\n", "\n", "Prior to plotting the double rake probe data, we'll write a little function which loads the time of the discharge beginning (plasma formation) and end (recombination). We'll use it to set the X axis limits later. Notice that `t1` and `t2` are in ms." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from urllib.request import urlopen\n", "\n", "def get_file(url, shot, silent=False):\n", " URL = 'http://golem.fjfi.cvut.cz/shots/%i/%s' % (shot, url)\n", " if not silent:\n", " print(URL)\n", " f = urlopen(URL)\n", " try:\n", " return np.loadtxt(f)\n", " except ValueError: # the data couldn't be converted to a row of numbers\n", " return np.array([np.nan])\n", "\n", "t1 = float(get_file('Diagnostics/BasicDiagnostics/Results/t_plasma_start', shot))\n", "t2 = float(get_file('Diagnostics/BasicDiagnostics/Results/t_plasma_end', shot))\n", "print('Discharge %i: %.1f ms - %.1f ms' % (shot, t1, t2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plot all the signals from the double rake probe pins\n", "\n", "Next, we load the double rake probe data for the current discharge and plot them." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Load the signals of all the rake probe pins\n", "DRP_R = []\n", "DRP_L = []\n", "for i in range(1, 7):\n", " t, data = get_data(shot, 'DRP-R%i' % i)\n", " DRP_R.append(data)\n", "for i in range(1, 7):\n", " t, data = get_data(shot, 'DRP-L%i' % i)\n", " DRP_L.append(data)\n", "DRP_R = np.array(DRP_R)\n", "DRP_L = np.array(DRP_L)\n", "\n", "#Plot the rake probe pin signals\n", "fig, axs = pl.subplots(6, 2, num=('All rake probe signals in the current discharge'), figsize=(9,9),\n", " sharex=True, sharey=True)\n", "for i in range(0, 6):\n", " axs[i, 0].plot(t*1000, DRP_L[i]*1000, label='DRP-L%i' % (i+1))\n", "for i in range(0, 6):\n", " axs[i, 1].plot(t*1000, DRP_R[i]*1000, label='DRP-R%i' % (i+1))\n", "fig.tight_layout()\n", "fig.subplots_adjust(hspace=0, wspace=0.3)\n", "for i in range(6):\n", " for j in range(2):\n", " axs[i,j].legend(loc=2)\n", " axs[i,j].grid(True)\n", " axs[i,j].set_xlim(t1-3, t2+3)\n", " axs[i,j].set_ylim(-0.5, 4)\n", " axs[i,j].axhline(c='k')\n", " axs[i,j].set_ylabel('$I_{sat}$ [mA]')\n", "pl.savefig(\"icon-fig.png\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice how the signal wanes on the pins with a higher number. This is because they are not as deep in the plasma column and they are surrounded by plasma of lower temperature and density, hence lower $I_{sat}$.\n", "\n", "## Load the rake probe position\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#R_probe = 70 #mm\n", "R_probe = get_file('Diagnostics/DoubleRakeProbe/Parameters/r_first_tip', shot)\n", "print('R_first_pin = %i mm' % R_probe)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plot histogram of the $I_{sat}$ fluctuations\n", "\n", "The basic method of investigating the presence of turbulent structures in the edge plasma is plotting the $I_{sat}$ histogram. A histogram is an approximation of the distribution function - the probability of a certain value occurring in the signal. It is plotted by the function `pl.hist()`.\n", "\n", "In the following, we plot the histogram of the $I_{sat}$ fluctuations in the test discharge `shot`. The fluctuations are sampled during a 1ms window from 8 ms to 9 ms (approx. middle of the discharge)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Choose data of the sample\n", "data = DRP_R[0] #deepest pin in the R row\n", "sample = data[(0.008 < t) & (t < 0.009)]\n", "\n", "#Plot the histogram\n", "fig = pl.figure('Histogram of I_sat fluctuations', figsize=(6,4))\n", "fig.tight_layout()\n", "pl.hist(sample*1000, bins=30)\n", "pl.xlabel('$I_{sat}$ [mA]')\n", "pl.ylabel('value count')\n", "\n", "#Plot the mean with a thick black line\n", "pl.axvline(sample.mean(), c='k', lw=2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the histogram might be asymmetrical.\n", "\n", "\n", "## Calculate the distribution skewness\n", "\n", "Skewness $S$ is the third statistical moment of a distribution function, the first two being the mean and the standard deviation. If the distribution is symmetric, its skewness is zero. For instance, the skewness of the normal (Gaussian) distribution is zero. The skewness of the $I_{sat}$ fluctuations in this case, however, isn't zero and the distribution is not normal. It has an abundance of large positive events (blobs) while lacking the same number of large negative events. This is evidence of the presence of blobs in the SOL.\n", "\n", "In the following, we calculate the skewness using the function `scipy.stats.skew`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from scipy.stats import skew\n", "\n", "S = skew(sample)\n", "print('The sample skewness is %.2f.' % S)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plot the skewness profile from all probes\n", "\n", "In the following, we use all the probe pins and plot the profile of the $I_{sat}$ fluctuations skewness. The distance between the pins is 2.5 mm." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Prepare the variables\n", "S = []\n", "T = np.arange(np.ceil(t1+0.5), t2-0.5) #windows centres, spaced by 1 ms\n", "pins = ['L'+str(i) for i in range(1,7)] + ['R'+str(i) for i in range(1,7)]\n", "r = [0, 2.5, 5, 7.5, 10, 12.5] * 2\n", "r = np.array(r) + R_probe #mm\n", "colour_indices = np.linspace(0, 1, T.size)\n", "\n", "#Iterate over the pins and time windows and calculate skewness\n", "for pin in pins:\n", " t, DRP = get_data(shot, 'DRP-'+pin)\n", " t *= 1000 #ms\n", " s = []\n", " for tau in T:\n", " sample = DRP[(tau-0.5 < t) & (t < tau+0.5)]\n", " s.append(skew(sample))\n", " S.append(s)\n", "S = np.array(S)\n", " \n", "#Plot the skewness profile\n", "fig = pl.figure('Skewness profile', figsize=(8,6))\n", "for i in range(T.size):\n", " pl.plot(r[:6], S[:6,i], marker='o', color=pl.cm.copper_r(colour_indices[i]),\n", " lw=0.5, label='%i ms' % T[i])\n", " pl.plot(r[6:], S[6:,i], marker='o', color=pl.cm.copper_r(colour_indices[i]), lw=0.5)\n", "pl.xlabel('$r$ [mm]')\n", "pl.ylabel('$S$')\n", "pl.axhline(0, c='k')\n", "pl.legend()\n", "pl.grid(True)" ] } ], "metadata": { "celltoolbar": "Edit Metadata", "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 4 }