{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Laser interaction with a beam of partially stripped ions\n", "A. Petrenko (CERN-Novosibirsk, 2017-2019)\n", "\n", "This notebook describes the kinematics of photon absorption and emission by a relativistic partially stripped ion beam and application of this process to ion beam cooling in a storage ring." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the ion's initial frame of reference:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's use 4-vectors $(E/c, \\boldsymbol{p})$ for the Lorentz transformations. Ion's 4-momentum is $(\\gamma m c, \\gamma m \\boldsymbol{v})$, and the photon's 4-momentum is $(\\hbar \\omega / c, \\hbar \\boldsymbol{k})$. If the $Z$-axis is aligned with the direction of ion motion, then Lorentz transformation can be written as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\left( \\begin{array}{c}E'/c \\\\ p_x'\\\\ p_y'\\\\ p_z' \\end{array} \\right) =\n", "\\begin{pmatrix}\n", "\\gamma & 0 & 0 & -\\beta\\gamma \\\\\n", "0 & 1 & 0 & 0 \\\\\n", "0 & 0 & 1 & 0 \\\\\n", "-\\beta\\gamma & 0 & 0 & \\gamma \\\\\n", "\\end{pmatrix}\n", "\\left( \\begin{array}{c}E/c \\\\ p_x \\\\ p_y \\\\ p_z \\end{array} \\right).\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Assuming that $k_x = -k\\sin\\theta$, $k_y = 0$, $k_z = -k\\cos\\theta$, and $k = \\omega/c$ we can find the incoming photon parameters in the ion's frame of reference:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\left( \\begin{array}{c} 1 \\\\ -\\sin\\theta' \\\\ 0 \\\\ -\\cos\\theta' \\end{array} \\right) \\frac{\\omega'}{c} =\n", "\\begin{pmatrix}\n", "\\gamma & 0 & 0 & -\\beta\\gamma \\\\\n", "0 & 1 & 0 & 0 \\\\\n", "0 & 0 & 1 & 0 \\\\\n", "-\\beta\\gamma & 0 & 0 & \\gamma \\\\\n", "\\end{pmatrix}\n", "\\left( \\begin{array}{c} 1 \\\\ -\\sin\\theta \\\\ 0 \\\\ -\\cos\\theta \\end{array} \\right) \\frac{\\omega}{c}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since $k = \\omega/c$,\n", "\n", "$$\n", "\\omega' = ( 1 + \\beta \\cos\\theta )\\gamma \\omega \\approx \\left( 1 + \\beta - \\beta\\frac{\\theta^2}{2} \\right) \\gamma \\omega \\approx 2 \\gamma \\omega.\\\\\n", "$$\n", "\n", "Incoming angular spread in the beam of $\\theta \\sim 1$ mrad will be translated to a frequency error of only $\\sim10^{-6}$ in the ion's frame of reference. Frequency mismatch is dominated by the energy spread in the ion beam (typically $\\sim 10^{-4}$). It also means that the laser beam can cross the ion trajectory with an angle of several mrad at least.\n", "\n", "$$\n", "\\omega' \\sin\\theta' = \\omega \\sin\\theta,\n", "$$\n", "\n", "therefore $\\theta'$ is very small\n", "\n", "$$\n", "\\theta' \\approx \\frac{\\theta}{2\\gamma}.\n", "$$\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Excited ion after the photon absorption:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And here is the ion after the photon emission:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Photon emission will occur in a random direction. For simplicity let's assume that the photon was emitted in the same plane ($X',Z'$) at a random angle $\\theta'_1$, i.e. $k'_{1x} = k'\\sin\\theta'_1$, $k'_{1z} = k'\\cos\\theta'_1$. Then inverse Lorentz transformation gives us the emitted photon parameters in the lab frame:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\left( \\begin{array}{c} 1 \\\\ ~\\sin\\theta_1 \\\\ 0 \\\\ \\cos\\theta_1 \\end{array} \\right) \\frac{\\omega_1}{c} =\n", "\\begin{pmatrix}\n", "\\gamma & 0 & 0 & \\beta\\gamma \\\\\n", "0 & 1 & 0 & 0 \\\\\n", "0 & 0 & 1 & 0 \\\\\n", "\\beta\\gamma & 0 & 0 & \\gamma \\\\\n", "\\end{pmatrix}\n", "\\left( \\begin{array}{c} 1 \\\\ ~\\sin\\theta'_1 \\\\ 0 \\\\ \\cos\\theta'_1 \\end{array} \\right) \\frac{\\omega'}{c}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hence the scattered photon has the frequency $\\omega_1 = \\gamma(1 +\\beta\\cos\\theta'_1)\\omega' \\approx 2\\gamma^2(1 +\\beta\\cos\\theta'_1) \\omega$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\omega_1 \\sin\\theta_1 = \\omega' \\sin\\theta'_1 ~\\Rightarrow~ \\sin\\theta_1 = \\frac{\\sin\\theta'_1} {\\gamma(1 +\\beta\\cos\\theta'_1)}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The typical $\\theta_1 \\sim 1/\\gamma$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Small fraction of photons is emitted with $\\theta_1 \\sim 1$ and in this case to separate forward and backward emission it's important to know $\\cos\\theta_1$:\n", "\n", "$$\n", "\\omega_1 \\cos\\theta_1 = \\omega'\\gamma(\\beta + \\cos\\theta_1') ~\\Rightarrow~ \\cos\\theta_1 = \\frac{\\beta + \\cos\\theta_1'}{1 +\\beta\\cos\\theta'_1}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Let's consider Li-like Pb example" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Consider Li-like Pb transition $2s-2p_{1/2}$. The data from: Calculations of 2p lifetimes in the Li sequence. C. E. Theodosiou, L. J. Curtis, and M. El-Mekki. Phys. Rev. A 44, 7144 (1991):" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Excitation energy:\n", "hw0 = 230.823 # eV (ħω0)\n", "\n", "# lifetime:\n", "tau_0 = 76.6e-12 # sec\n", "\n", "g1 = 2; g2 = 2 # degeneracy factors" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from IPython.display import Latex\n", "\n", "import warnings\n", "warnings.filterwarnings('ignore')" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "\n", "\\begin{aligned}\n", "\n", "\\gamma &= 96.29 \\\\\n", "\\hbar \\omega' &= 230.8~\\rm{eV} ~~ (\\lambda' = 5.4~ \\mathrm{nm}) \\\\\n", "\\hbar \\omega~ &= 1.20~\\rm{eV} ~~ (\\lambda = 1034.4~ \\mathrm{nm}) \\\\\n", "\\hbar \\omega_{1, \\rm{max}} &= 44.5~\\rm{keV} \\\\\n", "\n", "\\end{aligned}\n" ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = 207.98 # Atomic mass\n", "mc = A*0.931e9 # eV/c\n", "mc2 = mc # eV\n", "Z = 82 # Number of protons in the ion (Lead)\n", "Ne = 3 # Number of remaining electrons (Lithium-like)\n", "Qe = 1.60217662e-19 # elementary charge in Coulombs\n", "\n", "gamma_p = 236/0.938 # equvalent gamma for the protons in the SPS\n", "gamma = gamma_p*(Z-Ne)*0.938e9/mc # Lorentz factor of the ion\n", "gamma_0 = gamma # defined for later use below\n", "beta_0 = np.sqrt(1-1/(gamma_0*gamma_0))\n", "\n", "# Corresponding photon energy in the lab frame will be\n", "hc = 0.19732697e-6 # eV*m (ħc)\n", "lambda_0 = 2*np.pi*hc/hw0 # m\n", "\n", "hw = hw0/(2*gamma) # eV (ħω)\n", "\n", "# max energy of scattered photons will be\n", "\n", "hw1 = 4*gamma*gamma*hw\n", "\n", "Latex(r\"\"\"\n", "\\begin{aligned}\n", "\n", "\\gamma &= %.2f \\\\\n", "\\hbar \\omega' &= %.1f~\\rm{eV} ~~ (\\lambda' = %.1f~ \\mathrm{nm}) \\\\\n", "\\hbar \\omega~ &= %.2f~\\rm{eV} ~~ (\\lambda = %.1f~ \\mathrm{nm}) \\\\\n", "\\hbar \\omega_{1, \\rm{max}} &= %.1f~\\rm{keV} \\\\\n", "\n", "\\end{aligned}\n", "\"\"\" % (gamma, hw0, 1e9*lambda_0, hw, 1e9*2*np.pi*hc/hw, hw1/1e3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Typical transverse kick obtained by an ion due to the photon scattering is very small compared to the typical angular spread in the beam:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(px ~ 0.46 keV, pz = 18.64 TeV) Typical transverse kick ~ 2e-08 mrad\n" ] } ], "source": [ "px = hw1/gamma_0 # eV/c\n", "pz = gamma_0*mc*beta_0 # eV/c\n", "p0 = pz # defined for later use below\n", "kick = (px/pz)*1000 # mrad\n", "print('(px ~ %.2f keV, pz = %.2f TeV) Typical transverse kick ~ %.0e mrad' % (px/1e3, pz/1e12, kick))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Ion beam definition" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "N_ions = 0.9e8 # Number of ions in the beam\n", "Np = 20000 # number of ion macro-particles in this simulation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Twiss parameters in the laser-ion interaction point (see the MAD-X SPS optics output for details):" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "beta_x = 54.614389 # m\n", "alpha_x = -1.535235\n", "beta_y = 44.332517 # m\n", "alpha_y = 1.314101\n", "\n", "Dx = 2.444732 # m\n", "Dpx = 0.097522\n", "\n", "Dy = 0.0 # m\n", "Dpy = 0.0\n", "\n", "L = 6911.5038 # m length of the ring" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ion beam parameters:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "emitt_n = 1.5e-6 # m*rad (normalized emittance)\n", "emitt = emitt_n/gamma\n", "\n", "sigma_z = 0.063 # m\n", "sigma_dp = 2e-4 # relative momentum spread" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Distribution in $z$ and $\\delta p = \\frac{\\Delta p}{p}$ can be defined easily since they are not correlated:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "z = np.random.normal(scale=sigma_z, size=Np)\n", "dp = np.random.normal(scale=sigma_dp, size=Np)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Transverse coordinates are more complicated. Similar to Elegant first we generate the beam in the normalized coordinates, defined as\n", "\n", "$$\n", "\\begin{aligned}\n", "u~ &= \\frac{x}{\\sqrt{\\beta_x}} \\\\\n", "u' &= \\frac{x'}{\\sqrt{\\beta_x}} + \\frac{x\\alpha_x}{\\beta_x\\sqrt{\\beta_x}}.\n", "\\end{aligned}\n", "$$\n", "And $\\sigma_u = \\sqrt{\\epsilon_x}$, $\\sigma_{u'} = \\sigma_u/\\beta_x$.\n", "\n", "$u$ and $u'$ are uncorrelated, therefore it's easy to generate their distribution:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "u = np.random.normal(scale=np.sqrt(emitt), size=Np)\n", "up = np.random.normal(scale=np.sqrt(emitt)/beta_x, size=Np)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we need to apply the inverse transformation from $(u,u')$ to $(x,x')$:\n", "$$\n", "\\begin{aligned}\n", "x~ &= u\\sqrt{\\beta_x} \\\\\n", "x' &= u'\\sqrt{\\beta_x} - u \\frac{\\alpha_x}{\\sqrt{\\beta_x}}.\n", "\\end{aligned}\n", "$$\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "x = u*np.sqrt(beta_x) # m\n", "xp = up*np.sqrt(beta_x) - u*alpha_x/np.sqrt(beta_x) # rad" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The same for y-plane:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "u = np.random.normal(scale=np.sqrt(emitt), size=Np)\n", "up = np.random.normal(scale=np.sqrt(emitt)/beta_y, size=Np)\n", "\n", "y = u*np.sqrt(beta_y) # m\n", "yp = up*np.sqrt(beta_y) - u*alpha_y/np.sqrt(beta_y) # rad" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's plot these distributions:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "\n", "\n", "\n", "\n", "\n", " \n", " \n", "\n", "\n", "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "function HoloViewsWidget() {\n", "}\n", "\n", "HoloViewsWidget.prototype.init_slider = function(init_val){\n", " if(this.load_json) {\n", " this.from_json()\n", " } else {\n", " this.update_cache();\n", " }\n", "}\n", "\n", "HoloViewsWidget.prototype.populate_cache = function(idx){\n", " this.cache[idx].innerHTML = this.frames[idx];\n", " if (this.embed) {\n", " delete this.frames[idx];\n", " }\n", "}\n", "\n", "HoloViewsWidget.prototype.process_error = function(msg){\n", "}\n", "\n", "HoloViewsWidget.prototype.from_json = function() {\n", " var data_url = this.json_path + this.id + '.json';\n", " $.getJSON(data_url, $.proxy(function(json_data) {\n", " this.frames = json_data;\n", " this.update_cache();\n", " this.update(0);\n", " }, this));\n", "}\n", "\n", "HoloViewsWidget.prototype.dynamic_update = function(current){\n", " if (current === undefined) {\n", " return\n", " }\n", " this.current = current;\n", " if (this.comm) {\n", " var msg = {comm_id: this.id+'_client', content: current}\n", " this.comm.send(msg);\n", " }\n", "}\n", "\n", "HoloViewsWidget.prototype.update_cache = function(force){\n", " var frame_len = Object.keys(this.frames).length;\n", " for (var i=0; i 0) {\n", " that.time = Date.now();\n", " that.dynamic_update(that.queue[that.queue.length-1]);\n", " that.queue = [];\n", " } else {\n", " that.wait = false;\n", " }\n", " if ((msg.msg_type == \"Ready\") && msg.content) {\n", " console.log(\"Python callback returned following output:\", msg.content);\n", " } else if (msg.msg_type == \"Error\") {\n", " console.log(\"Python failed with the following traceback:\", msg.traceback)\n", " }\n", " }\n", " var comm = HoloViews.comm_manager.get_client_comm(this.plot_id, this.id+'_client', ack_callback);\n", " return comm\n", " }\n", "}\n", "\n", "HoloViewsWidget.prototype.msg_handler = function(msg) {\n", " var metadata = msg.metadata;\n", " if ((metadata.msg_type == \"Ready\")) {\n", " if (metadata.content) {\n", " console.log(\"Python callback returned following output:\", metadata.content);\n", " }\n", "\treturn;\n", " } else if (metadata.msg_type == \"Error\") {\n", " console.log(\"Python failed with the following traceback:\", metadata.traceback)\n", " return\n", " }\n", " this.process_msg(msg)\n", "}\n", "\n", "HoloViewsWidget.prototype.process_msg = function(msg) {\n", "}\n", "\n", "function SelectionWidget(frames, id, slider_ids, keyMap, dim_vals, notFound, load_json, mode, cached, json_path, dynamic, plot_id){\n", " this.frames = frames;\n", " this.id = id;\n", " this.plot_id = plot_id;\n", " this.slider_ids = slider_ids;\n", " this.keyMap = keyMap\n", " this.current_frame = 0;\n", " this.current_vals = dim_vals;\n", " this.load_json = load_json;\n", " this.mode = mode;\n", " this.notFound = notFound;\n", " this.cached = cached;\n", " this.dynamic = dynamic;\n", " this.cache = {};\n", " this.json_path = json_path;\n", " this.init_slider(this.current_vals[0]);\n", " this.queue = [];\n", " this.wait = false;\n", " if (!this.cached || this.dynamic) {\n", " this.comm = this.init_comms();\n", " }\n", "}\n", "\n", "SelectionWidget.prototype = new HoloViewsWidget;\n", "\n", "\n", "SelectionWidget.prototype.get_key = function(current_vals) {\n", " var key = \"(\";\n", " for (var i=0; i Date.now()))) {\n", " this.queue.push(key);\n", " return\n", " }\n", " this.queue = [];\n", " this.time = Date.now();\n", " this.current_frame = key;\n", " this.wait = true;\n", " this.dynamic_update(key)\n", " } else if (key !== undefined) {\n", " this.update(key)\n", " }\n", "}\n", "\n", "\n", "/* Define the ScrubberWidget class */\n", "function ScrubberWidget(frames, num_frames, id, interval, load_json, mode, cached, json_path, dynamic, plot_id){\n", " this.slider_id = \"_anim_slider\" + id;\n", " this.loop_select_id = \"_anim_loop_select\" + id;\n", " this.id = id;\n", " this.plot_id = plot_id;\n", " this.interval = interval;\n", " this.current_frame = 0;\n", " this.direction = 0;\n", " this.dynamic = dynamic;\n", " this.timer = null;\n", " this.load_json = load_json;\n", " this.mode = mode;\n", " this.cached = cached;\n", " this.frames = frames;\n", " this.cache = {};\n", " this.length = num_frames;\n", " this.json_path = json_path;\n", " document.getElementById(this.slider_id).max = this.length - 1;\n", " this.init_slider(0);\n", " this.wait = false;\n", " this.queue = [];\n", " if (!this.cached || this.dynamic) {\n", " this.comm = this.init_comms()\n", " }\n", "}\n", "\n", "ScrubberWidget.prototype = new HoloViewsWidget;\n", "\n", "ScrubberWidget.prototype.set_frame = function(frame){\n", " this.current_frame = frame;\n", " var widget = document.getElementById(this.slider_id);\n", " if (widget === null) {\n", " this.pause_animation();\n", " return\n", " }\n", " widget.value = this.current_frame;\n", " if (this.dynamic || !this.cached) {\n", " if ((this.time !== undefined) && ((this.wait) && ((this.time + 10000) > Date.now()))) {\n", " this.queue.push(frame);\n", " return\n", " }\n", " this.queue = [];\n", " this.time = Date.now();\n", " this.wait = true;\n", " this.dynamic_update(frame)\n", " } else {\n", " this.update(frame)\n", " }\n", "}\n", "\n", "ScrubberWidget.prototype.get_loop_state = function(){\n", " var button_group = document[this.loop_select_id].state;\n", " for (var i = 0; i < button_group.length; i++) {\n", " var button = button_group[i];\n", " if (button.checked) {\n", " return button.value;\n", " }\n", " }\n", " return undefined;\n", "}\n", "\n", "\n", "ScrubberWidget.prototype.next_frame = function() {\n", " this.set_frame(Math.min(this.length - 1, this.current_frame + 1));\n", "}\n", "\n", "ScrubberWidget.prototype.previous_frame = function() {\n", " this.set_frame(Math.max(0, this.current_frame - 1));\n", "}\n", "\n", "ScrubberWidget.prototype.first_frame = function() {\n", " this.set_frame(0);\n", "}\n", "\n", "ScrubberWidget.prototype.last_frame = function() {\n", " this.set_frame(this.length - 1);\n", "}\n", "\n", "ScrubberWidget.prototype.slower = function() {\n", " this.interval /= 0.7;\n", " if(this.direction > 0){this.play_animation();}\n", " else if(this.direction < 0){this.reverse_animation();}\n", "}\n", "\n", "ScrubberWidget.prototype.faster = function() {\n", " this.interval *= 0.7;\n", " if(this.direction > 0){this.play_animation();}\n", " else if(this.direction < 0){this.reverse_animation();}\n", "}\n", "\n", "ScrubberWidget.prototype.anim_step_forward = function() {\n", " if(this.current_frame < this.length - 1){\n", " this.next_frame();\n", " }else{\n", " var loop_state = this.get_loop_state();\n", " if(loop_state == \"loop\"){\n", " this.first_frame();\n", " }else if(loop_state == \"reflect\"){\n", " this.last_frame();\n", " this.reverse_animation();\n", " }else{\n", " this.pause_animation();\n", " this.last_frame();\n", " }\n", " }\n", "}\n", "\n", "ScrubberWidget.prototype.anim_step_reverse = function() {\n", " if(this.current_frame > 0){\n", " this.previous_frame();\n", " } else {\n", " var loop_state = this.get_loop_state();\n", " if(loop_state == \"loop\"){\n", " this.last_frame();\n", " }else if(loop_state == \"reflect\"){\n", " this.first_frame();\n", " this.play_animation();\n", " }else{\n", " this.pause_animation();\n", " this.first_frame();\n", " }\n", " }\n", "}\n", "\n", "ScrubberWidget.prototype.pause_animation = function() {\n", " this.direction = 0;\n", " if (this.timer){\n", " clearInterval(this.timer);\n", " this.timer = null;\n", " }\n", "}\n", "\n", "ScrubberWidget.prototype.play_animation = function() {\n", " this.pause_animation();\n", " this.direction = 1;\n", " var t = this;\n", " if (!this.timer) this.timer = setInterval(function(){t.anim_step_forward();}, this.interval);\n", "}\n", "\n", "ScrubberWidget.prototype.reverse_animation = function() {\n", " this.pause_animation();\n", " this.direction = -1;\n", " var t = this;\n", " if (!this.timer) this.timer = setInterval(function(){t.anim_step_reverse();}, this.interval);\n", "}\n", "\n", "function extend(destination, source) {\n", " for (var k in source) {\n", " if (source.hasOwnProperty(k)) {\n", " destination[k] = source[k];\n", " }\n", " }\n", " return destination;\n", "}\n", "\n", "function update_widget(widget, values) {\n", " if (widget.hasClass(\"ui-slider\")) {\n", " widget.slider('option', {\n", " min: 0,\n", " max: values.length-1,\n", " dim_vals: values,\n", " value: 0,\n", " dim_labels: values\n", " })\n", " widget.slider('option', 'slide').call(widget, event, {value: 0})\n", " } else {\n", " widget.empty();\n", " for (var i=0; i\", {\n", " value: i,\n", " text: values[i]\n", " }))\n", " };\n", " widget.data('values', values);\n", " widget.data('value', 0);\n", " widget.trigger(\"change\");\n", " };\n", "}\n", "\n", "function init_slider(id, plot_id, dim, values, next_vals, labels, dynamic, step, value, next_dim,\n", " dim_idx, delay, jQueryUI_CDN, UNDERSCORE_CDN) {\n", " // Slider JS Block START\n", " function loadcssfile(filename){\n", " var fileref=document.createElement(\"link\")\n", " fileref.setAttribute(\"rel\", \"stylesheet\")\n", " fileref.setAttribute(\"type\", \"text/css\")\n", " fileref.setAttribute(\"href\", filename)\n", " document.getElementsByTagName(\"head\")[0].appendChild(fileref)\n", " }\n", " loadcssfile(\"https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css\");\n", " /* Check if jQuery and jQueryUI have been loaded\n", " otherwise load with require.js */\n", " var jQuery = window.jQuery,\n", " // check for old versions of jQuery\n", " oldjQuery = jQuery && !!jQuery.fn.jquery.match(/^1\\.[0-4](\\.|$)/),\n", " jquery_path = '',\n", " paths = {},\n", " noConflict;\n", " var jQueryUI = jQuery.ui;\n", " // check for jQuery\n", " if (!jQuery || oldjQuery) {\n", " // load if it's not available or doesn't meet min standards\n", " paths.jQuery = jQuery;\n", " noConflict = !!oldjQuery;\n", " } else {\n", " // register the current jQuery\n", " define('jquery', [], function() { return jQuery; });\n", " }\n", " if (!jQueryUI) {\n", " paths.jQueryUI = jQueryUI_CDN.slice(null, -3);\n", " } else {\n", " define('jQueryUI', [], function() { return jQuery.ui; });\n", " }\n", " paths.underscore = UNDERSCORE_CDN.slice(null, -3);\n", " var jquery_require = {\n", " paths: paths,\n", " shim: {\n", " \"jQueryUI\": {\n", " exports:\"$\",\n", " deps: ['jquery']\n", " },\n", " \"underscore\": {\n", " exports: '_'\n", " }\n", " }\n", " }\n", " require.config(jquery_require);\n", " require([\"jQueryUI\", \"underscore\"], function(jUI, _){\n", " if (noConflict) $.noConflict(true);\n", " var vals = values;\n", " if (dynamic && vals.constructor === Array) {\n", " var default_value = parseFloat(value);\n", " var min = parseFloat(vals[0]);\n", " var max = parseFloat(vals[vals.length-1]);\n", " var wstep = step;\n", " var wlabels = [default_value];\n", " var init_label = default_value;\n", " } else {\n", " var min = 0;\n", " if (dynamic) {\n", " var max = Object.keys(vals).length - 1;\n", " var init_label = labels[value];\n", " var default_value = values[value];\n", " } else {\n", " var max = vals.length - 1;\n", " var init_label = labels[value];\n", " var default_value = value;\n", " }\n", " var wstep = 1;\n", " var wlabels = labels;\n", " }\n", " function adjustFontSize(text) {\n", " var width_ratio = (text.parent().width()/8)/text.val().length;\n", " var size = Math.min(0.9, Math.max(0.6, width_ratio))+'em';\n", " text.css('font-size', size);\n", " }\n", " var slider = $('#_anim_widget'+id+'_'+dim);\n", " slider.slider({\n", " animate: \"fast\",\n", " min: min,\n", " max: max,\n", " step: wstep,\n", " value: default_value,\n", " dim_vals: vals,\n", " dim_labels: wlabels,\n", " next_vals: next_vals,\n", " slide: function(event, ui) {\n", " var vals = slider.slider(\"option\", \"dim_vals\");\n", " var next_vals = slider.slider(\"option\", \"next_vals\");\n", " var dlabels = slider.slider(\"option\", \"dim_labels\");\n", " if (dynamic) {\n", " var dim_val = ui.value;\n", " if (vals.constructor === Array) {\n", " var label = ui.value;\n", " } else {\n", " var label = dlabels[ui.value];\n", " }\n", " } else {\n", " var dim_val = vals[ui.value];\n", " var label = dlabels[ui.value];\n", " }\n", " var text = $('#textInput'+id+'_'+dim);\n", " text.val(label);\n", " adjustFontSize(text);\n", " HoloViews.index[plot_id].set_frame(dim_val, dim_idx);\n", " if (Object.keys(next_vals).length > 0) {\n", " var new_vals = next_vals[dim_val];\n", " var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n", " update_widget(next_widget, new_vals);\n", " }\n", " }\n", " });\n", " slider.keypress(function(event) {\n", " if (event.which == 80 || event.which == 112) {\n", " var start = slider.slider(\"option\", \"value\");\n", " var stop = slider.slider(\"option\", \"max\");\n", " for (var i=start; i<=stop; i++) {\n", " var delay = i*delay;\n", " $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n", " var val = {value:i};\n", " slider.slider('value',i);\n", " slider.slider(\"option\", \"slide\")(null, val);\n", " }, slider), delay);}, slider)(i);\n", " }\n", " }\n", " if (event.which == 82 || event.which == 114) {\n", " var start = slider.slider(\"option\", \"value\");\n", " var stop = slider.slider(\"option\", \"min\");\n", " var count = 0;\n", " for (var i=start; i>=stop; i--) {\n", " var delay = count*delay;\n", " count = count + 1;\n", " $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n", " var val = {value:i};\n", " slider.slider('value',i);\n", " slider.slider(\"option\", \"slide\")(null, val);\n", " }, slider), delay);}, slider)(i);\n", " }\n", " }\n", " });\n", " var textInput = $('#textInput'+id+'_'+dim)\n", " textInput.val(init_label);\n", " adjustFontSize(textInput);\n", " });\n", "}\n", "\n", "function init_dropdown(id, plot_id, dim, vals, value, next_vals, labels, next_dim, dim_idx, dynamic) {\n", " var widget = $(\"#_anim_widget\"+id+'_'+dim);\n", " widget.data('values', vals)\n", " for (var i=0; i\", {\n", " value: val,\n", " text: labels[i]\n", " }));\n", " };\n", " widget.data(\"next_vals\", next_vals);\n", " widget.val(value);\n", " widget.on('change', function(event, ui) {\n", " if (dynamic) {\n", " var dim_val = parseInt(this.value);\n", " } else {\n", " var dim_val = $.data(this, 'values')[this.value];\n", " }\n", " var next_vals = $.data(this, \"next_vals\");\n", " if (Object.keys(next_vals).length > 0) {\n", " var new_vals = next_vals[dim_val];\n", " var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n", " update_widget(next_widget, new_vals);\n", " }\n", " var widgets = HoloViews.index[plot_id]\n", " if (widgets) {\n", " widgets.set_frame(dim_val, dim_idx);\n", " }\n", " });\n", "}\n", "\n", "\n", "if (window.HoloViews === undefined) {\n", " window.HoloViews = {}\n", " window.PyViz = window.HoloViews\n", "} else if (window.PyViz === undefined) {\n", " window.PyViz = window.HoloViews\n", "}\n", "\n", "\n", "var _namespace = {\n", " init_slider: init_slider,\n", " init_dropdown: init_dropdown,\n", " comms: {},\n", " comm_status: {},\n", " index: {},\n", " plot_index: {},\n", " kernels: {},\n", " receivers: {}\n", "}\n", "\n", "for (var k in _namespace) {\n", " if (!(k in window.HoloViews)) {\n", " window.HoloViews[k] = _namespace[k];\n", " }\n", "}\n", "\n", "// Define MPL specific subclasses\n", "function MPLSelectionWidget() {\n", " SelectionWidget.apply(this, arguments);\n", "}\n", "\n", "function MPLScrubberWidget() {\n", " ScrubberWidget.apply(this, arguments);\n", "}\n", "\n", "// Let them inherit from the baseclasses\n", "MPLSelectionWidget.prototype = Object.create(SelectionWidget.prototype);\n", "MPLScrubberWidget.prototype = Object.create(ScrubberWidget.prototype);\n", "\n", "// Define methods to override on widgets\n", "var MPLMethods = {\n", " init_slider : function(init_val){\n", " if(this.load_json) {\n", " this.from_json()\n", " } else {\n", " this.update_cache();\n", " }\n", " if (this.dynamic | !this.cached | (this.current_vals === undefined)) {\n", " this.update(0)\n", " } else {\n", " this.set_frame(this.current_vals[0], 0)\n", " }\n", " },\n", " process_msg : function(msg) {\n", " var data = msg.content.data;\n", " this.frames[this.current] = data;\n", " this.update_cache(true);\n", " this.update(this.current);\n", " }\n", "}\n", "// Extend MPL widgets with backend specific methods\n", "extend(MPLSelectionWidget.prototype, MPLMethods);\n", "extend(MPLScrubberWidget.prototype, MPLMethods);\n", "\n", "window.HoloViews.MPLSelectionWidget = MPLSelectionWidget\n", "window.HoloViews.MPLScrubberWidget = MPLScrubberWidget\n", "\n", " function JupyterCommManager() {\n", " }\n", "\n", " JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n", " if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", " comm_manager.register_target(comm_id, function(comm) {\n", " comm.on_msg(msg_handler);\n", " });\n", " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", " window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n", " comm.onMsg = msg_handler;\n", " });\n", " }\n", " }\n", "\n", " JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n", " if (comm_id in window.PyViz.comms) {\n", " return window.PyViz.comms[comm_id];\n", " } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", " var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n", " if (msg_handler) {\n", " comm.on_msg(msg_handler);\n", " }\n", " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", " var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n", " comm.open();\n", " if (msg_handler) {\n", " comm.onMsg = msg_handler;\n", " }\n", " }\n", "\n", " window.PyViz.comms[comm_id] = comm;\n", " return comm;\n", " }\n", "\n", " window.PyViz.comm_manager = new JupyterCommManager();\n", " \n", "\n", "var JS_MIME_TYPE = 'application/javascript';\n", "var HTML_MIME_TYPE = 'text/html';\n", "var EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\n", "var CLASS_NAME = 'output';\n", "\n", "/**\n", " * Render data to the DOM node\n", " */\n", "function render(props, node) {\n", " var div = document.createElement(\"div\");\n", " var script = document.createElement(\"script\");\n", " node.appendChild(div);\n", " node.appendChild(script);\n", "}\n", "\n", "/**\n", " * Handle when a new output is added\n", " */\n", "function handle_add_output(event, handle) {\n", " var output_area = handle.output_area;\n", " var output = handle.output;\n", " if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", " return\n", " }\n", " var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", " if (id !== undefined) {\n", " var nchildren = toinsert.length;\n", " var html_node = toinsert[nchildren-1].children[0];\n", " html_node.innerHTML = output.data[HTML_MIME_TYPE];\n", " var scripts = [];\n", " var nodelist = html_node.querySelectorAll(\"script\");\n", " for (var i in nodelist) {\n", " if (nodelist.hasOwnProperty(i)) {\n", " scripts.push(nodelist[i])\n", " }\n", " }\n", "\n", " scripts.forEach( function (oldScript) {\n", " var newScript = document.createElement(\"script\");\n", " var attrs = [];\n", " var nodemap = oldScript.attributes;\n", " for (var j in nodemap) {\n", " if (nodemap.hasOwnProperty(j)) {\n", " attrs.push(nodemap[j])\n", " }\n", " }\n", " attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n", " newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n", " oldScript.parentNode.replaceChild(newScript, oldScript);\n", " });\n", " if (JS_MIME_TYPE in output.data) {\n", " toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n", " }\n", " output_area._hv_plot_id = id;\n", " if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n", " window.PyViz.plot_index[id] = Bokeh.index[id];\n", " } else {\n", " window.PyViz.plot_index[id] = null;\n", " }\n", " } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", " var bk_div = document.createElement(\"div\");\n", " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", " var script_attrs = bk_div.children[0].attributes;\n", " for (var i = 0; i < script_attrs.length; i++) {\n", " toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n", " }\n", " // store reference to server id on output_area\n", " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", " }\n", "}\n", "\n", "/**\n", " * Handle when an output is cleared or removed\n", " */\n", "function handle_clear_output(event, handle) {\n", " var id = handle.cell.output_area._hv_plot_id;\n", " var server_id = handle.cell.output_area._bokeh_server_id;\n", " if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n", " var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n", " if (server_id !== null) {\n", " comm.send({event_type: 'server_delete', 'id': server_id});\n", " return;\n", " } else if (comm !== null) {\n", " comm.send({event_type: 'delete', 'id': id});\n", " }\n", " delete PyViz.plot_index[id];\n", " if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n", " var doc = window.Bokeh.index[id].model.document\n", " doc.clear();\n", " const i = window.Bokeh.documents.indexOf(doc);\n", " if (i > -1) {\n", " window.Bokeh.documents.splice(i, 1);\n", " }\n", " }\n", "}\n", "\n", "/**\n", " * Handle kernel restart event\n", " */\n", "function handle_kernel_cleanup(event, handle) {\n", " delete PyViz.comms[\"hv-extension-comm\"];\n", " window.PyViz.plot_index = {}\n", "}\n", "\n", "/**\n", " * Handle update_display_data messages\n", " */\n", "function handle_update_output(event, handle) {\n", " handle_clear_output(event, {cell: {output_area: handle.output_area}})\n", " handle_add_output(event, handle)\n", "}\n", "\n", "function register_renderer(events, OutputArea) {\n", " function append_mime(data, metadata, element) {\n", " // create a DOM node to render to\n", " var toinsert = this.create_output_subarea(\n", " metadata,\n", " CLASS_NAME,\n", " EXEC_MIME_TYPE\n", " );\n", " this.keyboard_manager.register_events(toinsert);\n", " // Render to node\n", " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", " render(props, toinsert[0]);\n", " element.append(toinsert);\n", " return toinsert\n", " }\n", "\n", " events.on('output_added.OutputArea', handle_add_output);\n", " events.on('output_updated.OutputArea', handle_update_output);\n", " events.on('clear_output.CodeCell', handle_clear_output);\n", " events.on('delete.Cell', handle_clear_output);\n", " events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n", "\n", " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", " safe: true,\n", " index: 0\n", " });\n", "}\n", "\n", "if (window.Jupyter !== undefined) {\n", " try {\n", " var events = require('base/js/events');\n", " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", " register_renderer(events, OutputArea);\n", " }\n", " } catch(err) {\n", " }\n", "}\n" ], "application/vnd.holoviews_load.v0+json": "function HoloViewsWidget() {\n}\n\nHoloViewsWidget.prototype.init_slider = function(init_val){\n if(this.load_json) {\n this.from_json()\n } else {\n this.update_cache();\n }\n}\n\nHoloViewsWidget.prototype.populate_cache = function(idx){\n this.cache[idx].innerHTML = this.frames[idx];\n if (this.embed) {\n delete this.frames[idx];\n }\n}\n\nHoloViewsWidget.prototype.process_error = function(msg){\n}\n\nHoloViewsWidget.prototype.from_json = function() {\n var data_url = this.json_path + this.id + '.json';\n $.getJSON(data_url, $.proxy(function(json_data) {\n this.frames = json_data;\n this.update_cache();\n this.update(0);\n }, this));\n}\n\nHoloViewsWidget.prototype.dynamic_update = function(current){\n if (current === undefined) {\n return\n }\n this.current = current;\n if (this.comm) {\n var msg = {comm_id: this.id+'_client', content: current}\n this.comm.send(msg);\n }\n}\n\nHoloViewsWidget.prototype.update_cache = function(force){\n var frame_len = Object.keys(this.frames).length;\n for (var i=0; i 0) {\n that.time = Date.now();\n that.dynamic_update(that.queue[that.queue.length-1]);\n that.queue = [];\n } else {\n that.wait = false;\n }\n if ((msg.msg_type == \"Ready\") && msg.content) {\n console.log(\"Python callback returned following output:\", msg.content);\n } else if (msg.msg_type == \"Error\") {\n console.log(\"Python failed with the following traceback:\", msg.traceback)\n }\n }\n var comm = HoloViews.comm_manager.get_client_comm(this.plot_id, this.id+'_client', ack_callback);\n return comm\n }\n}\n\nHoloViewsWidget.prototype.msg_handler = function(msg) {\n var metadata = msg.metadata;\n if ((metadata.msg_type == \"Ready\")) {\n if (metadata.content) {\n console.log(\"Python callback returned following output:\", metadata.content);\n }\n\treturn;\n } else if (metadata.msg_type == \"Error\") {\n console.log(\"Python failed with the following traceback:\", metadata.traceback)\n return\n }\n this.process_msg(msg)\n}\n\nHoloViewsWidget.prototype.process_msg = function(msg) {\n}\n\nfunction SelectionWidget(frames, id, slider_ids, keyMap, dim_vals, notFound, load_json, mode, cached, json_path, dynamic, plot_id){\n this.frames = frames;\n this.id = id;\n this.plot_id = plot_id;\n this.slider_ids = slider_ids;\n this.keyMap = keyMap\n this.current_frame = 0;\n this.current_vals = dim_vals;\n this.load_json = load_json;\n this.mode = mode;\n this.notFound = notFound;\n this.cached = cached;\n this.dynamic = dynamic;\n this.cache = {};\n this.json_path = json_path;\n this.init_slider(this.current_vals[0]);\n this.queue = [];\n this.wait = false;\n if (!this.cached || this.dynamic) {\n this.comm = this.init_comms();\n }\n}\n\nSelectionWidget.prototype = new HoloViewsWidget;\n\n\nSelectionWidget.prototype.get_key = function(current_vals) {\n var key = \"(\";\n for (var i=0; i Date.now()))) {\n this.queue.push(key);\n return\n }\n this.queue = [];\n this.time = Date.now();\n this.current_frame = key;\n this.wait = true;\n this.dynamic_update(key)\n } else if (key !== undefined) {\n this.update(key)\n }\n}\n\n\n/* Define the ScrubberWidget class */\nfunction ScrubberWidget(frames, num_frames, id, interval, load_json, mode, cached, json_path, dynamic, plot_id){\n this.slider_id = \"_anim_slider\" + id;\n this.loop_select_id = \"_anim_loop_select\" + id;\n this.id = id;\n this.plot_id = plot_id;\n this.interval = interval;\n this.current_frame = 0;\n this.direction = 0;\n this.dynamic = dynamic;\n this.timer = null;\n this.load_json = load_json;\n this.mode = mode;\n this.cached = cached;\n this.frames = frames;\n this.cache = {};\n this.length = num_frames;\n this.json_path = json_path;\n document.getElementById(this.slider_id).max = this.length - 1;\n this.init_slider(0);\n this.wait = false;\n this.queue = [];\n if (!this.cached || this.dynamic) {\n this.comm = this.init_comms()\n }\n}\n\nScrubberWidget.prototype = new HoloViewsWidget;\n\nScrubberWidget.prototype.set_frame = function(frame){\n this.current_frame = frame;\n var widget = document.getElementById(this.slider_id);\n if (widget === null) {\n this.pause_animation();\n return\n }\n widget.value = this.current_frame;\n if (this.dynamic || !this.cached) {\n if ((this.time !== undefined) && ((this.wait) && ((this.time + 10000) > Date.now()))) {\n this.queue.push(frame);\n return\n }\n this.queue = [];\n this.time = Date.now();\n this.wait = true;\n this.dynamic_update(frame)\n } else {\n this.update(frame)\n }\n}\n\nScrubberWidget.prototype.get_loop_state = function(){\n var button_group = document[this.loop_select_id].state;\n for (var i = 0; i < button_group.length; i++) {\n var button = button_group[i];\n if (button.checked) {\n return button.value;\n }\n }\n return undefined;\n}\n\n\nScrubberWidget.prototype.next_frame = function() {\n this.set_frame(Math.min(this.length - 1, this.current_frame + 1));\n}\n\nScrubberWidget.prototype.previous_frame = function() {\n this.set_frame(Math.max(0, this.current_frame - 1));\n}\n\nScrubberWidget.prototype.first_frame = function() {\n this.set_frame(0);\n}\n\nScrubberWidget.prototype.last_frame = function() {\n this.set_frame(this.length - 1);\n}\n\nScrubberWidget.prototype.slower = function() {\n this.interval /= 0.7;\n if(this.direction > 0){this.play_animation();}\n else if(this.direction < 0){this.reverse_animation();}\n}\n\nScrubberWidget.prototype.faster = function() {\n this.interval *= 0.7;\n if(this.direction > 0){this.play_animation();}\n else if(this.direction < 0){this.reverse_animation();}\n}\n\nScrubberWidget.prototype.anim_step_forward = function() {\n if(this.current_frame < this.length - 1){\n this.next_frame();\n }else{\n var loop_state = this.get_loop_state();\n if(loop_state == \"loop\"){\n this.first_frame();\n }else if(loop_state == \"reflect\"){\n this.last_frame();\n this.reverse_animation();\n }else{\n this.pause_animation();\n this.last_frame();\n }\n }\n}\n\nScrubberWidget.prototype.anim_step_reverse = function() {\n if(this.current_frame > 0){\n this.previous_frame();\n } else {\n var loop_state = this.get_loop_state();\n if(loop_state == \"loop\"){\n this.last_frame();\n }else if(loop_state == \"reflect\"){\n this.first_frame();\n this.play_animation();\n }else{\n this.pause_animation();\n this.first_frame();\n }\n }\n}\n\nScrubberWidget.prototype.pause_animation = function() {\n this.direction = 0;\n if (this.timer){\n clearInterval(this.timer);\n this.timer = null;\n }\n}\n\nScrubberWidget.prototype.play_animation = function() {\n this.pause_animation();\n this.direction = 1;\n var t = this;\n if (!this.timer) this.timer = setInterval(function(){t.anim_step_forward();}, this.interval);\n}\n\nScrubberWidget.prototype.reverse_animation = function() {\n this.pause_animation();\n this.direction = -1;\n var t = this;\n if (!this.timer) this.timer = setInterval(function(){t.anim_step_reverse();}, this.interval);\n}\n\nfunction extend(destination, source) {\n for (var k in source) {\n if (source.hasOwnProperty(k)) {\n destination[k] = source[k];\n }\n }\n return destination;\n}\n\nfunction update_widget(widget, values) {\n if (widget.hasClass(\"ui-slider\")) {\n widget.slider('option', {\n min: 0,\n max: values.length-1,\n dim_vals: values,\n value: 0,\n dim_labels: values\n })\n widget.slider('option', 'slide').call(widget, event, {value: 0})\n } else {\n widget.empty();\n for (var i=0; i\", {\n value: i,\n text: values[i]\n }))\n };\n widget.data('values', values);\n widget.data('value', 0);\n widget.trigger(\"change\");\n };\n}\n\nfunction init_slider(id, plot_id, dim, values, next_vals, labels, dynamic, step, value, next_dim,\n dim_idx, delay, jQueryUI_CDN, UNDERSCORE_CDN) {\n // Slider JS Block START\n function loadcssfile(filename){\n var fileref=document.createElement(\"link\")\n fileref.setAttribute(\"rel\", \"stylesheet\")\n fileref.setAttribute(\"type\", \"text/css\")\n fileref.setAttribute(\"href\", filename)\n document.getElementsByTagName(\"head\")[0].appendChild(fileref)\n }\n loadcssfile(\"https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css\");\n /* Check if jQuery and jQueryUI have been loaded\n otherwise load with require.js */\n var jQuery = window.jQuery,\n // check for old versions of jQuery\n oldjQuery = jQuery && !!jQuery.fn.jquery.match(/^1\\.[0-4](\\.|$)/),\n jquery_path = '',\n paths = {},\n noConflict;\n var jQueryUI = jQuery.ui;\n // check for jQuery\n if (!jQuery || oldjQuery) {\n // load if it's not available or doesn't meet min standards\n paths.jQuery = jQuery;\n noConflict = !!oldjQuery;\n } else {\n // register the current jQuery\n define('jquery', [], function() { return jQuery; });\n }\n if (!jQueryUI) {\n paths.jQueryUI = jQueryUI_CDN.slice(null, -3);\n } else {\n define('jQueryUI', [], function() { return jQuery.ui; });\n }\n paths.underscore = UNDERSCORE_CDN.slice(null, -3);\n var jquery_require = {\n paths: paths,\n shim: {\n \"jQueryUI\": {\n exports:\"$\",\n deps: ['jquery']\n },\n \"underscore\": {\n exports: '_'\n }\n }\n }\n require.config(jquery_require);\n require([\"jQueryUI\", \"underscore\"], function(jUI, _){\n if (noConflict) $.noConflict(true);\n var vals = values;\n if (dynamic && vals.constructor === Array) {\n var default_value = parseFloat(value);\n var min = parseFloat(vals[0]);\n var max = parseFloat(vals[vals.length-1]);\n var wstep = step;\n var wlabels = [default_value];\n var init_label = default_value;\n } else {\n var min = 0;\n if (dynamic) {\n var max = Object.keys(vals).length - 1;\n var init_label = labels[value];\n var default_value = values[value];\n } else {\n var max = vals.length - 1;\n var init_label = labels[value];\n var default_value = value;\n }\n var wstep = 1;\n var wlabels = labels;\n }\n function adjustFontSize(text) {\n var width_ratio = (text.parent().width()/8)/text.val().length;\n var size = Math.min(0.9, Math.max(0.6, width_ratio))+'em';\n text.css('font-size', size);\n }\n var slider = $('#_anim_widget'+id+'_'+dim);\n slider.slider({\n animate: \"fast\",\n min: min,\n max: max,\n step: wstep,\n value: default_value,\n dim_vals: vals,\n dim_labels: wlabels,\n next_vals: next_vals,\n slide: function(event, ui) {\n var vals = slider.slider(\"option\", \"dim_vals\");\n var next_vals = slider.slider(\"option\", \"next_vals\");\n var dlabels = slider.slider(\"option\", \"dim_labels\");\n if (dynamic) {\n var dim_val = ui.value;\n if (vals.constructor === Array) {\n var label = ui.value;\n } else {\n var label = dlabels[ui.value];\n }\n } else {\n var dim_val = vals[ui.value];\n var label = dlabels[ui.value];\n }\n var text = $('#textInput'+id+'_'+dim);\n text.val(label);\n adjustFontSize(text);\n HoloViews.index[plot_id].set_frame(dim_val, dim_idx);\n if (Object.keys(next_vals).length > 0) {\n var new_vals = next_vals[dim_val];\n var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n update_widget(next_widget, new_vals);\n }\n }\n });\n slider.keypress(function(event) {\n if (event.which == 80 || event.which == 112) {\n var start = slider.slider(\"option\", \"value\");\n var stop = slider.slider(\"option\", \"max\");\n for (var i=start; i<=stop; i++) {\n var delay = i*delay;\n $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n var val = {value:i};\n slider.slider('value',i);\n slider.slider(\"option\", \"slide\")(null, val);\n }, slider), delay);}, slider)(i);\n }\n }\n if (event.which == 82 || event.which == 114) {\n var start = slider.slider(\"option\", \"value\");\n var stop = slider.slider(\"option\", \"min\");\n var count = 0;\n for (var i=start; i>=stop; i--) {\n var delay = count*delay;\n count = count + 1;\n $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n var val = {value:i};\n slider.slider('value',i);\n slider.slider(\"option\", \"slide\")(null, val);\n }, slider), delay);}, slider)(i);\n }\n }\n });\n var textInput = $('#textInput'+id+'_'+dim)\n textInput.val(init_label);\n adjustFontSize(textInput);\n });\n}\n\nfunction init_dropdown(id, plot_id, dim, vals, value, next_vals, labels, next_dim, dim_idx, dynamic) {\n var widget = $(\"#_anim_widget\"+id+'_'+dim);\n widget.data('values', vals)\n for (var i=0; i\", {\n value: val,\n text: labels[i]\n }));\n };\n widget.data(\"next_vals\", next_vals);\n widget.val(value);\n widget.on('change', function(event, ui) {\n if (dynamic) {\n var dim_val = parseInt(this.value);\n } else {\n var dim_val = $.data(this, 'values')[this.value];\n }\n var next_vals = $.data(this, \"next_vals\");\n if (Object.keys(next_vals).length > 0) {\n var new_vals = next_vals[dim_val];\n var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n update_widget(next_widget, new_vals);\n }\n var widgets = HoloViews.index[plot_id]\n if (widgets) {\n widgets.set_frame(dim_val, dim_idx);\n }\n });\n}\n\n\nif (window.HoloViews === undefined) {\n window.HoloViews = {}\n window.PyViz = window.HoloViews\n} else if (window.PyViz === undefined) {\n window.PyViz = window.HoloViews\n}\n\n\nvar _namespace = {\n init_slider: init_slider,\n init_dropdown: init_dropdown,\n comms: {},\n comm_status: {},\n index: {},\n plot_index: {},\n kernels: {},\n receivers: {}\n}\n\nfor (var k in _namespace) {\n if (!(k in window.HoloViews)) {\n window.HoloViews[k] = _namespace[k];\n }\n}\n\n// Define MPL specific subclasses\nfunction MPLSelectionWidget() {\n SelectionWidget.apply(this, arguments);\n}\n\nfunction MPLScrubberWidget() {\n ScrubberWidget.apply(this, arguments);\n}\n\n// Let them inherit from the baseclasses\nMPLSelectionWidget.prototype = Object.create(SelectionWidget.prototype);\nMPLScrubberWidget.prototype = Object.create(ScrubberWidget.prototype);\n\n// Define methods to override on widgets\nvar MPLMethods = {\n init_slider : function(init_val){\n if(this.load_json) {\n this.from_json()\n } else {\n this.update_cache();\n }\n if (this.dynamic | !this.cached | (this.current_vals === undefined)) {\n this.update(0)\n } else {\n this.set_frame(this.current_vals[0], 0)\n }\n },\n process_msg : function(msg) {\n var data = msg.content.data;\n this.frames[this.current] = data;\n this.update_cache(true);\n this.update(this.current);\n }\n}\n// Extend MPL widgets with backend specific methods\nextend(MPLSelectionWidget.prototype, MPLMethods);\nextend(MPLScrubberWidget.prototype, MPLMethods);\n\nwindow.HoloViews.MPLSelectionWidget = MPLSelectionWidget\nwindow.HoloViews.MPLScrubberWidget = MPLScrubberWidget\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n }\n\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import holoviews as hv\n", "hv.extension('matplotlib')" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "%output backend='matplotlib' fig='png' size=120 dpi=120" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "%opts Points Curve [show_grid=True aspect=2]\n", "%opts Points (alpha=0.3 s=1)\n", "#%opts Points (alpha=0.5) [aspect=2]" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "dim_x = hv.Dimension('x', unit='mm', range=(-8,+8))\n", "dim_xp = hv.Dimension('xp', unit='mrad', label=\"x'\", range=(-0.16,+0.16))\n", "\n", "dim_y = hv.Dimension('y', unit='mm', range=(-8,+8))\n", "dim_yp = hv.Dimension('yp', unit='mrad', label=\"y'\", range=(-0.16,+0.16))\n", "\n", "dim_z = hv.Dimension('z', unit='mm', range=(-250.0,+250.0))\n", "#dim_dp = hv.Dimension('dp', label='100%*Δp/p')\n", "dim_dp = hv.Dimension('dp', label='100%*$\\Delta p/p$', range=(-0.15,+0.15))" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Points [y,yp]" ] }, "execution_count": 16, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "hv.Points((y*1e3, yp*1e3), kdims=[dim_y,dim_yp])" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Points [z,dp]" ] }, "execution_count": 17, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "hv.Points((z*1e3,dp*100), kdims=[dim_z,dim_dp])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As a crosscheck we can calculate the RMS-emittance of this beam using the statistical definition of emittance $\\epsilon_{RMS} = \\sqrt{\\left \\left - \\left^2}$." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "Normalized $\\epsilon_{RMS}$ = 1.50 mm*mrad (compared to 1.50 mm*mrad set originally)." ], "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emitt_RMS = np.sqrt(np.mean(x*x)*np.mean(xp*xp) - np.mean(x*xp)*np.mean(x*xp))\n", "#emitt_RMS = np.sqrt(np.mean(y*y)*np.mean(yp*yp) - np.mean(y*yp)*np.mean(y*yp))\n", "\n", "Latex(\"Normalized $\\epsilon_{RMS}$ = %.2f mm*mrad (compared to %.2f mm*mrad set originally).\"\n", " % (emitt_RMS*1e6*gamma, emitt_n*1e6))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$\\epsilon_{RMS}$ will be closer to the input value with more macro-particles per bunch." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And finally the effect of transverse dispersion function can be included as $x = x + D_x \\frac{\\Delta p}{p}$ and $x' = x' + D'_x\\frac{\\Delta p}{p}$." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "x = x + Dx*dp; xp = xp + Dpx*dp\n", "y = y + Dy*dp; yp = yp + Dpy*dp" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Layout\n", " .Points.I :Points [x,xp]\n", " .Points.II :Points [y,yp]" ] }, "execution_count": 20, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "hv.Points((x*1e3,xp*1e3), kdims=[dim_x,dim_xp]) + hv.Points((y*1e3,yp*1e3), kdims=[dim_y,dim_yp])" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Points [dp,x]" ] }, "execution_count": 21, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "hv.Points((dp*100,x*1e3), kdims=[dim_dp,dim_x])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Laser beam" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To define the laser beam we define several parameters (assuming Gaussian beam):\n", "
    \n", "
  1. laser wavelength $\\lambda_l$,\n", "
  2. a vector to the laser focal point $\\boldsymbol{a}=(a_x,a_y,a_z)$,\n", "
  3. a vector of laser direction of propagation $\\boldsymbol{n}=(n_x,n_y,n_z)$,\n", "
  4. laser waist radius $w_0$,\n", "
  5. delay of the laser pulse with respect to the ion beam (i.e. the laser pulse reaches its focal point at the moment $t=t_l$).\n", "
\n", "\n", "We assume that the laser pulse length is much shorter than the length of the ion bunch (with this approach a long laser pulse can be represented as a sequence of several short pulses). In this case for every particle we can calculate the moment and location of its collision with the laser wavefront." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "# Let the laser beam collide with the ion beam at the center:\n", "\n", "ax = 0; ay = 0; az = 0 # m\n", "t_l = 0.0 # sec\n", "\n", "# i.e. some ions collide with the laser at t < 0,\n", "# before it reaches the center of the ion beam.\n", "# So the tracking should start at some t < 0." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We assume that the laser crosses the ion beam trajectory with an angle $\\theta_l$:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "#theta_l = 10.0*np.pi/180 # rad\n", "#theta_l = 5.0*np.pi/180 # rad\n", "theta_l = 2.6*np.pi/180 # rad\n", "#theta_l = 1.0*np.pi/180 # rad\n", "#theta_l = 0.1*np.pi/180 # rad" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then $n_x = 0$, $n_y = -\\sin\\theta_l$, $n_z = -\\cos\\theta_l$." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "nx = 0; ny = -np.sin(theta_l); nz = -np.cos(theta_l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And because of this angle the resonant wavelength of the laser should be shifted from $\\lambda_0$ as\n", "\n", "$$\n", "\\lambda_l = \\frac{\\lambda_0}{2}(1 + \\beta_0 \\cos\\theta_l).\n", "$$" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\lambda_l$ = 1034.0 nm" ], "text/plain": [ "" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lambda_l = (2*np.pi*hc/hw)*(1 + beta_0*np.cos(theta_l))/2 # m\n", "\n", "# Shift laser wavelength for fast longitudinal cooling\n", "lambda_l = lambda_l*(1+sigma_dp)\n", "\n", "Latex(r\"$\\lambda_l$ = %.1f nm\" % (lambda_l*1e9))" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "w_0 = 1.3e-3 # m\n", "#w_0 = 2.0e-3 # m\n", "#w_0 = 4.0e-3 # m\n", "#w_0 = 0.2e-3 # m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The corresponding Rayleigh length is\n", "$$\n", "z_R = \\frac{\\pi w_0^2} {\\lambda_l}.\n", "$$" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$z_R$ = 5.13 m" ], "text/plain": [ "" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z_R = np.pi*w_0*w_0/lambda_l\n", "Latex(\"$z_R$ = %.2f m\" % z_R)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "Divergence of the laser beam $\\theta = \\frac{\\lambda_l}{\\pi w_0} = 0.25$ mrad." ], "text/plain": [ "" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Latex(r\"Divergence of the laser beam $\\theta = \\frac{\\lambda_l}{\\pi w_0} = %.2f$ mrad.\" %\n", " (1e3*lambda_l/(np.pi*w_0)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Collision of the ion beam with the laser pulse" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The position of the laser beam center is $\\boldsymbol{r}_l = \\boldsymbol{a} + c(t-t_l)\\boldsymbol{n}$. We can find the moment when a particle with a position $\\boldsymbol{r} = \\boldsymbol{r}_0 + \\boldsymbol{v}t$ collides with the laser as the moment when $\\boldsymbol{r}-\\boldsymbol{r}_l$ is perpendicular to $\\boldsymbol{n}$. Then $(\\boldsymbol{r}-\\boldsymbol{r}_l, \\boldsymbol{n})=0$, which yields the equation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "(\\boldsymbol{r}_0, \\boldsymbol{n}) + (\\boldsymbol{v}, \\boldsymbol{n})t - (\\boldsymbol{a}, \\boldsymbol{n}) - c(t-t_l)(\\boldsymbol{n}, \\boldsymbol{n}) = 0.\n", "$$\n", "\n", "Therefore the moment of ion-photon collision is\n", "$$\n", "t_\\mathrm{col} = \\frac{(\\boldsymbol{r}_0 - \\boldsymbol{a}, \\boldsymbol{n}) + ct_l}{c - (\\boldsymbol{v}, \\boldsymbol{n})}.\n", "$$" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "c = 299792458 # m/s" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "vz = c*beta_0\n", "\n", "vx = vz*xp\n", "vy = vz*yp" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "t_col = ((x-ax)*nx + (y-ay)*ny + (z-az)*nz + c*t_l) / (c - (vx*nx+vy*ny+vz*nz)) # sec" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For every particle we can find the location of its collision with the laser pulse as $\\boldsymbol{r} = \\boldsymbol{r}_0 + \\boldsymbol{v}t$:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "x_col = x + vx*t_col # m\n", "y_col = y + vy*t_col # m\n", "z_col = z + vz*t_col # m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As a test let's plot the location of all ions which have already passed the laser pulse at $t=0$:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Points [z,y]" ] }, "execution_count": 33, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "hv.Points((z[t_col<0]*1e3, y[t_col<0]*1e3), kdims=[dim_z,dim_y])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we need to determine if the photon is absorbed and the ion is excited according to the laser beam intensity and angle at the location of the interaction point." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "At the moment of the ion collision with the laser pulse its intensity $I$ is given by the expression\n", "\n", "$$\n", "I = I_0 \\frac{w^2_0}{w^2} \\exp \\left(-\\frac{2(\\boldsymbol{r} - \\boldsymbol{r}_l)^2}{w^2}\\right),$$\n", "\n", "where $\\boldsymbol{r} - \\boldsymbol{r}_l$ is the distance to the laser center, $I_0$ is the maximum intensity at the focal point $\\boldsymbol{a}$, and the laser beam width $w$ is evolving with the distance to the focal point as\n", "\n", "$$\n", "w = w_0 \\sqrt{1 + \\left(\\frac{\\boldsymbol{r}_l - \\boldsymbol{a}}{z_R}\\right)^2}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also the radius of wavefront curvature $R$ can be written as\n", "\n", "$$\n", "R = |\\boldsymbol{r}_l - \\boldsymbol{a}| \\left[ 1 + \\left(\\frac{z_R}{\\boldsymbol{r}_l - \\boldsymbol{a}}\\right)^2 \\right].\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The total laser pulse energy $J_l$ can be integrated as\n", "\n", "$$\n", "J_l = \\Delta t_l \\int\\limits_{S} I dS =\n", "\\Delta t_l I_0 \\int\\limits_{0}^{\\infty} \\exp \\left(-\\frac{2r^2}{w_0^2}\\right) 2 \\pi rdr =\n", "\\Delta t_l \\frac{I_0 \\pi w_0^2}{2},\n", "$$\n", "\n", "where $\\Delta t_l$ is the laser pulse duration (assuming uniform temporal profile). The same expression assuming the Gaussian temporal pulse profile can be written as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "J_l = \\int\\limits_{-\\infty}^{+\\infty} \\exp \\left( -\\frac{t^2}{2\\sigma_t^2} \\right) dt \\int\\limits_{S} I dS =\n", "\\sqrt{2\\pi} \\sigma_t I_0 \\int\\limits_{0}^{\\infty} \\exp \\left(-\\frac{2r^2}{w_0^2}\\right) 2 \\pi rdr =\n", "\\sqrt{2\\pi} \\sigma_t \\frac{I_0 \\pi w_0^2}{2},\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To calculate the probabilistic ion excitation with a given process cross-section we only need the density of photons (and their incoming angle which is slightly varying due to the wavefront curvature) at the location of every ion. The total number of photons per pulse\n", "\n", "$$\n", "N_{\\hbar \\omega} = \\frac{J_l} {\\hbar \\omega}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "While the surface density of photons (number of photons per unit surface) is proportional to the laser intensity $I$:\n", "\n", "$$\n", "\\frac{dN_{\\hbar\\omega}}{dS} = \\frac{I\\Delta t_l}{\\hbar\\omega} = \\frac{2N_{\\hbar\\omega}}{\\pi w^2} \\exp \\left(-\\frac{2(\\boldsymbol{r} - \\boldsymbol{r}_l)^2}{w^2}\\right).\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Photon absorption cross-section" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The cross-section of the ion excitation by a photon with a frequency $\\omega'$ is given by\n", "\n", "$$\n", "\\sigma = 2\\pi^2 c r_e f_{12} g(\\omega' - \\omega'_0),\n", "$$\n", "\n", "where $r_e$ is classical electron radius, $f_{12}$ is the oscillator strength, $\\omega'_0$ is the resonance frequency of the ion transition, $g(\\omega'-\\omega'_0)$ is the Lorentzian" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "g(\\omega' - \\omega'_0) = \\frac{1}{2\\pi}\\cdot\\frac{\\Gamma}{(\\omega' - \\omega'_0)^2 + \\Gamma^2/4},\n", "$$\n", "\n", "where $\\Gamma$ is the resonance width defined by the lifetime of the excited ion $\\tau_0$:\n", "\n", "$$\n", "\\Gamma = \\frac{1}{\\tau_0}.\n", "$$\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also\n", "\n", "$$\n", "\\Gamma = 2 r_e \\omega_0'^2 f_{12}\\frac{g_1}{cg_2}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Therefore\n", "$$\n", "\\sigma(\\omega' - \\omega'_0) = \\frac{\\sigma_0}{1 + 4\\tau^2_0(\\omega' - \\omega'_0)^2},\n", "$$\n", "where\n", "$$\n", "\\sigma_0 = \\frac{\\lambda_0'^2 g_2}{2\\pi g_1}.\n", "$$" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\sigma_0$ = 4.59e-18 $\\mathrm{m}^2$ = 45.9 Gbarn" ], "text/plain": [ "" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sigma_0 = lambda_0*lambda_0*g2/(2*np.pi*g1)\n", "\n", "Latex(r\"$\\sigma_0$ = %.2e $\\mathrm{m}^2$ = %.1f Gbarn\" % (sigma_0, sigma_0/1e-28/1e9))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's plot $\\sigma$ as a function of relative frequency error $(\\omega' - \\omega'_0)/\\omega'_0$" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "omega_0 = 2*np.pi*c/lambda_0\n", "domega = np.linspace(-1e-7,+1e-7,201)*omega_0\n", "sigma = sigma_0/(1 + 4*(tau_0*domega)*(tau_0*domega))" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Curve [domega] (sigma)" ] }, "execution_count": 36, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "dim_sigma = hv.Dimension('sigma', label=\"$\\sigma/\\sigma_0$\", range=(0,1))\n", "dim_domega = hv.Dimension('domega', label=\"$(\\omega-\\omega_0)/\\omega_0$\")\n", "\n", "hv.Curve((domega/omega_0, sigma/sigma_0), kdims=[dim_domega], vdims=[dim_sigma])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we can estimate the energy of the laser pulse required to excite every ion in the bunch.\n", "\n", "If the laser wavelength perfectly matches the ion level we would need one photon per every $\\sigma_0$ coss-section i.e.\n", "\n", "$$\n", "N_{\\hbar \\omega_0} \\sim \\frac{w_0^2}{\\sigma_0}.\n", "$$ \n", "\n", "However since the momentum spread $\\Delta p/p$ in the ion bunch is larger than the width of the resonance we need $N_{\\hbar \\omega_0}$ photons for all possible frequencies, i.e.\n", "\n", "$$\n", "N_{\\hbar \\omega} \\sim N_{\\hbar \\omega_0}\\frac{\\Delta p}{p}\\bigg/\\frac{\\Gamma}{\\omega_0} \\sim \\frac{w_0^2}{\\sigma_0}\\frac{\\Delta p}{p}\\omega_0\\tau_0.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And the energy of the laser pulse should be\n", "\n", "$$\n", "J_l \\sim \\hbar\\omega \\frac{w_0^2}{\\sigma_0}\\frac{\\Delta p}{p}\\omega_0\\tau_0.\n", "$$\n", "\n", "This expression is an order of magnitude estimate neglecting geometric factors." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$N_{\\hbar\\omega} \\sim$ 2.0e+15, \\\n", " laser pulse energy $\\sim$ 0.4 mJ, \\\n", " laser average power $\\sim$ 16.5 W." ], "text/plain": [ "" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "N_hw = (w_0*w_0/sigma_0)*sigma_dp*omega_0*tau_0\n", "J_l = N_hw*hw*1.602e-19 # joules\n", "\n", "Latex(r\"$N_{\\hbar\\omega} \\sim$ %.1e, \\\n", " laser pulse energy $\\sim$ %.1f mJ, \\\n", " laser average power $\\sim$ %.1f W.\" %\n", " (N_hw, J_l*1e3, J_l/(L/c)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's set the specific laser pulse energy:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$N_{\\hbar\\omega} =$ 2.6e+16, \\\n", " laser pulse energy = 5.0 mJ, \\\n", " laser average power = 216.9 W." ], "text/plain": [ "" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "J_l = 5e-3 # joules\n", "N_hw = J_l/(hw*1.602e-19)\n", "\n", "Latex(r\"$N_{\\hbar\\omega} =$ %.1e, \\\n", " laser pulse energy = %.1f mJ, \\\n", " laser average power = %.1f W.\" %\n", " (N_hw, J_l*1e3, J_l/(L/c)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For a Gaussian (in time) beam there is a simple relation between the pulse duration $\\sigma_t$ and its frequency spread $\\sigma_\\omega$:\n", "\n", "$$\n", "\\sigma_t \\sigma_\\omega = 1.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Suppose we would like to cool the ion beam in the momentum space. For this we would need to interact only with the ions with $\\Delta p > 0$ and with a Gaussian beam one can use, for example, $\\sigma_\\omega/\\omega = 0.5 \\sigma_{\\Delta p/p}$ and the laser frequency shifted by $2\\sigma_\\omega$ from the resonanse frequency corresponding to the equillibrium ion momentum." ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "sigma_w = (c*hw/hc)*sigma_dp/2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then the pulse duration can be found as $\\sigma_t = 1/\\sigma_\\omega$" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "sigma_t = 1/sigma_w" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\sigma_t = $ 5.5 ps." ], "text/plain": [ "" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Latex(r\"$\\sigma_t = $ %.1f ps.\" % (sigma_t*1e12))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Peak power density $I_0$ in the center of this laser pulse\n", "\n", "$$\n", "I_0 = \\sqrt{\\frac{2}{\\pi}} \\frac{J_l}{\\sigma_t \\pi w_0^2},\n", "$$" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$I_0 = $ 136.83 TW/$\\mathrm{m}^2$ = 13.68 GW/$\\mathrm{cm}^2$." ], "text/plain": [ "" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "I_0 = np.sqrt(2.0/np.pi)*J_l/(sigma_t*np.pi*w_0*w_0)\n", "Latex(r\"$I_0 = $ %.2f TW/$\\mathrm{m}^2$ = %.2f GW/$\\mathrm{cm}^2$.\" % ((I_0*1e-12),(I_0*1e-9*1e-4)) )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using these estimates as a reference we can now do the detailed Monte Carlo simulation including all geometric factors." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Monte Carlo simulation of the ion bunch interaction with the laser pulse" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Above we have already determined the moment and location of collision of every ion with the laser pulse. Using the ion excitation cross-section, laser intensity and frequency distribution we can find the probability of ion excitation after the collision with the laser pulse." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Number of excitation events for every ion can be found as\n", "\n", "$$\n", "N_{exc} = \\frac{dN_{\\hbar \\omega}}{dS} \\bar\\sigma = \\frac{2N_{\\hbar\\omega}}{\\pi w^2} \\exp \\left(-\\frac{2(\\boldsymbol{r} - \\boldsymbol{r}_l)^2}{w^2}\\right) \\bar\\sigma,\n", "$$\n", "\n", "where $\\bar\\sigma$ is the excitation cross-section averaged over the laser frequency distribution. And as we've seen already $\\bar\\sigma \\ll \\sigma_0$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In our model here we assumed that if $N_{exc} \\ll 1$, then $N_{exc}$ is the probability of ion excitation. For $N_{exc} \\sim 0.1$ we should take into account the saturation effects. This can be done by solving the rate equation\n", "\n", "$$\n", "\\frac{dP_2}{dt} = m_2 P_1 - m_2 B P_2,\n", "$$\n", "\n", "where $P_2, P_1$ are the probabilities of ion to be in the excited and non-excited state ($P_1 + P_2 = 1$). The rate of excitation events $m_2 P_1$ is proportional to the population of lower level $P_1$, photon density, and the absorption cross-section, $m_2 B P_2$ is the rate of stimulated emission events ($B = g_1/g_2$). Before ion enters into the laser pulse $P_1 = 1$ and $P_2 = 0$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To solve the rate equation we need to separate the variables:\n", "\n", "$$\n", "\\frac{dP_2}{1 - (1 + B)P_2} = m_2 dt.\n", "$$\n", "\n", "$m_2$ depends on time during the passage of the ion through the laser pulse, but we already know the answer in the case of small $P_2 = N_{exc} \\ll 1$, hence\n", "\n", "$$\n", "\\int\\limits_t m_2(t) dt = \\frac{dN_{\\hbar \\omega}}{dS} \\bar\\sigma.\n", "$$\n", "\n", "(In the general case of a long laser pulse maybe we would need to keep the $P_2$ number during the slice-by-slice integration of ion motion through the laser pulse)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Therefore the result of integration\n", "\n", "$$\n", "N_{exc} = P_2 = \\frac{1 - \\exp\\left[-(1+B)\\int\\limits_t m_2(t) dt \\right]}{1+B} =\n", "\\frac{1 - \\exp\\left[-(1+B)\\frac{dN_{\\hbar \\omega}}{dS} \\bar\\sigma \\right]}{1+B}.\n", "$$\n", "\n", "As we can see while $\\frac{dN_{\\hbar \\omega}}{dS} \\bar\\sigma$ is small it equals $N_{exc}$, while for large $\\frac{dN_{\\hbar \\omega}}{dS} \\bar\\sigma$ the result is limited by $1/(1+B)$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "More accurate model can also include the time-dependent effects of Rabi oscillations -- to be done in the future." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Excitation cross section averaged over the whole laser spectrum will depend on the energy of the ion and on the angle of collision between the ion and the laser pulse.\n", "\n", "$$\n", "\\bar\\sigma = \\int\\limits_{-\\infty}^{+\\infty} \\sigma(\\omega') f(\\omega')d\\omega' = \\int\\limits_{-\\infty}^{+\\infty} \\frac{\\sigma_0}{1 + 4\\tau_0^2\\left( \\omega' - \\omega_0' \\right)^2} f(\\omega')d\\omega'\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If laser frequency distribution $f(\\omega')$ is much wider than the width of the resonance line then we can simply replace $f(\\omega')$ with $f(\\omega_0')$ and\n", "\n", "$$\n", "\\bar\\sigma \\approx \\sigma_0 f(\\omega_0') \\int\\limits_{-\\infty}^{+\\infty} \\frac{d\\omega'}{1 + 4\\tau_0^2\\left( \\omega' - \\omega_0' \\right)^2} = \\sigma_0 f(\\omega_0') \\frac{\\pi}{2\\tau_0}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Gaussian frequency distribution in the laser pulse with the main frequency $\\omega_l$ is written as\n", "\n", "$$\n", "f(\\omega) = \\frac{1}{\\sigma_\\omega\\sqrt{2\\pi}}\\exp\\left[-\\frac{(\\omega - \\omega_l)^2}{2\\sigma_\\omega^2}\\right],\n", "$$\n", "\n", "therefore" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\bar{\\sigma} \\approx \\sigma_0 \\frac{1}{\\sigma_{\\omega'}\\sqrt{2\\pi}}\\exp\\left[-\\frac{(\\omega_0' - \\omega_l')^2}{2\\sigma_{\\omega'}^2}\\right] \\frac{\\pi}{2\\tau_0}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Doppler effect $\\omega' = \\gamma (1 + \\beta \\cos\\theta) \\omega$ applied to $\\sigma_\\omega = 1/\\sigma_t$ and $\\omega_l$ introduces the dependency on the ion momentum\n", "\n", "$$\n", "\\sigma_{\\omega'} = \\gamma \\frac{1 + \\beta \\cos\\theta} {\\sigma_t},\n", "$$\n", "\n", "$$\n", "\\omega_l' = \\gamma (1 + \\beta \\cos\\theta) \\omega_l,\n", "$$\n", "\n", "where $\\theta = \\theta_l + \\delta\\theta$, and $\\delta\\theta$ is the additional small angle due to the anglular spread in the ion beam and due to the curvature of the laser wavefront." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\bar{\\sigma} \\approx \\sigma_0 \\frac{\\sigma_t}{\\gamma \\tau_0 (1 + \\beta\\cos\\theta)} \\cdot \\frac{\\sqrt{2\\pi}}{4} \\exp\\left[-\\frac{(\\omega_0' - \\omega_l')^2}{2\\sigma_{\\omega'}^2}\\right].\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the SPS $\\delta\\theta \\sim 0.01~$mrad and its effect is important only at the final stage of extreme ion cooling in the SPS when the energy spread is reduced from $\\Delta p/p\\sim10^{-4}$ to $10^{-6}$ or less. We can put $\\theta = \\theta_l$ for now." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In our case $\\sigma_t \\ll \\gamma\\tau_0$, which leads to $\\bar\\sigma \\ll \\sigma_0$:" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\sigma_t = $ 5.5 ps, while $\\gamma\\tau_0 = $ 7375.6 ps." ], "text/plain": [ "" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Latex(r\"$\\sigma_t = $ %.1f ps, while $\\gamma\\tau_0 = $ %.1f ps.\" % (sigma_t*1e12, gamma_0*tau_0*1e12))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can calculate the excitation probability for every ion in the bunch." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We define Doppler factor $f_D = \\gamma (1 + \\beta \\cos\\theta)$." ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "f_D = gamma_0*(1+dp)*(1+beta_0*np.cos(theta_l))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then $\\omega_l'$ " ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "omega_l = 2*np.pi/(lambda_l/c)*f_D" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [], "source": [ "sigma_average = sigma_0*sigma_t/(tau_0*f_D) * \\\n", " np.sqrt(2*np.pi)/4 * \\\n", " np.exp(-np.power( (omega_0 - omega_l)/(sigma_w*f_D) , 2)/2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Position of the laser center at the time of collision $\\boldsymbol{r}_l = \\boldsymbol{a} + c(t_{\\mathrm{col}}-t_l)\\boldsymbol{n}$:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "x_l = ax + c*(t_col-t_l)*nx\n", "y_l = ay + c*(t_col-t_l)*ny\n", "z_l = az + c*(t_col-t_l)*nz" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Distance to the laser center r2 = $(\\boldsymbol{r} - \\boldsymbol{r}_l)^2$" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [], "source": [ "r2 = (x_col-x_l)*(x_col-x_l) + (y_col-y_l)*(y_col-y_l) + (z_col-z_l)*(z_col-z_l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's find the laser pulse width\n", "\n", "$$\n", "w = w_0 \\sqrt{1 + \\left(\\frac{\\boldsymbol{r}_l - \\boldsymbol{a}}{z_R}\\right)^2}.\n", "$$" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [], "source": [ "w = w_0*np.sqrt( 1 + ((x_l-ax)*(x_l-ax) + (y_l-ay)*(y_l-ay) + (z_l-az)*(z_l-az))/(z_R*z_R) )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And finally number of excitation events for every ion can be found as\n", "\n", "$$\n", "\\frac{dN_{\\hbar \\omega}}{dS} \\bar\\sigma = \\frac{2N_{\\hbar\\omega}}{\\pi w^2} \\exp \\left(-\\frac{2(\\boldsymbol{r} - \\boldsymbol{r}_l)^2}{w^2}\\right) \\bar\\sigma,\n", "$$\n", "\n", "$$\n", "N_{exc} = \\frac{1 - \\exp\\left[-(1+B)\\frac{dN_{\\hbar \\omega}}{dS} \\bar\\sigma \\right]}{1+B}.\n", "$$" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [], "source": [ "N_exc = 2*N_hw/(np.pi*w*w)*np.exp(-2*r2/w/w)*sigma_average\n", "N_exc = (1 - np.exp(-1*(1+g1/g2)*N_exc))/(1+g1/g2)" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Points [z,N_exc]" ] }, "execution_count": 51, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "#%output backend='matplotlib' fig='png' size=250\n", "#%opts Points [aspect=3]\n", "\n", "hv.Points((z*1e3, N_exc), [dim_z,\"N_exc\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since we treat $N_{exc}$ as the probability of ion excitation, let's randomply select excited atoms according to their $N_{exc}$ value:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For every ion select a random number from 0 to 1:" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [], "source": [ "rnd = np.random.uniform(size=Np)" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Points [z,rnd]" ] }, "execution_count": 53, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "hv.Points((z*1e3, rnd), [dim_z,\"rnd\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And we count the ion as excited if its N_exc value is larger than the rnd value:" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [], "source": [ "Excited = N_exc > rnd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plot excited ions:" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [], "source": [ "%opts Points.Excited (alpha=0.8, color='black')\n", "%opts Points.Still (alpha=0.3)" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Overlay\n", " .Still.I :Points [z,y]\n", " .Excited.I :Points [z,y]" ] }, "execution_count": 56, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "hv.Points((z*1e3, y*1e3), [dim_z,dim_y], group='Still') * \\\n", "hv.Points((z[Excited]*1e3, y[Excited]*1e3), [dim_z,dim_y], group='Excited')" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Overlay\n", " .Still.I :Points [z,dp]\n", " .Excited.I :Points [z,dp]" ] }, "execution_count": 57, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "hv.Points((z*1e3, dp*100), [dim_z,dim_dp], group='Still') * \\\n", "hv.Points((z[Excited]*1e3, dp[Excited]*100), [dim_z,dim_dp], group='Excited')" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "13.5% of all ions are excited.\n" ] } ], "source": [ "print(\"%.1f%% of all ions are excited.\" % (100*len(z[Excited])/len(z)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Photon emissions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The effect of photon absorption on the ion momentum in the lab frame can be neglected. We should take into account only the emitted photon. On every turn we will select a random direction of photon emission in the ion's frame of reference. The random polar angle $\\theta_1'$ can be obtained as arccos of uniformly distributed random number from $-1$ to $+1$. Then using the above expressions we can find the frequency of the scattered photon as well as $\\theta_1$ angle between the initial ion momentum and the direction of photon emission in the lab frame. Finally a random azimuthal angle (from $0$ to $2\\pi$) is needed to get the direction of photon scattering in the plane which is perpendicular to the initial ion momentum." ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [], "source": [ "# random polar angle in the ion's frame:\n", "costheta = np.random.uniform(-1,+1, size=Np)\n", "theta1p = np.arccos(costheta)\n", "\n", "# emitted photon energy in the lab frame:\n", "hw1_emitted = gamma_0*(1+dp) * ( 1 + beta_0*np.cos(theta1p) )*hw0 # eV\n", "# only excited atoms are emitting of course:\n", "hw1_emitted = hw1_emitted*Excited\n", "\n", "dp = dp - hw1_emitted/(gamma_0*mc)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Overlay\n", " .Still.I :Points [z,dp]\n", " .Excited.I :Points [z,dp]" ] }, "execution_count": 60, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "hv.Points((z*1e3, dp*100), [dim_z,dim_dp], group='Still') * \\\n", "hv.Points((z[Excited]*1e3, dp[Excited]*100), [dim_z,dim_dp], group='Excited')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's summarize the above theory into the python function to simulate the process of ion excitations and photon emissions in a turn-by-turn simulation." ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [], "source": [ "X = np.matrix([\n", " x ,\n", " xp,\n", " y ,\n", " yp,\n", " z ,\n", " dp\n", "])" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [], "source": [ "def ExciteIons(X):\n", " # Returns a vector of ion states after interaction with the laser: Excited / Not excited.\n", "\n", " x = X[0].A1; xp = X[1].A1; y = X[2].A1; yp = X[3].A1; z = X[4].A1; dp = X[5].A1\n", " \n", " vz = c*beta_0\n", "\n", " vx = vz*xp\n", " vy = vz*yp\n", " \n", " t_col = ((x-ax)*nx + (y-ay)*ny + (z-az)*nz + c*t_l) / (c - (vx*nx+vy*ny+vz*nz)) # sec\n", " \n", " x_col = x + vx*t_col # m\n", " y_col = y + vy*t_col # m\n", " z_col = z + vz*t_col # m\n", " \n", " f_D = gamma_0*(1+dp)*(1+beta_0*np.cos(theta_l))\n", " omega_l = 2*np.pi/(lambda_l/c)*f_D\n", " sigma_average = sigma_0*sigma_t/(tau_0*f_D) * \\\n", " np.sqrt(2*np.pi)/4 * \\\n", " np.exp(-np.power( (omega_0 - omega_l)/(sigma_w*f_D) , 2)/2)\n", " \n", " x_l = ax + c*(t_col-t_l)*nx\n", " y_l = ay + c*(t_col-t_l)*ny\n", " z_l = az + c*(t_col-t_l)*nz\n", " \n", " r2 = (x_col-x_l)*(x_col-x_l) + (y_col-y_l)*(y_col-y_l) + (z_col-z_l)*(z_col-z_l)\n", " \n", " w = w_0*np.sqrt( 1 + ((x_l-ax)*(x_l-ax) + (y_l-ay)*(y_l-ay) + (z_l-az)*(z_l-az))/(z_R*z_R) )\n", " \n", " N_exc = 2*N_hw/(np.pi*w*w)*np.exp(-2*r2/w/w)*sigma_average\n", " N_exc = (1 - np.exp(-1*(1+g1/g2)*N_exc))/(1+g1/g2)\n", " \n", " rnd = np.random.uniform(size=Np)\n", " \n", " Excited = N_exc > rnd\n", " \n", " return Excited" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Overlay\n", " .Still.I :Points [z,dp]\n", " .Excited.I :Points [z,dp]" ] }, "execution_count": 63, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "Excited = ExciteIons(X)\n", "\n", "hv.Points((z*1e3, dp*100), [dim_z,dim_dp], group='Still') * \\\n", "hv.Points((z[Excited]*1e3, dp[Excited]*100), [dim_z,dim_dp], group='Excited')" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [], "source": [ "def EmitPhotons(X, Excited):\n", " # Returns vector of emitted photons and vector X after photon emissions\n", " \n", " x = X[0].A1; xp = X[1].A1; y = X[2].A1; yp = X[3].A1; z = X[4].A1; dp = X[5].A1\n", " \n", " Np_Excited = len(x[Excited])\n", " \n", " # random polar angle in the ion's frame:\n", " cos_theta1p = np.random.uniform(-1,+1, size=Np_Excited)\n", " theta1p = np.arccos(cos_theta1p)\n", " \n", " # emission angle in the lab frame:\n", " sin_theta1 = np.sin(theta1p)/(gamma_0 * (1+dp[Excited]) * ( 1 + beta_0*np.cos(theta1p) ))\n", " theta1 = np.arcsin(sin_theta1)\n", " \n", " # azimuthal angle of emission:\n", " phi1 = np.random.uniform(0,2*np.pi, size=Np_Excited)\n", "\n", " # emitted photon energy in the lab frame:\n", " hw1_emitted = gamma_0*(1+dp[Excited]) * ( 1 + beta_0*np.cos(theta1p) )*hw0 # eV\n", "\n", "\n", " # duration of time between the ion excitation and photon emission (in the lab frame):\n", " dt_excited = gamma_0*np.random.exponential(scale=tau_0, size=Np_Excited) # seconds\n", "\n", " # distance travelled by the ion between its excitation and photon emission:\n", " ds = dt_excited*c # meters\n", "\n", " # emitted photon location:\n", " s_photon = z[Excited] + ds\n", " x_photon = x[Excited] + ds*xp[Excited]\n", " y_photon = y[Excited] + ds*yp[Excited]\n", "\n", " p_photon = hw1_emitted # eV/c\n", " pz_photon = p_photon*np.cos(theta1)\n", " pt_photon = p_photon*np.sin(theta1) # photon transverse momentum\n", " px_photon = pt_photon*np.cos(phi1)\n", " py_photon = pt_photon*np.sin(phi1)\n", " \n", " xp_photon = px_photon/pz_photon # rad\n", " yp_photon = py_photon/pz_photon # rad\n", " \n", " # The effect of photon emission on ion is noticeble only in the dp value (see earlier explanation):\n", " dp[Excited] = dp[Excited] - hw1_emitted/(gamma_0*mc)\n", " \n", " Photons = np.matrix([\n", " x_photon,\n", " xp_photon,\n", " y_photon,\n", " yp_photon,\n", " s_photon,\n", " p_photon\n", " ])\n", " \n", " X1 = np.matrix([\n", " x ,\n", " xp,\n", " y ,\n", " yp,\n", " z ,\n", " dp\n", " ])\n", " \n", " return X1, Photons" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [], "source": [ "X1, Photons = EmitPhotons(X, Excited)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Layout\n", " .Points.I :Points [s,x]\n", " .Points.II :Points [xp_photon,x]" ] }, "execution_count": 66, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "#%output backend='matplotlib' fig='png' size=150\n", "#%opts Points [aspect=2]\n", "\n", "x_photon = Photons[0].A1\n", "xp_photon = Photons[1].A1\n", "s_photon = Photons[4].A1\n", "p_photon = Photons[5].A1\n", "\n", "dim_s = hv.Dimension('s', unit='m')# , range=(-600.0,+600.0))\n", "dim_xp_photon = hv.Dimension('xp_photon', unit='mrad', label=\"x'\", range=(-100,+100))\n", "\n", "hv.Points((s_photon,x_photon*1e3), [dim_s,dim_x]) + \\\n", "hv.Points((xp_photon*1e3,x_photon*1e3), [dim_xp_photon,dim_x])" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Layout\n", " .Points.I :Points [s,p_photon]\n", " .Points.II :Points [xp_photon,p_photon]" ] }, "execution_count": 67, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "dim_p_photon = hv.Dimension('p_photon', unit='keV', label=\"Photon energy\") #, range=(0,None))\n", "\n", "hv.Points((s_photon,p_photon/1e3), [dim_s,dim_p_photon]) + \\\n", "hv.Points((xp_photon*1e3,p_photon/1e3), [dim_xp_photon,dim_p_photon])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And now we can do the full 1-turn tracking of the ion beam." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Transverse 1-turn matrix\n", "\n", "The expressions will be written for the general coupled case (in x-y) but with matrix elements simplified for the uncoupled case. 1-turn matrix in the uncoupled case can be expressed in the Twiss parametrization." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Full 1-turn matrix (excluding RF-effects) can be written as\n", "\n", "$$\n", "\\begin{pmatrix}\n", "x \\\\\n", "x' \\\\\n", "y \\\\\n", "y' \\\\\n", "\\Delta l \\\\\n", "\\Delta p/p\n", "\\end{pmatrix}_{n+1} =\n", "\\begin{pmatrix}\n", "R_{11} & R_{12} & R_{13} & R_{14} & 0 & R_{16} \\\\\n", "R_{21} & R_{22} & R_{23} & R_{24} & 0 & R_{26} \\\\\n", "R_{31} & R_{32} & R_{33} & R_{34} & 0 & R_{36} \\\\\n", "R_{41} & R_{42} & R_{43} & R_{44} & 0 & R_{46} \\\\\n", "R_{51} & R_{52} & R_{53} & R_{54} & 1 & R_{56} \\\\\n", "0 & 0 & 0 & 0 & 0 & 1\n", "\\end{pmatrix}\n", "\\begin{pmatrix}\n", "x \\\\\n", "x' \\\\\n", "y \\\\\n", "y' \\\\\n", "0 \\\\\n", "\\Delta p/p\n", "\\end{pmatrix}_n,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where $n$ is the turn number." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the notation used here $\\Delta l$ is the path length difference between the trajectory of the particle with these particular coordinates and the central particle. $\\Delta l$ can be used to find the longitudinal particle coordinate along the bunch $z$ (used earlier). Since particles with different momentum move with different velocity\n", "\n", "$$\n", "z_{n+1} = z_n - \\Delta l_{n+1} + T_s \\Delta \\upsilon,\n", "$$\n", "\n", "where $T_s$ is the revolution period and\n", "\n", "$$\n", "\\Delta \\upsilon = \\upsilon_s \\frac{1}{\\gamma^2}\\frac{\\Delta p}{p},\n", "$$\n", "\n", "where $\\upsilon_s$ is the reference ion velocity. Therefore \n", "\n", "$$\n", "z_{n+1} = z_n - \\Delta l_{n+1} + T_s \\upsilon_s \\frac{1}{\\gamma^2}\\frac{\\Delta p}{p} = z_n - \\Delta l_{n+1} + \\frac{L}{\\gamma^2}\\frac{\\Delta p}{p},\n", "$$\n", "\n", "where $T_s \\upsilon_s = L$ is the ring circumference." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we can replace $\\Delta l$ with $z$ in the above matrix 1-turn expression:\n", "\n", "$$\n", "\\begin{pmatrix}\n", "x \\\\\n", "x' \\\\\n", "y \\\\\n", "y' \\\\\n", "z \\\\\n", "\\Delta p/p\n", "\\end{pmatrix}_{n+1} =\n", "\\begin{pmatrix}\n", "R_{11} & R_{12} & R_{13} & R_{14} & 0 & R_{16} \\\\\n", "R_{21} & R_{22} & R_{23} & R_{24} & 0 & R_{26} \\\\\n", "R_{31} & R_{32} & R_{33} & R_{34} & 0 & R_{36} \\\\\n", "R_{41} & R_{42} & R_{43} & R_{44} & 0 & R_{46} \\\\\n", "-R_{51} & -R_{52} & -R_{53} & -R_{54} & 1 & -R_{56} + L\\gamma^{-2} \\\\\n", "0 & 0 & 0 & 0 & 0 & 1\n", "\\end{pmatrix}\n", "\\begin{pmatrix}\n", "x \\\\\n", "x' \\\\\n", "y \\\\\n", "y' \\\\\n", "z \\\\\n", "\\Delta p/p\n", "\\end{pmatrix}_n.\n", "$$ " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Effects of RF-resonators should be included separately because in our case the longitudinal nonlinearity of RF is often important." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The expression looks more simple in the uncoupled case:\n", "\n", "$$\n", "\\begin{pmatrix}\n", "x \\\\\n", "x' \\\\\n", "y \\\\\n", "y' \\\\\n", "z \\\\\n", "\\Delta p/p\n", "\\end{pmatrix}_{n+1} =\n", "\\begin{pmatrix}\n", "R_{11} & R_{12} & 0 & 0 & 0 & R_{16} \\\\\n", "R_{21} & R_{22} & 0 & 0 & 0 & R_{26} \\\\\n", "0 & 0 & R_{33} & R_{34} & 0 & 0 \\\\\n", "0 & 0 & R_{43} & R_{44} & 0 & 0 \\\\\n", "-R_{51} & -R_{52} & 0 & 0 & 1 & -R_{56} + L\\gamma^{-2} \\\\\n", "0 & 0 & 0 & 0 & 0 & 1\n", "\\end{pmatrix}\n", "\\begin{pmatrix}\n", "x \\\\\n", "x' \\\\\n", "y \\\\\n", "y' \\\\\n", "z \\\\\n", "\\Delta p/p\n", "\\end{pmatrix}_n.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since the closed orbit with some momentum offset is given by\n", "\n", "$$\n", "\\begin{pmatrix}\n", "x \\\\\n", "x'\n", "\\end{pmatrix} =\n", "\\begin{pmatrix}\n", "D_x \\\\\n", "D'_x\n", "\\end{pmatrix}\n", "\\frac{\\Delta p}{p},\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "we can find the $R_{16}$ and $R_{26}$ matrix elements from the vales of dispersion functions defined earlier\n", "\n", "$$\n", "\\begin{pmatrix}\n", "D_x \\\\\n", "D'_x\n", "\\end{pmatrix}\n", "\\frac{\\Delta p}{p} =\n", "\\begin{pmatrix}\n", "R_{11} & R_{12} \\\\\n", "R_{21} & R_{22}\n", "\\end{pmatrix}\n", "\\begin{pmatrix}\n", "D_x \\\\\n", "D'_x\n", "\\end{pmatrix}\n", "\\frac{\\Delta p}{p} +\n", "\\begin{pmatrix}\n", "R_{16} \\\\\n", "R_{26} \\\\\n", "\\end{pmatrix}\n", "\\frac{\\Delta p}{p}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "therefore\n", "\n", "$$\n", "\\begin{pmatrix}\n", "R_{16} \\\\\n", "R_{26}\n", "\\end{pmatrix} = \n", "\\begin{pmatrix}\n", "D_x \\\\\n", "D'_x\n", "\\end{pmatrix} -\n", "\\begin{pmatrix}\n", "R_{11} & R_{12} \\\\\n", "R_{21} & R_{22}\n", "\\end{pmatrix}\n", "\\begin{pmatrix}\n", "D_x \\\\\n", "D'_x\n", "\\end{pmatrix}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the uncoupled case the 1-turn matrix can be written in Twiss as\n", "\n", "$$\n", "\\begin{pmatrix}\n", "R_{11} & R_{12} \\\\\n", "R_{21} & R_{22} \\\\\n", "\\end{pmatrix} = \n", "\\begin{pmatrix}\n", "\\cos\\mu_x + \\alpha_x\\sin\\mu_x & \\beta_x \\sin\\mu_x \\\\\n", "-\\gamma_x \\sin\\mu_x & \\cos\\mu_x - \\alpha_x \\sin\\mu_x \\\\\n", "\\end{pmatrix},\n", "$$\n", "\n", "where $\\mu_x = 2\\pi\\nu_x$ ($\\nu_x$ -- betatron tune), $\\alpha_x = -\\beta'_x / 2$, $\\gamma_x = (1 + \\alpha_x^2)/\\beta_x$.\n", "\n", "And the same for the y-plane." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The matrix elements $R_{51}$ and $R_{52}$ can be found from the symplecticity of the full-turn matrix $M$ as described in Analytical Tools in Accelerator Physics by V. Litvinenko (page 43, equation L3-53):\n", "\n", "$$\n", "-R_{51} = R_{16}R_{21} - R_{26}R_{11}, \\\\\n", "-R_{52} = R_{16}R_{22} - R_{26}R_{12}.\n", "$$" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [], "source": [ "# For the SPS Q26 optics the betatron tunes are:\n", "\n", "nux = 26.12999969\n", "nuy = 26.17999991\n", "\n", "mux = 2*np.pi*nux\n", "muy = 2*np.pi*nuy\n", "\n", "R11 = np.cos(mux) + alpha_x*np.sin(mux); R12 = beta_x*np.sin(mux)\n", "R21 = -(1+alpha_x*alpha_x)*np.sin(mux)/beta_x; R22 = np.cos(mux) - alpha_x*np.sin(mux)\n", "\n", "R33 = np.cos(muy) + alpha_y*np.sin(muy); R34 = beta_y*np.sin(muy)\n", "R43 = -(1+alpha_y*alpha_y)*np.sin(muy)/beta_y; R44 = np.cos(muy) - alpha_y*np.sin(muy)\n", "\n", "R16 = Dx - (Dx*R11 + Dpx*R12)\n", "R26 = Dpx - (Dx*R21 + Dpx*R22)\n", "\n", "R51 = R26*R11 - R16*R21\n", "R52 = R26*R12 - R16*R22\n", "\n", "gamma_t = 22.77422909 # gamma transition in the ring\n", "\n", "R56 = L/(gamma_t*gamma_t)" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [], "source": [ "M = np.matrix([\n", " [ R11, R12, 0 , 0 , 0 , R16],\n", " [ R21, R22, 0 , 0 , 0 , R26],\n", " [ 0 , 0 , R33, R34, 0 , 0 ],\n", " [ 0 , 0 , R43, R44, 0 , 0 ],\n", " [-R51, -R52, 0 , 0 , 1 , -R56+L/(gamma_0*gamma_0)],\n", " [ 0 , 0 , 0 , 0 , 0 , 1 ]\n", "])" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-4.346e-01 +3.981e+01 +0.000e+00 +0.000e+00 +0.000e+00 -3.754e-01 \n", "\n", "-4.481e-02 +1.804e+00 +0.000e+00 +0.000e+00 +0.000e+00 +3.116e-02 \n", "\n", "+0.000e+00 +0.000e+00 +1.615e+00 +4.011e+01 +0.000e+00 +0.000e+00 \n", "\n", "+0.000e+00 +0.000e+00 -5.566e-02 -7.633e-01 +0.000e+00 +0.000e+00 \n", "\n", "+3.036e-02 -1.918e+00 +0.000e+00 +0.000e+00 +1.000e+00 -1.258e+01 \n", "\n", "+0.000e+00 +0.000e+00 +0.000e+00 +0.000e+00 +0.000e+00 +1.000e+00 \n", "\n" ] } ], "source": [ "for row in M:\n", " for itm in row.A1:\n", " print(\"%+.3e \" % itm, end=\"\")\n", " print(\"\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " From SPS_optics:\n", "\n", "
\n",
    "\n",
    "-4.346e-01 +3.981e+01 +0.000e+00 +0.000e+00 +0.000e+00 -3.754e-01 \n",
    "\n",
    "-4.481e-02 +1.804e+00 +0.000e+00 +0.000e+00 +0.000e+00 +3.116e-02 \n",
    "\n",
    "+0.000e+00 +0.000e+00 +1.615e+00 +4.011e+01 +0.000e+00 +0.000e+00 \n",
    "\n",
    "+0.000e+00 +0.000e+00 -5.566e-02 -7.633e-01 +0.000e+00 +0.000e+00 \n",
    "\n",
    "+3.036e-02 -1.918e+00 +0.000e+00 +0.000e+00 +1.000e+00 -1.318e+01 \n",
    "\n",
    "+0.000e+00 +0.000e+00 +0.000e+00 +0.000e+00 +0.000e+00 +1.000e+00 \n",
    "\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can quickly test that the 1-turn transformation is not changing the beam distribution:\n", "\n", "Initial distribution:" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [], "source": [ "def plot_xxp_yyp(X):\n", " x = X[0].A1 # .A1 converts matrix into 1D-array\n", " xp = X[1].A1\n", " y = X[2].A1\n", " yp = X[3].A1\n", " \n", " return hv.Points((x*1e3,xp*1e3), kdims=[dim_x,dim_xp]) + hv.Points((y*1e3,yp*1e3), kdims=[dim_y,dim_yp])" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Layout\n", " .Points.I :Points [x,xp]\n", " .Points.II :Points [y,yp]" ] }, "execution_count": 72, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "plot_xxp_yyp(X)" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Layout\n", " .Points.I :Points [x,xp]\n", " .Points.II :Points [y,yp]" ] }, "execution_count": 73, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "# test one turn:\n", "X1 = M*X\n", "# test many turns\n", "#X1 = M*M*M*M*M*M*M*M*M*M*M*M*M*M*M*M*M*M*M*X\n", "\n", "plot_xxp_yyp(X1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Effects of RF-resonator" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The transverse cooling happens because RF-resonator restores only the longitudinal component $p_z$ of the ion momentum, while the photon emission reduces the full momentum $p$. So, in order to simulate the transverse radiative cooling we need to take into account the resonator effect not only on $\\Delta p/p$ but also on the angles $x'$ and $y'$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Longitudinal momentum gain of the ion after it has passed through the RF-resonator depends on the ion phase with respect to the RF:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\frac{dp_z}{dt} = e(Z-N_e)E_{\\rm{RF}}\\cos\\phi,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where $E_{\\rm{RF}}$ is the accelerating electric field and $\\phi$ is the ion phase in the RF resonator. The resulting longitudinal momentum change:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\delta p_z = e(Z-N_e)\\frac{ V_{\\rm{RF}} }{ L_{\\rm{RF}}} (\\cos\\phi) \\Delta t = e(Z-N_e)\\frac{ V_{\\rm{RF}} }{\\upsilon_z} \\cos\\phi,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where $V_{\\rm{RF}}$ is the RF-voltage, $\\upsilon_z$ is the ion velocity." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since $x' = p_x/p_z$ and $p_x$ is not changed in the resonator the angle $x'$ after the resonator" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "x' + \\delta x' = \\frac{p_x}{p_z + \\delta p_z} = x' \\left ( 1+\\frac{\\delta p_z} {p_z} \\right )^{-1}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "RF-resonator frequency $f_{\\rm{RF}}$ is some high harmonic $h$ of ion revolution frequency:\n", "\n", "$$\n", "f_{\\rm{RF}} = \\frac{h}{T_s},\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Longitudinal coordinate $z$ gives the longitudinal distance from the ion to the reference particle at the moment when the reference particle arrives at the RF-phase $\\phi_0$ (which is always the same). So the ion then arrives to the RF-resonator after the time\n", "\n", "$$\n", "\\Delta T = -\\frac{z}{\\upsilon_z} \\approx -\\frac{z}{\\upsilon_s}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then the ion phase in the RF-resonator is\n", "\n", "$$\n", "\\phi = \\phi_0 + 2\\pi f_{\\rm{RF}}\\Delta T = \\phi_0 - 2\\pi \\frac{hz}{T_s\\upsilon_z} \\approx \\phi_0 - 2\\pi \\frac{hz}{L}.\n", "$$" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [], "source": [ "# Pass beam through the RF-resonator:\n", "\n", "def RFcavity(X, h, eVrf, phi0):\n", " # returns vector X after RF-cavity\n", "\n", " x = X[0].A1; xp = X[1].A1; y = X[2].A1; yp = X[3].A1; z = X[4].A1; dp = X[5].A1\n", " \n", " p = p0 + p0*dp # eV/c\n", " # since p*p = px*px + py*py + pz*pz, and px = pz*x', py=pz*y':\n", " pz = p/np.sqrt(1+xp*xp+yp*yp)\n", " px = pz*xp\n", " py = pz*yp\n", "\n", " v0 = c/np.sqrt(1 + mc*mc/(p0*p0))\n", " v = c/np.sqrt(1 + mc*mc/(p*p))\n", " vz = v*pz/p\n", " \n", " # phase in the resonator: \n", " phi = phi0 - 2*np.pi*h*(z/L)*v0/vz\n", "\n", " pz = pz + eVrf*(Z-Ne)*np.cos(phi)/beta_0 # pz after RF-cavity \n", " \n", " xp = px/pz\n", " yp = py/pz\n", " \n", " p = np.sqrt(px*px+py*py+pz*pz)\n", " dp = (p-p0)/p0 # new relative momentum deviation\n", " \n", " return np.matrix([\n", " x ,\n", " xp,\n", " y ,\n", " yp,\n", " z ,\n", " dp\n", " ])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multi-turn tracking" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we can check if our implementation of RF-cavity is changing the transverse beam emittance (because we've included higher-order effects)." ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "turn = 100000 (100 %)" ] } ], "source": [ "N_turns = 100000\n", "N_plots = 11\n", "\n", "h = 4620 # SPS 200 MHz\n", "eVrf = 7e6 # eV\n", "phi0 = np.pi/2\n", "\n", "t_plots = np.arange(0,N_turns+1,int(N_turns/(N_plots-1)))\n", "\n", "X_plots = {}\n", "\n", "X1 = X;\n", "for turn in range(0,N_turns+1):\n", " if turn in t_plots:\n", " print( \"\\rturn = %g (%g %%)\" % (turn, (100*turn/N_turns)), end=\"\")\n", " X_plots[turn] = X1\n", " X1 = RFcavity(X1, h, eVrf, phi0)\n", " X1 = M*X1" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Area [z] (I)" ] }, "execution_count": 76, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "# function to calculate beam current profile:\n", "def get_I(z, z_bin = 5e-3, z_min=-0.4, z_max=+0.4):\n", " # z, z_bin, z_min, z_max in meters\n", " \n", " hist, bins = np.histogram( z, range=(z_min, z_max), bins=int((z_max-z_min)/z_bin) )\n", " Qm = Qe*(Z-Ne)*N_ions/Np # macroparticle charge in C\n", " I = hist*Qm/(z_bin/c) # A\n", "\n", " z_centers = (bins[:-1] + bins[1:]) / 2\n", " \n", " return z_centers, I\n", "\n", "%opts Area [show_grid=True aspect=2] (linewidth=1, alpha=0.7)\n", "\n", "z_centers, I = get_I(z)\n", "\n", "dim_I = hv.Dimension('I', unit='A', range=(0.0,+4.0))\n", "\n", "z_I = hv.Area((z_centers*1e3, I), kdims=[dim_z], vdims=[dim_I])\n", "\n", "z_I" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [], "source": [ "# plot z-dp + beam current profile:\n", "def plot_z_dp(X):\n", " z = X[4].A1\n", " dp = X[5].A1\n", " z_centers, I = get_I(z)\n", " img = (hv.Points((z*1e3, dp*100), [dim_z,dim_dp]) + \\\n", " hv.Area((z_centers*1e3, I), kdims=[dim_z], vdims=[dim_I]) ).cols(1)\n", " return img" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ ":Layout\n", " .Points.I :Points [z,dp]\n", " .Area.I :Area [z] (I)" ] }, "execution_count": 78, "metadata": { "application/vnd.holoviews_exec.v0+json": {} }, "output_type": "execute_result" } ], "source": [ "plot_z_dp(X_plots[t_plots[-1]])" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "data": { "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", " \n", "
