From 4dd8d672bb5f20da6b20e5d38ff535a3efc0e6e9 Mon Sep 17 00:00:00 2001 From: Anchal Gupta Date: Mon, 13 Nov 2023 14:35:05 -0500 Subject: [PATCH] Plotting recipes for interferometer data Added recipe to plot interferometer geometry on top of SOLPS mesh in the R-Z plane. Added recipes for plotting integrated and averaged electron density data from interferometer as time series. Added example file to see how to use these functions. --- examples/plotting_interferometer.ipynb | 222 ++++++++++++++++++ samples/.gitignore | 1 + ...edge_profiles_with_interferometer.json.dvc | 12 + src/recipes.jl | 149 ++++++++++++ 4 files changed, 384 insertions(+) create mode 100644 examples/plotting_interferometer.ipynb create mode 100644 samples/time_dep_edge_profiles_with_interferometer.json.dvc diff --git a/examples/plotting_interferometer.ipynb b/examples/plotting_interferometer.ipynb new file mode 100644 index 0000000..917d0a5 --- /dev/null +++ b/examples/plotting_interferometer.ipynb @@ -0,0 +1,222 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plotting interferometer using GGDUtils\n", + " \n", + " For running this notebook, you need to install package IJulia in your home environment (that is messy, but that is the only way I know right now). So in your terminal:\n", + " ```\n", + " % julia\n", + " julia > ]\n", + " (@v1.9 pkg) pkg> add IJulia\n", + " ```\n", + "\n", + " After this, Julia kernel would appear in your jupyter notebooks as an option. This also works for julia notebooks directly opened on VSCode. Select the Julia kernel to run this notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "using Pkg\n", + "Pkg.activate(\"./\")\n", + "Pkg.add(url=\"git@github.com:ProjectTorreyPines/OMAS.jl.git\")\n", + "Pkg.develop(path=\"../\")\n", + "Pkg.add(\"Plots\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "using OMAS\n", + "using GGDUtils\n", + "using Plots" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load the interferometer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ids = OMAS.json2imas(\"$(@__DIR__)/../samples/time_dep_edge_profiles_with_interferometer.json\")\n", + "grid_ggd = ids.edge_profiles.grid_ggd[1]\n", + "space = grid_ggd.space[1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plotting the interferometer geometry on top of SOLPS mesh" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Choose backend\n", + "gr() # Fast and can save pdf\n", + "# plotlyjs() # Use for interactive plot, can only save png\n", + "\n", + "plot(space)\n", + "plot!(ids.interferometer) # Default plot_type is :los \n", + "plot!(legend=true)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can provide custom lengthand thickness of mirror to be plotted and linewidth of the laser beams" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Choose backend\n", + "gr() # Fast and can save pdf\n", + "# plotlyjs() # Use for interactive plot, can only save png\n", + "\n", + "plot(space)\n", + "plot!(ids.interferometer, mirror_length=0.7, linewidth=4, mirror_thickness=0.2)\n", + "plot!(legend=true)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or you can choose to omit the mirror" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Choose backend\n", + "gr() # Fast and can save pdf\n", + "# plotlyjs() # Use for interactive plot, can only save png\n", + "\n", + "plot(space)\n", + "plot!(ids.interferometer, mirror=false)\n", + "plot!(legend=true)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can plot a single channel as well. You can override the in-built channel name for the label." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Choose backend\n", + "gr() # Fast and can save pdf\n", + "# plotlyjs() # Use for interactive plot, can only save png\n", + "\n", + "plot(space)\n", + "plot!(ids.interferometer.channel[1], label=\"Channel 1\")\n", + "plot!(legend=true)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plotting interferometer data vs time\n", + "\n", + " * Use plot_type=:n_e for integrated electron density data\n", + " * Use plot_type=:n_e_average for averaged electron density data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Choose backend\n", + "gr() # Fast and can save pdf\n", + "# plotlyjs() # Use for interactive plot, can only save png\n", + "\n", + "plot(ids.interferometer, plot_type=:n_e)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Choose backend\n", + "gr() # Fast and can save pdf\n", + "# plotlyjs() # Use for interactive plot, can only save png\n", + "\n", + "plot(ids.interferometer, plot_type=:n_e_average)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Again, to plot an individual channel, just provide the channel with correct plot_type" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Choose backend\n", + "gr() # Fast and can save pdf\n", + "# plotlyjs() # Use for interactive plot, can only save png\n", + "\n", + "plot(ids.interferometer.channel[1], plot_type=:n_e_average)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.9.2", + "language": "julia", + "name": "julia-1.9" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.9.2" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/samples/.gitignore b/samples/.gitignore index c4e9f4a..d203298 100644 --- a/samples/.gitignore +++ b/samples/.gitignore @@ -1 +1,2 @@ /time_dep_edge_profiles_last_step_only.json +/time_dep_edge_profiles_with_interferometer.json diff --git a/samples/time_dep_edge_profiles_with_interferometer.json.dvc b/samples/time_dep_edge_profiles_with_interferometer.json.dvc new file mode 100644 index 0000000..c45c30b --- /dev/null +++ b/samples/time_dep_edge_profiles_with_interferometer.json.dvc @@ -0,0 +1,12 @@ +md5: fb9792d06bb5d13f3a8803654b78624c +frozen: true +deps: +- path: ITER_Lore_2296_00000/IMAS/time_dep_edge_profiles_with_interferometer.json + repo: + url: git@github.com:ProjectTorreyPines/SOLPSTestSamples.git + rev_lock: 80688afd131b17455c3320a4485bbf2b7a55c80c +outs: +- md5: 610272ceabe4a62dd776e0f3c08d2036 + size: 40662828 + hash: md5 + path: time_dep_edge_profiles_with_interferometer.json diff --git a/src/recipes.jl b/src/recipes.jl index 37205be..3e4ef79 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -1,5 +1,6 @@ using RecipesBase using ColorSchemes: ColorSchemes +import Statistics: norm, dot @recipe function f(space::OMAS.edge_profiles__grid_ggd___space) nodes = space.objects_per_dimension[1].object @@ -175,3 +176,151 @@ end ) end end + +@recipe f( + ifo::OMAS.interferometer, +) = + for ch ∈ ifo.channel + @series begin + return ch + end + end + +@recipe function f( + ifo_ch::OMAS.interferometer__channel, +) + if :plot_type ∈ keys(plotattributes) + plot_type = plotattributes[:plot_type] + else + plot_type = :los + end + if plot_type == :los + @series begin + label --> ifo_ch.name + ifo_ch.line_of_sight + end + elseif plot_type == :n_e || plot_type == :n_e_line + @series begin + label --> ifo_ch.name + ifo_ch.n_e_line + end + elseif plot_type == :n_e_average || plot_type == :n_e_line_average + @series begin + label --> ifo_ch.name + ifo_ch.n_e_line_average + end + else + error("Invalid plot type. Choose between los (line of sight, R-Z plane), ", + "n_e (integrated n_e along line of sight vs time), ", + "and n_e_average (average n_e vs time)") + end +end + +@recipe function f( + ifo_ch_los::OMAS.interferometer__channel___line_of_sight, +) + size --> [600, 900] + xaxis --> "R / m" + yaxis --> "Z / m" + fp = ifo_ch_los.first_point + sp = ifo_ch_los.second_point + tp = ifo_ch_los.third_point + if tp == OMAS.interferometer__channel___line_of_sight__third_point() + tp = fp + end + + if :mirror ∈ keys(plotattributes) + mirror = plotattributes[:mirror] + else + mirror = true + end + if mirror + # Calculate mirror points + fsuv = Array([sp.r - fp.r, sp.z - fp.z]) + stuv = Array([tp.r - sp.r, tp.z - sp.z]) + fsuv = fsuv / norm(fsuv) + stuv = stuv / norm(stuv) + mirror_uv = fsuv + stuv + if mirror_uv[1] == 0 && mirror_uv[2] == 0 + mirror_uv = [fsuv[2], -fsuv[1]] + end + mirror_uv = mirror_uv / norm(mirror_uv) + if :mirror_length ∈ keys(plotattributes) + mirror_length = plotattributes[:mirror_length] + else + mirror_length = 0.5 + end + mirror_perp = ([-mirror_uv[2], mirror_uv[1]]) + mirror_perp = mirror_perp * sign(dot(mirror_perp, fsuv)) + if :mirror_thickness ∈ keys(plotattributes) + mirror_thickness = plotattributes[:mirror_thickness] + else + mirror_thickness = 0.1 + end + mirror_r1 = sp.r - mirror_uv[1] * mirror_length / 2 + mirror_r2 = sp.r + mirror_uv[1] * mirror_length / 2 + mirror_r3 = mirror_r2 + mirror_perp[1] * mirror_thickness + mirror_r4 = mirror_r1 + mirror_perp[1] * mirror_thickness + mirror_z1 = sp.z - mirror_uv[2] * mirror_length / 2 + mirror_z2 = sp.z + mirror_uv[2] * mirror_length / 2 + mirror_z3 = mirror_z2 + mirror_perp[2] * mirror_thickness + mirror_z4 = mirror_z1 + mirror_perp[2] * mirror_thickness + end + + # Draw line of sight + @series begin + seriestype := :path + linewidth --> 2 + [fp.r, sp.r, tp.r], [fp.z, sp.z, tp.z] + end + + if mirror + # Draw mirror + @series begin + seriestype := :path + label := "" + linecolor := :gray + linewidth := 2 + [mirror_r1, mirror_r2], [mirror_z1, mirror_z2] + end + @series begin + seriestype := :shape + label := "" + linealpha := 0 + fillstyle := :/ + fillcolor := :black + [mirror_r1, mirror_r2, mirror_r3, mirror_r4], + [mirror_z1, mirror_z2, mirror_z3, mirror_z4] + end + end +end + +@recipe function f( + ifo_ch_n_e_line::OMAS.interferometer__channel___n_e_line, +) + if :average ∈ keys(plotattributes) + @series begin + ifo_ch.n_e_line_average + end + else + xaxis --> "time / s" + yaxis --> "Integrrated n_e / m^-2" + @series begin + seriestype := :path + linewidth --> 2 + ifo_ch_n_e_line.time, ifo_ch_n_e_line.data + end + end +end + +@recipe function f( + ifo_ch_n_e_line_average::OMAS.interferometer__channel___n_e_line_average, +) + xaxis --> "time / s" + yaxis --> "Average n_e / m^-3" + @series begin + seriestype := :path + linewidth --> 2 + ifo_ch_n_e_line_average.time, ifo_ch_n_e_line_average.data + end +end