{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "In this notebook, let's code up some 2D wave propagation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inspiration : https://dionhaefner.github.io/2016/11/suck-less-scientific-python-part-2-efficient-number-crunching/\n", "\n", "(which in turn comes from here: http://matthewrocklin.com/blog/work/2018/01/30/the-case-for-numba)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Explain that we also want to try all the nice visualization goodness coming from Holoviews." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Theory " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from numba import njit" ] }, { "cell_type": "code", "execution_count": 2, "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.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", "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.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", " HoloViews.plot_index[id] = Bokeh.index[id];\n", " } else {\n", " HoloViews.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 HoloViews.plot_index)) { return; }\n", " var comm = window.HoloViews.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 HoloViews.plot_index[id];\n", " if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n", " window.Bokeh.index[id].model.document.clear();\n", " delete Bokeh.index[id];\n", " }\n", "}\n", "\n", "/**\n", " * Handle kernel restart event\n", " */\n", "function handle_kernel_cleanup(event, handle) {\n", " delete HoloViews.comms[\"hv-extension-comm\"];\n", " window.HoloViews.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);\t\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", "\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", " " ], "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.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\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.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 HoloViews.plot_index[id] = Bokeh.index[id];\n } else {\n HoloViews.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 HoloViews.plot_index)) { return; }\n var comm = window.HoloViews.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 HoloViews.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n window.Bokeh.index[id].model.document.clear();\n delete Bokeh.index[id];\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete HoloViews.comms[\"hv-extension-comm\"];\n window.HoloViews.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);\t\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\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 " }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import holoviews as hv\n", "hv.extension('matplotlib')" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "@njit\n", "def step(u_curr, u_prev, c, dt, dx):\n", " \"\"\"Unitary finite difference propagation step.\"\"\"\n", " u_next = np.zeros_like(u_curr)\n", " for i in range(1, u_curr.shape[0] - 1):\n", " for j in range(1, u_curr.shape[1] - 1):\n", " u_next[i, j] = 2 * u_curr[i, j] - u_prev[i, j] + c**2 * dt**2 / dx**2 * (- 4 * u_curr[i, j] + \\\n", " u_curr[i+1, j] + u_curr[i-1, j] + \\\n", " u_curr[i, j+1] + u_curr[i, j-1])\n", " return u_next" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Practice 1: a square room " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's setup the grid and step sizes." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "dx = dy = 0.005\n", "x = np.arange(0, 1 + dx, dx)\n", "y = np.arange(0, 1 + dy, dy)\n", "X, Y = np.meshgrid(x, y)\n", "bounds = (x.min(), y.min(), y.max(), x.max())" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "source_loc = (0.5, 0.5)\n", "u_curr = np.zeros_like(X)\n", "u_prev = u_curr.copy()\n", "u_curr = np.exp(-((X - source_loc[0])**2 + (Y - source_loc[1])**2) * 5000.)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "celerity = 1." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "dt = 0.1 * 1/2 * dx / celerity" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "def make_steps(nstep=10):\n", " global u_curr, u_prev, u_next\n", " for _ in range(nstep):\n", " u_next = step(u_curr, u_prev, celerity, dt, dx)\n", " u_prev = u_curr.copy()\n", " u_curr = u_next.copy()\n", " return u_curr" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, ...,\n", " 0.00000000e+000, 0.00000000e+000, 0.00000000e+000],\n", " [ 0.00000000e+000, 5.44361463e-212, 4.95809542e-210, ...,\n", " 4.95809542e-210, 5.44361463e-212, 0.00000000e+000],\n", " [ 0.00000000e+000, 4.95809542e-210, 4.48537552e-208, ...,\n", " 4.48537552e-208, 4.95809542e-210, 0.00000000e+000],\n", " ..., \n", " [ 0.00000000e+000, 4.95809542e-210, 4.48537552e-208, ...,\n", " 4.48537552e-208, 4.95809542e-210, 0.00000000e+000],\n", " [ 0.00000000e+000, 5.44361463e-212, 4.95809542e-210, ...,\n", " 4.95809542e-210, 5.44361463e-212, 0.00000000e+000],\n", " [ 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, ...,\n", " 0.00000000e+000, 0.00000000e+000, 0.00000000e+000]])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "make_steps(500)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Unexpected plot option 'width' for Image in loaded backend 'matplotlib'.\n", "\n", "Similar keywords in the currently active 'matplotlib' renderer are: ['cbar_width']\n", "\n", "If you believe this keyword is correct, please make sure the backend has been imported or loaded with the hv.extension.Unexpected plot option 'width' for Image in loaded backend 'matplotlib'.\n", "\n", "Similar keywords in the currently active 'matplotlib' renderer are: ['cbar_width']\n", "\n", "If you believe this keyword is correct, please make sure the backend has been imported or loaded with the hv.extension." ] }, { "data": { "application/javascript": [ "\n", "// Ugly hack - see #2574 for more information\n", "if (!(document.getElementById('218756488')) && !(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", " parentTag.append(htmlObject)\n", "}\n" ], "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
" ], "text/plain": [ ":Image [x,y] (z)" ] }, "execution_count": 10, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 218756488 } }, "output_type": "execute_result" } ], "source": [ "%%opts Image [colorbar=True] (cmap='seismic')\n", "img = hv.Image(u_curr, bounds=bounds).redim.range(z=(-10, 10))\n", "img" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Looks nice!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's animate this using a HoloMap:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see #2574 for more information\n", "if (!(document.getElementById('222421400')) && !(document.getElementById('_anim_imgb0b909e3383e4f2f88a8f0ca77984d26'))) {\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", "
\n", "
\n", " \n", " \n", "
\n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " parentTag.append(htmlObject)\n", "}\n", "/* Instantiate the MPLSelectionWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "var widget_ids = new Array(1);\n", "\n", "\n", "widget_ids[0] = \"_anim_widgetb0b909e3383e4f2f88a8f0ca77984d26_index\";\n", "\n", "\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\"};\n", " var dim_vals = ['0.0'];\n", " var keyMap = {\"('0.0',)\": 0, \"('1.0',)\": 1, \"('2.0',)\": 2, \"('3.0',)\": 3, \"('4.0',)\": 4, \"('5.0',)\": 5, \"('6.0',)\": 6, \"('7.0',)\": 7, \"('8.0',)\": 8, \"('9.0',)\": 9};\n", " var notFound = \"