\n", "
\n", "
\n", "" ], "text/plain": [ ":Layout\n", " .Points.I :HoloMap [t (sec)]\n", " :Points [z,dp]\n", " .Area.I :HoloMap [t (sec)]\n", " :Area [z] (I)" ] }, "execution_count": 79, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 140260486533712 } }, "output_type": "execute_result" } ], "source": [ "items = [(turn*L/c, (plot_z_dp(X_plots[turn]))) for turn in t_plots]\n", "\n", "m = hv.HoloMap(items, kdims = ['t (sec)'])\n", "m.collate()" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "scrolled": true }, "outputs": [ { "data": { "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", " \n", "
\n", "
\n", "
\n", "" ], "text/plain": [ ":Layout\n", " .Points.I :HoloMap [t (sec)]\n", " :Points [x,xp]\n", " .Points.II :HoloMap [t (sec)]\n", " :Points [y,yp]" ] }, "execution_count": 80, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 140260447643920 } }, "output_type": "execute_result" } ], "source": [ "items = [(turn*L/c, plot_xxp_yyp(X_plots[turn])) for turn in t_plots]\n", "\n", "m = hv.HoloMap(items, kdims = ['t (sec)'])\n", "m.collate()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### And finally the full turn-by-turn Monte Carlo simulation including photon emissions" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "turn = 1e+06 (100 %, 0 h. 0 min. left))" ] } ], "source": [ "import time\n", "\n", "N_turns = 1000000\n", "#N_turns = 2000000\n", "\n", "N_plots = 21\n", "\n", "t_plots = np.arange(0,N_turns+1,int(N_turns/(N_plots-1)))\n", "\n", "X_plots = {}\n", "Excited_plots = {}\n", "\n", "X1 = X;\n", "T_start = time.time()\n", "for turn in range(0,N_turns+1):\n", " Excited = ExciteIons(X1)\n", "\n", " if turn in t_plots:\n", " if turn != t_plots[0]:\n", " sec_left = ((time.time() - T_start)/turn)*(N_turns - turn)\n", " hours_left = np.floor(sec_left/3600)\n", " min_left = (sec_left - hours_left*3600)/60\n", " print( \"\\rturn = %g (%g %%, %g h. %.0f min. left)\" %\n", " (turn, (100*turn/N_turns), hours_left, min_left), end=\"\")\n", " X_plots[turn] = X1\n", " Excited_plots[turn] = Excited\n", "\n", " X1, Photons = EmitPhotons(X1, Excited)\n", " X1 = RFcavity(X1, h, eVrf, phi0)\n", " X1 = M*X1" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [], "source": [ "#X_plots" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [], "source": [ "np.save(\"X_plots.npy\", X_plots)" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [], "source": [ "def plot_z_dp_Excited(X,Excited):\n", " z = X[4].A1\n", " dp = X[5].A1\n", "\n", " txt = \"%.1f%% of ions excited\" % (100*len(z[Excited])/len(z))\n", "\n", " z_centers, I = get_I(z, z_bin = 1e-3)\n", " img = (hv.Points((z*1e3, dp*100), [dim_z,dim_dp], group='Still') * \\\n", " hv.Points((z[Excited]*1e3, dp[Excited]*100), [dim_z,dim_dp], group='Excited', label=txt) + \\\n", " hv.Area((z_centers*1e3, I), kdims=[dim_z], vdims=[dim_I]) ).cols(1)\n", " \n", " return img" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "data": { "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", " \n", "
\n", "
\n", "
\n", "" ], "text/plain": [ ":Layout\n", " .Overlay.I :HoloMap [t (sec)]\n", " :Overlay\n", " .Still.I :Points [z,dp]\n", " .Excited.A_18_full_stop_8_percent_of_ions_excited :Points [z,dp]\n", " .Area.I :HoloMap [t (sec)]\n", " :Area [z] (I)" ] }, "execution_count": 85, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 140260477082128 } }, "output_type": "execute_result" } ], "source": [ "#dim_I = hv.Dimension('I', unit='A', range=(0.0,+100.0))\n", "dim_I = hv.Dimension('I', unit='A', range=(0.0,+8.0))\n", "\n", "items = [(turn*L/c, plot_z_dp_Excited(X_plots[turn],Excited_plots[turn])) for turn in t_plots]\n", "\n", "m = hv.HoloMap(items, kdims = ['t (sec)'])\n", "m.collate()" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "data": { "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", " \n", "
\n", "
\n", "
\n", "" ], "text/plain": [ ":Layout\n", " .Points.I :HoloMap [t (sec)]\n", " :Points [x,xp]\n", " .Points.II :HoloMap [t (sec)]\n", " :Points [y,yp]" ] }, "execution_count": 86, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 140260434768976 } }, "output_type": "execute_result" } ], "source": [ "items = [(turn*L/c, plot_xxp_yyp(X_plots[turn])) for turn in t_plots]\n", "\n", "m = hv.HoloMap(items, kdims = ['t (sec)'])\n", "m.collate()" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [], "source": [ "def plot_z_y_Excited(X,Excited):\n", " z = X[4].A1\n", " y = X[2].A1\n", "\n", " txt = \"%.1f%% of ions excited\" % (100*len(z[Excited])/len(z))\n", " \n", " z_centers, I = get_I(z, z_bin = 1e-3)\n", " img = (hv.Points((z*1e3, y*1e3), [dim_z,dim_y], group='Still') * \\\n", " hv.Points((z[Excited]*1e3, y[Excited]*1e3), [dim_z,dim_y], group='Excited', label=txt) + \\\n", " hv.Area((z_centers*1e3, I), kdims=[dim_z], vdims=[dim_I]) ).cols(1)\n", " \n", " return img" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "data": { "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", " \n", "
\n", "
\n", "
\n", "" ], "text/plain": [ ":Layout\n", " .Overlay.I :HoloMap [t (sec)]\n", " :Overlay\n", " .Still.I :Points [z,y]\n", " .Excited.A_18_full_stop_8_percent_of_ions_excited :Points [z,y]\n", " .Area.I :HoloMap [t (sec)]\n", " :Area [z] (I)" ] }, "execution_count": 88, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 140260285494864 } }, "output_type": "execute_result" } ], "source": [ "items = [(turn*L/c, plot_z_y_Excited(X_plots[turn],Excited_plots[turn])) for turn in t_plots]\n", "\n", "m = hv.HoloMap(items, kdims = ['t (sec)'])\n", "m.collate()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Technical info:\n", "\n" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [], "source": [ "%load_ext watermark" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "holoviews 1.12.6\n", "numpy 1.17.2\n", "2019-10-24 \n", "\n", "CPython 3.7.4\n", "IPython 7.8.0\n", "\n", "compiler : GCC 7.3.0\n", "system : Linux\n", "release : 4.4.0-165-generic\n", "machine : x86_64\n", "processor : x86_64\n", "CPU cores : 40\n", "interpreter: 64bit\n" ] } ], "source": [ "%watermark --python --date --iversions --machine" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[NbConvertApp] Converting notebook psi_beam_vs_laser.ipynb to HTML\n", "[NbConvertApp] Writing 4313792 bytes to psi_beam_vs_laser.html\n" ] } ], "source": [ "!jupyter nbconvert --to HTML psi_beam_vs_laser.ipynb" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'/home/petrenko/work/GF'" ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pwd" ] } ], "metadata": { "anaconda-cloud": {}, "hide_input": false, "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.7.4" } }, "nbformat": 4, "nbformat_minor": 4 }