{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "In this notebook (which I wrote as a side-experiment to the Coursera class [Fundamentals of waves and vibrations](https://www.coursera.org/learn/fundamentals-waves-vibrations)), I investigate one-dimensional wave propagation and in particular the dispersion phenomenon." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Non-dispersive 1D waves " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's start with non-dispersive 1D waves. A simple object that allows us to do some nice computations is the harmonic wave. Quite simply put, an harmonic wave has a spatial pattern, which is due to its spatial phase and in particular its *wavenumber* k and a temporal pattern, due to its *pulsation* omega.\n", "\n", "We start by defining the spatial pattern, a sort of phase map (as I described it in [this previous blog article](http://flothesof.github.io/monopole-dipole-quadrupole.html))." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "x = np.linspace(-5, 5, num=200)\n", "k = 2 * np.pi / 1\n", "phase = np.exp(1j * k * x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we add the time oscillation to the phase map, we get a propagating wave, which we can animate using `holoviews`:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\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", " toinsert[nchildren-1].children[0].innerHTML = output.data[HTML_MIME_TYPE];\n", " toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\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", " }\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", " if ((id === undefined) || !(id in PyViz.plot_index)) { return; }\n", " var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n", " 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 toinsert[nchildren-1].children[0].innerHTML = output.data[HTML_MIME_TYPE];\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\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 }\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 if ((id === undefined) || !(id in PyViz.plot_index)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n 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', logo=False)\n", "\n", "hv.opts.defaults(hv.opts.Curve(fig_inches=6, aspect=1.5))\n", "hv.opts.defaults(hv.opts.Overlay(fig_inches=6, aspect=1.5))\n", "\n", "omega = k * 1.\n", "T = 2 * np.pi / omega\n", "N = 15" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('2151378378992')) && !(document.getElementById('_anim_img2bef328e0095403fa476556a6d0c3a97'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 15, \"2bef328e0095403fa476556a6d0c3a97\", 50, false, \"default\", true, \"./json_figures/\", false, \"2151378378992\");\n", " HoloViews.index['2151378378992'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "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", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :Curve [x] (y)" ] }, "execution_count": 3, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 2151378378992 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber'\n", "hmap1 = hv.HoloMap({i: hv.Curve((x, np.real(phase * np.exp(-1j * omega * i / N * T))), \n", " label='plane wave') for i in range(N)})\n", "hmap1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With this simple relationship, we see a wave that propagates from left to right." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Two plane waves " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A first interesting pattern that we can obtain using this harmonic wave toolkit is what happens when two plane waves propagate in the same medium, each with the same speed, but going in opposite directions. \n", "\n", "The wave going in the opposite direction is defined by the following phase map:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "phase_opposite = np.exp(-1j * k * x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Which we can animate:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('2151395877832')) && !(document.getElementById('_anim_img8504ca03d80c40c68a5025083c339aa3'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 15, \"8504ca03d80c40c68a5025083c339aa3\", 50, false, \"default\", true, \"./json_figures/\", false, \"2151395877832\");\n", " HoloViews.index['2151395877832'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "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", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :Curve [x] (y)" ] }, "execution_count": 5, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 2151395877832 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber'\n", "hmap2 = hv.HoloMap({i: hv.Curve((x, np.real(phase_opposite * np.exp(-1j * omega * i / N * T))),\n", " label='plane wave -x') for i in range(N)})\n", "hmap2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can guess what will happen if we superpose both waves on the same plot:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('2151397170480')) && !(document.getElementById('_anim_img3dfb53cd8fbc450286c4e8e213cb5838'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 15, \"3dfb53cd8fbc450286c4e8e213cb5838\", 50, false, \"default\", true, \"./json_figures/\", false, \"2151397170480\");\n", " HoloViews.index['2151397170480'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "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", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :Overlay\n", " .Curve.Plane_wave :Curve [x] (y)\n", " .Curve.Plane_wave_hyphen_minus_x :Curve [x] (y)" ] }, "execution_count": 6, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 2151397170480 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber'\n", "hmap3 = (hmap1 * hmap2).opts(show_legend=False)\n", "hmap3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The waves interfere, sometimes adding up and sometimes destroying each other, which we can highlight with some snapshots of the above animation:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('2151397462424')) && !(document.getElementById('_anim_imgNone'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n" ], "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
" ], "text/plain": [ ":Layout\n", " .Overlay.I :Overlay\n", " .Curve.Plane_wave :Curve [x] (y)\n", " .Curve.Plane_wave_hyphen_minus_x :Curve [x] (y)\n", " .Overlay.II :Overlay\n", " .Curve.Plane_wave :Curve [x] (y)\n", " .Curve.Plane_wave_hyphen_minus_x :Curve [x] (y)" ] }, "execution_count": 7, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 2151397462424 } }, "output_type": "execute_result" } ], "source": [ "hmap3[7] + hmap3[13]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since waves add up in reality, the resulting wave will look as follows:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('2151396200968')) && !(document.getElementById('_anim_img8bfb8210818044cdb30ad744b7c7ba7d'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 15, \"8bfb8210818044cdb30ad744b7c7ba7d\", 50, false, \"default\", true, \"./json_figures/\", false, \"2151396200968\");\n", " HoloViews.index['2151396200968'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "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", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :Curve [x] (y)" ] }, "execution_count": 8, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 2151396200968 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber'\n", "hmap4 = hv.HoloMap({i: hv.Curve((x, np.real((phase + phase_opposite) * np.exp(-1j * omega * i / N * T))),\n", " label='two plane waves -x +x ') for i in range(N)})\n", "hmap4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see a dynamical destruction and amplification of the two waves, a so-called *standing wave*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Group velocity " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The above were animated using constant phase velocity. Some of the most interesting waves are those that exist in dispersive media. This means that waves of different wavelengths can propagate at different velocities." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A simple example to understand what happens is to plot the motion of two waves going in the same direction (not opposite ones as above) but at different speeds." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, we use a so-called dispersion relation, which relates the speed of waves at different frequencies such as the one observed for [water waves in deep waters](https://en.wikipedia.org/wiki/Dispersion_(water_waves)) (the group velocity is half the phase velocity)." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "N = 5" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "cphi = 2.\n", "k1 = 2 * np.pi / 1.\n", "w1 = k1 * cphi\n", "k2 = k1 + np.pi\n", "w2 = w1 + .5 * cphi * (k2 - k1)\n", "\n", "phase_k1 = np.exp(1j * k1 * x)\n", "phase_k2 = np.exp(1j * k2 * x)\n", "\n", "T1 = 2 * np.pi / w1\n", "T2 = 2 * np.pi / w2" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('2151399044432')) && !(document.getElementById('_anim_imgcd66557e3f864d5fab6d315f3792767a'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\", \"15\": \"\", \"16\": \"\", \"17\": \"\", \"18\": \"\", \"19\": \"\", \"20\": \"\", \"21\": \"\", \"22\": \"\", \"23\": \"\", \"24\": \"\", \"25\": \"\", \"26\": \"\", \"27\": \"\", \"28\": \"\", \"29\": \"\", \"30\": \"\", \"31\": \"\", \"32\": \"\", \"33\": \"\", \"34\": \"\", \"35\": \"\", \"36\": \"\", \"37\": \"\", \"38\": \"\", \"39\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 40, \"cd66557e3f864d5fab6d315f3792767a\", 50, false, \"default\", true, \"./json_figures/\", false, \"2151399044432\");\n", " HoloViews.index['2151399044432'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "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", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :Curve [x] (y)" ] }, "execution_count": 11, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 2151399044432 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber'\n", "hmap5 = hv.HoloMap({i: hv.Curve((x, np.real(phase_k1 * np.exp(-1j * w1 * i / N * T1) + \\\n", " phase_k2 * np.exp(-1j * w2 * i / N * T1))),\n", " label='two waves travelling at different speeds') for i in range(8 * N)})\n", "hmap5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What is interesting here is that the shape of the resulting wave deforms in complicated way while moving from place to place. This is what is called dispersive propagation. However, there is still a way of describing this propagation in terms of things that don't change shape so much." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, since we are summing cosine waves, we have an exact formula describing this propagation.\n", "\n", "\n", "$$\n", "\\cos(kx - \\omega t) + cos \\left( (k + \\delta k) x - (\\omega + \\delta \\omega) t \\right) = 2 \\cos \\left ( (k + \\frac{\\delta k}{2}) x - (\\omega + \\frac{\\delta \\omega}{2}) t \\right ) cos(\\frac{\\delta k}{2} x - \\frac{\\delta \\omega}{2} t) \n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the above formula, there is a wave of approximately the same propagation properties that gets multiplied by an envelope that propagates at a slower speed than the carrier frequency (again, this is naturally found in the case of water waves in deep waters)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To see things more clearly, we can plot the two components as separated entities, as a *carrier* and an *envelope*." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "phase_carrier = np.exp(1j * (k1 + (k2 - k1)/2) * x)\n", "phase_envelope = 2 * np.exp(1j * (k2 - k1)/2 * x)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('2151401565768')) && !(document.getElementById('_anim_img1fb929db0fcc4d4585a80395548cf3c3'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\", \"15\": \"\", \"16\": \"\", \"17\": \"\", \"18\": \"\", \"19\": \"\", \"20\": \"\", \"21\": \"\", \"22\": \"\", \"23\": \"\", \"24\": \"\", \"25\": \"\", \"26\": \"\", \"27\": \"\", \"28\": \"\", \"29\": \"\", \"30\": \"\", \"31\": \"\", \"32\": \"\", \"33\": \"\", \"34\": \"\", \"35\": \"\", \"36\": \"\", \"37\": \"\", \"38\": \"\", \"39\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 40, \"1fb929db0fcc4d4585a80395548cf3c3\", 50, false, \"default\", true, \"./json_figures/\", false, \"2151401565768\");\n", " HoloViews.index['2151401565768'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "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", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :Overlay\n", " .Curve.Carrier :Curve [x] (y)\n", " .Curve.Envelope :Curve [x] (y)" ] }, "execution_count": 13, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 2151401565768 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber' \n", "%%opts Overlay [show_legend=False]\n", "hmap6 = hv.HoloMap({i: hv.Curve((x, np.real(phase_carrier * np.exp(-1j * (w1 + (w2 - w1) / 2) * i / N * T1))),\n", " label='carrier').opts(alpha=0.2) * \\\n", " hv.Curve((x, np.real(phase_envelope * np.exp(-1j * ((w2 - w1) / 2) * i / N * T1))),\n", " label='envelope') for i in range(8 * N)})\n", "hmap6" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we can see, the carrier is twice as fast as the envelope, which is the case for dispersive deep water waves." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What happens if we overlay the envelope and the carrier on top of the complicated wave that we had above?" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('2151403251976')) && !(document.getElementById('_anim_img2fee8aa1cd2847a897b2c110f8350e92'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\", \"15\": \"\", \"16\": \"\", \"17\": \"\", \"18\": \"\", \"19\": \"\", \"20\": \"\", \"21\": \"\", \"22\": \"\", \"23\": \"\", \"24\": \"\", \"25\": \"\", \"26\": \"\", \"27\": \"\", \"28\": \"\", \"29\": \"\", \"30\": \"\", \"31\": \"\", \"32\": \"\", \"33\": \"\", \"34\": \"\", \"35\": \"\", \"36\": \"\", \"37\": \"\", \"38\": \"\", \"39\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 40, \"2fee8aa1cd2847a897b2c110f8350e92\", 50, false, \"default\", true, \"./json_figures/\", false, \"2151403251976\");\n", " HoloViews.index['2151403251976'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "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", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :Overlay\n", " .Curve.Two_waves_travelling_at_different_speeds :Curve [x] (y)\n", " .Curve.Carrier :Curve [x] (y)\n", " .Curve.Envelope :Curve [x] (y)" ] }, "execution_count": 14, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 2151403251976 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber' \n", "%%opts Overlay [show_legend=False]\n", "hmap5 * hmap6" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The superposition lets the underlying structure of the complicated wave appear! The complicated pattern can be described by an envelope that propagates at slower speeds than the faster oscillations of the wave. This description can be very useful for many applications of dispersive waves, for instance guided waves in fiber optics, structures or at the surface of the sea. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*This post was entirely written using the IPython notebook. Its content is BSD-licensed. You can see a static view or download this notebook with the help of nbviewer at [20190204_beatsGroupVelocity.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/flothesof/posts/master/20190204_beatsGroupVelocity.ipynb).*" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.1" } }, "nbformat": 4, "nbformat_minor": 2 }