\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": [ ":HoloMap [index]\n", " :Image [x,y] (z)" ] }, "execution_count": 11, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 222421400 } }, "output_type": "execute_result" } ], "source": [ "u_curr = np.zeros_like(X)\n", "u_prev = u_curr.copy()\n", "u_curr = np.exp(-((X - source_loc[0])**2 + (Y - source_loc[1])**2) * 500.)\n", "hmap1 = hv.HoloMap({i: hv.Image(make_steps(1000), bounds=bounds).options(colorbar=True, cmap='seismic').redim.range(z=(-10, 10)) for i in list(range(10))}, kdims='index')\n", "hmap1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can select the best snapshots of the wave below:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see #2574 for more information\n", "if (!(document.getElementById('222237024')) && !(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", " parentTag.append(htmlObject)\n", "}\n" ], "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
" ], "text/plain": [ ":Layout\n", " .Image.I :Image [x,y] (z)\n", " .Image.II :Image [x,y] (z)\n", " .Image.III :Image [x,y] (z)\n", " .Image.IV :Image [x,y] (z)" ] }, "execution_count": 12, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 222237024 } }, "output_type": "execute_result" } ], "source": [ "(hmap1[4] + hmap1[7] + hmap1[8] + hmap1[9]).cols(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By playing with the initial conditions and visualizing method we can get other cool results:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see #2574 for more information\n", "if (!(document.getElementById('244114824')) && !(document.getElementById('_anim_img90a770c4a5fd469a840f237820b3229a'))) {\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", "
\n", "
\n", " \n", " \n", "
\n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " parentTag.append(htmlObject)\n", "}\n", "/* Instantiate the MPLSelectionWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "var widget_ids = new Array(1);\n", "\n", "\n", "widget_ids[0] = \"_anim_widget90a770c4a5fd469a840f237820b3229a_index\";\n", "\n", "\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\"};\n", " var dim_vals = ['0.0'];\n", " var keyMap = {\"('0.0',)\": 0, \"('1.0',)\": 1, \"('2.0',)\": 2, \"('3.0',)\": 3, \"('4.0',)\": 4, \"('5.0',)\": 5, \"('6.0',)\": 6, \"('7.0',)\": 7, \"('8.0',)\": 8, \"('9.0',)\": 9};\n", " var notFound = \"

\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": [ ":HoloMap [index]\n", " :Image [x,y] (z)" ] }, "execution_count": 13, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 244114824 } }, "output_type": "execute_result" } ], "source": [ "u_curr = np.zeros_like(X)\n", "u_prev = u_curr.copy()\n", "u_curr = np.exp(-((X - source_loc[0])**2 + (Y - source_loc[1])**2) * 5000.)\n", "hmap2 = hv.HoloMap({i: hv.Image(make_steps(2000)).options(colorbar=True, cmap='seismic').redim.range(z=(-2, 2)) for i in list(range(10))}, kdims='index')\n", "hmap2" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see #2574 for more information\n", "if (!(document.getElementById('244164648')) && !(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", " parentTag.append(htmlObject)\n", "}\n" ], "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
" ], "text/plain": [ ":Layout\n", " .Image.I :Image [x,y] (z)\n", " .Image.II :Image [x,y] (z)\n", " .Image.III :Image [x,y] (z)\n", " .Image.IV :Image [x,y] (z)" ] }, "execution_count": 14, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 244164648 } }, "output_type": "execute_result" } ], "source": [ "(hmap2[4] + hmap2[7] + hmap2[8] + hmap2[9]).cols(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Practice 2: a plane wave source" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another fun variation on this is to put in a source that is a plane wave. \n", "\n", "To do this, we have to define a wave vector and also work a little bit on the initial conditions, since we want to analytically define the initial pressure as well as the initial pressure change." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "dx = dy = 0.005\n", "x = np.arange(0, 3 + dx, dx)\n", "y = np.arange(0, 1 + dy, dy)\n", "X, Y = np.meshgrid(x, y)\n", "bounds = (x.min(), y.min(), x.max(), y.max())" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "dt = 0.1 * 1/2 * dx / celerity" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "@njit\n", "def step_initial(u_curr, du_currdt, c, dt, dx):\n", " \"\"\"Initial finite difference propagation step.\"\"\"\n", " u_next = np.zeros_like(u_curr)\n", " for i in range(1, u_curr.shape[0] - 1):\n", " for j in range(1, u_curr.shape[1] - 1):\n", " u_next[i, j] = u_curr[i, j] + dt * du_currdt[i, j] + 0.5*c**2 * dt**2 / dx**2 * (- 4 * u_curr[i, j] + \\\n", " u_curr[i+1, j] + u_curr[i-1, j] + \\\n", " u_curr[i, j+1] + u_curr[i, j-1])\n", " return u_next" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "@njit\n", "def plane_wave(k, X, Y, x0, y0, celerity, dt, dx):\n", " kx, ky = k\n", " absk = np.sqrt(kx**2 + ky**2)\n", " omega = absk * celerity\n", " nx, ny = kx/absk, ky/absk\n", " phase = - kx * (X-x0) - ky * (Y-y0)\n", " envelope = np.exp(- (nx * (X - x0) + ny * (Y - y0))**2 * 10)\n", " u0 = np.sin(phase) * envelope\n", " du0 = omega * np.cos(phase) * envelope\n", " u1 = step_initial(u0, du0, celerity, dt, dx)\n", " return u0, u1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, let's setup a plane wave travelling towards the bottom." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "k = (0., 10.)\n", "u_prev, u_curr = plane_wave(k, X, Y, 0.1, 0.2, celerity, dt, dx)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see #2574 for more information\n", "if (!(document.getElementById('275876944')) && !(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", " parentTag.append(htmlObject)\n", "}\n" ], "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
" ], "text/plain": [ ":Image [x,y] (z)" ] }, "execution_count": 20, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 275876944 } }, "output_type": "execute_result" } ], "source": [ "%%opts Image [colorbar=True fig_size=400] (cmap='seismic')\n", "make_steps(200)\n", "img = hv.Image(u_next, bounds=bounds).redim.range(z=(-1, 1))\n", "img" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's make an animation of this wave." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see #2574 for more information\n", "if (!(document.getElementById('281819568')) && !(document.getElementById('_anim_img566f00c6ac36476e84f72d93cb7b5b84'))) {\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", "
\n", "
\n", " \n", " \n", "
\n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " parentTag.append(htmlObject)\n", "}\n", "/* Instantiate the MPLSelectionWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "var widget_ids = new Array(1);\n", "\n", "\n", "widget_ids[0] = \"_anim_widget566f00c6ac36476e84f72d93cb7b5b84_index\";\n", "\n", "\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\"};\n", " var dim_vals = ['0.0'];\n", " var keyMap = {\"('0.0',)\": 0, \"('1.0',)\": 1, \"('2.0',)\": 2, \"('3.0',)\": 3, \"('4.0',)\": 4, \"('5.0',)\": 5, \"('6.0',)\": 6, \"('7.0',)\": 7, \"('8.0',)\": 8, \"('9.0',)\": 9};\n", " var notFound = \"

\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": [ ":HoloMap [index]\n", " :Image [x,y] (z)" ] }, "execution_count": 21, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 281819568 } }, "output_type": "execute_result" } ], "source": [ "k = (0., 10.)\n", "u_prev, u_curr = plane_wave(k, X, Y, 0.1, 0.2, celerity, dt, dx)\n", "\n", "hmap3 = hv.HoloMap({i: hv.Image(make_steps(2000), bounds=bounds).options(fig_size=400, colorbar=True, cmap='seismic').redim.range(z=(-1, 1)) for i in list(range(10))}, kdims='index')\n", "hmap3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's try a wave travelling to the right:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see #2574 for more information\n", "if (!(document.getElementById('277170664')) && !(document.getElementById('_anim_img937de455b1b04e0992fd46bcd7ed51bf'))) {\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", "
\n", "
\n", " \n", " \n", "
\n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " parentTag.append(htmlObject)\n", "}\n", "/* Instantiate the MPLSelectionWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "var widget_ids = new Array(1);\n", "\n", "\n", "widget_ids[0] = \"_anim_widget937de455b1b04e0992fd46bcd7ed51bf_index\";\n", "\n", "\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\"};\n", " var dim_vals = ['0.0'];\n", " var keyMap = {\"('0.0',)\": 0, \"('1.0',)\": 1, \"('2.0',)\": 2, \"('3.0',)\": 3, \"('4.0',)\": 4, \"('5.0',)\": 5, \"('6.0',)\": 6, \"('7.0',)\": 7, \"('8.0',)\": 8, \"('9.0',)\": 9};\n", " var notFound = \"

\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": [ ":HoloMap [index]\n", " :Image [x,y] (z)" ] }, "execution_count": 22, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 277170664 } }, "output_type": "execute_result" } ], "source": [ "k = (10., 0.)\n", "u_prev, u_curr = plane_wave(k, X, Y, 0.1, 0.2, celerity, dt, dx)\n", "\n", "hmap4 = hv.HoloMap({i: hv.Image(make_steps(2000), bounds=bounds).options(fig_size=400, colorbar=True, cmap='seismic').redim.range(z=(-1, 1)) for i in list(range(10))}, kdims='index')\n", "hmap4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And finally, with a little bit of rotational geometry, we can get oblique wave incidence:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see #2574 for more information\n", "if (!(document.getElementById('302184264')) && !(document.getElementById('_anim_imgf9542263c0b445058fc3424496d6289d'))) {\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", "
\n", "
\n", " \n", " \n", "
\n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " parentTag.append(htmlObject)\n", "}\n", "/* Instantiate the MPLSelectionWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "var widget_ids = new Array(1);\n", "\n", "\n", "widget_ids[0] = \"_anim_widgetf9542263c0b445058fc3424496d6289d_index\";\n", "\n", "\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\"};\n", " var dim_vals = ['0.0'];\n", " var keyMap = {\"('0.0',)\": 0, \"('1.0',)\": 1, \"('2.0',)\": 2, \"('3.0',)\": 3, \"('4.0',)\": 4, \"('5.0',)\": 5, \"('6.0',)\": 6, \"('7.0',)\": 7, \"('8.0',)\": 8, \"('9.0',)\": 9};\n", " var notFound = \"

\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": [ ":HoloMap [index]\n", " :Image [x,y] (z)" ] }, "execution_count": 23, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 302184264 } }, "output_type": "execute_result" } ], "source": [ "theta = 60\n", "k = 10. * np.array([np.cos(np.deg2rad(theta)), np.sin(np.deg2rad(theta))])\n", "u_prev, u_curr = plane_wave(k, X, Y, 0.1, 0.2, celerity, dt, dx)\n", "\n", "hmap5 = hv.HoloMap({i: hv.Image(make_steps(2000), bounds=bounds).options(fig_size=400, colorbar=True, cmap='seismic').redim.range(z=(-1, 1)) for i in list(range(10))}, kdims='index')\n", "hmap5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's it! I hope you've enjoyed seeing these beautiful wave shapes as much as I did." ] } ], "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.6.4" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }