From a2b296caeffa60a4dfa892e9a045f89753806566 Mon Sep 17 00:00:00 2001 From: Aaron Zedwick Date: Wed, 13 Nov 2024 12:21:37 -0500 Subject: [PATCH 1/8] Initial Commit --- .../user-guide/topological-aggregations.ipynb | 2454 ++++++++++++++++- test/test_topological_agg.py | 7 +- uxarray/core/aggregation.py | 69 +- 3 files changed, 2511 insertions(+), 19 deletions(-) diff --git a/docs/user-guide/topological-aggregations.ipynb b/docs/user-guide/topological-aggregations.ipynb index 68593878d..73070cd8f 100644 --- a/docs/user-guide/topological-aggregations.ipynb +++ b/docs/user-guide/topological-aggregations.ipynb @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "93d83a02a42e21c1", "metadata": { "collapsed": false, @@ -36,7 +36,606 @@ "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "application/javascript": [ + "(function(root) {\n", + " function now() {\n", + " return new Date();\n", + " }\n", + "\n", + " var force = true;\n", + " var py_version = '3.4.3'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", + " var reloading = false;\n", + " var Bokeh = root.Bokeh;\n", + "\n", + " if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n", + " root._bokeh_timeout = Date.now() + 5000;\n", + " root._bokeh_failed_load = false;\n", + " }\n", + "\n", + " function run_callbacks() {\n", + " try {\n", + " root._bokeh_onload_callbacks.forEach(function(callback) {\n", + " if (callback != null)\n", + " callback();\n", + " });\n", + " } finally {\n", + " delete root._bokeh_onload_callbacks;\n", + " }\n", + " console.debug(\"Bokeh: all callbacks have finished\");\n", + " }\n", + "\n", + " function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n", + " if (css_urls == null) css_urls = [];\n", + " if (js_urls == null) js_urls = [];\n", + " if (js_modules == null) js_modules = [];\n", + " if (js_exports == null) js_exports = {};\n", + "\n", + " root._bokeh_onload_callbacks.push(callback);\n", + "\n", + " if (root._bokeh_is_loading > 0) {\n", + " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", + " return null;\n", + " }\n", + " if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n", + " run_callbacks();\n", + " return null;\n", + " }\n", + " if (!reloading) {\n", + " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", + " }\n", + "\n", + " function on_load() {\n", + " root._bokeh_is_loading--;\n", + " if (root._bokeh_is_loading === 0) {\n", + " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", + " run_callbacks()\n", + " }\n", + " }\n", + " window._bokeh_on_load = on_load\n", + "\n", + " function on_error() {\n", + " console.error(\"failed to load \" + url);\n", + " }\n", + "\n", + " var skip = [];\n", + " if (window.requirejs) {\n", + " window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n", + " root._bokeh_is_loading = css_urls.length + 0;\n", + " } else {\n", + " root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n", + " }\n", + "\n", + " var existing_stylesheets = []\n", + " var links = document.getElementsByTagName('link')\n", + " for (var i = 0; i < links.length; i++) {\n", + " var link = links[i]\n", + " if (link.href != null) {\n", + "\texisting_stylesheets.push(link.href)\n", + " }\n", + " }\n", + " for (var i = 0; i < css_urls.length; i++) {\n", + " var url = css_urls[i];\n", + " if (existing_stylesheets.indexOf(url) !== -1) {\n", + "\ton_load()\n", + "\tcontinue;\n", + " }\n", + " const element = document.createElement(\"link\");\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.rel = \"stylesheet\";\n", + " element.type = \"text/css\";\n", + " element.href = url;\n", + " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", + " document.body.appendChild(element);\n", + " } var existing_scripts = []\n", + " var scripts = document.getElementsByTagName('script')\n", + " for (var i = 0; i < scripts.length; i++) {\n", + " var script = scripts[i]\n", + " if (script.src != null) {\n", + "\texisting_scripts.push(script.src)\n", + " }\n", + " }\n", + " for (var i = 0; i < js_urls.length; i++) {\n", + " var url = js_urls[i];\n", + " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " for (var i = 0; i < js_modules.length; i++) {\n", + " var url = js_modules[i];\n", + " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " element.type = \"module\";\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " for (const name in js_exports) {\n", + " var url = js_exports[name];\n", + " if (skip.indexOf(url) >= 0 || root[name] != null) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.type = \"module\";\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " element.textContent = `\n", + " import ${name} from \"${url}\"\n", + " window.${name} = ${name}\n", + " window._bokeh_on_load()\n", + " `\n", + " document.head.appendChild(element);\n", + " }\n", + " if (!js_urls.length && !js_modules.length) {\n", + " on_load()\n", + " }\n", + " };\n", + "\n", + " function inject_raw_css(css) {\n", + " const element = document.createElement(\"style\");\n", + " element.appendChild(document.createTextNode(css));\n", + " document.body.appendChild(element);\n", + " }\n", + "\n", + " var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.4.3.min.js\", \"https://cdn.holoviz.org/panel/1.4.4/dist/panel.min.js\"];\n", + " var js_modules = [];\n", + " var js_exports = {};\n", + " var css_urls = [];\n", + " var inline_js = [ function(Bokeh) {\n", + " Bokeh.set_log_level(\"info\");\n", + " },\n", + "function(Bokeh) {} // ensure no trailing comma for IE\n", + " ];\n", + "\n", + " function run_inline_js() {\n", + " if ((root.Bokeh !== undefined) || (force === true)) {\n", + " for (var i = 0; i < inline_js.length; i++) {\n", + "\ttry {\n", + " inline_js[i].call(root, root.Bokeh);\n", + "\t} catch(e) {\n", + "\t if (!reloading) {\n", + "\t throw e;\n", + "\t }\n", + "\t}\n", + " }\n", + " // Cache old bokeh versions\n", + " if (Bokeh != undefined && !reloading) {\n", + "\tvar NewBokeh = root.Bokeh;\n", + "\tif (Bokeh.versions === undefined) {\n", + "\t Bokeh.versions = new Map();\n", + "\t}\n", + "\tif (NewBokeh.version !== Bokeh.version) {\n", + "\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n", + "\t}\n", + "\troot.Bokeh = Bokeh;\n", + " }} else if (Date.now() < root._bokeh_timeout) {\n", + " setTimeout(run_inline_js, 100);\n", + " } else if (!root._bokeh_failed_load) {\n", + " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", + " root._bokeh_failed_load = true;\n", + " }\n", + " root._bokeh_is_initializing = false\n", + " }\n", + "\n", + " function load_or_wait() {\n", + " // Implement a backoff loop that tries to ensure we do not load multiple\n", + " // versions of Bokeh and its dependencies at the same time.\n", + " // In recent versions we use the root._bokeh_is_initializing flag\n", + " // to determine whether there is an ongoing attempt to initialize\n", + " // bokeh, however for backward compatibility we also try to ensure\n", + " // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n", + " // before older versions are fully initialized.\n", + " if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n", + " root._bokeh_is_initializing = false;\n", + " root._bokeh_onload_callbacks = undefined;\n", + " console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n", + " load_or_wait();\n", + " } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n", + " setTimeout(load_or_wait, 100);\n", + " } else {\n", + " root._bokeh_is_initializing = true\n", + " root._bokeh_onload_callbacks = []\n", + " var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n", + " if (!reloading && !bokeh_loaded) {\n", + "\troot.Bokeh = undefined;\n", + " }\n", + " load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n", + "\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + "\trun_inline_js();\n", + " });\n", + " }\n", + " }\n", + " // Give older versions of the autoload script a head-start to ensure\n", + " // they initialize before we start loading newer version.\n", + " setTimeout(load_or_wait, 100)\n", + "}(window));" + ], + "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n var py_version = '3.4.3'.replace('rc', '-rc.').replace('.dev', '-dev.');\n var reloading = false;\n var Bokeh = root.Bokeh;\n\n if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n run_callbacks();\n return null;\n }\n if (!reloading) {\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n var skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n var existing_stylesheets = []\n var links = document.getElementsByTagName('link')\n for (var i = 0; i < links.length; i++) {\n var link = links[i]\n if (link.href != null) {\n\texisting_stylesheets.push(link.href)\n }\n }\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n if (existing_stylesheets.indexOf(url) !== -1) {\n\ton_load()\n\tcontinue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n var scripts = document.getElementsByTagName('script')\n for (var i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n\texisting_scripts.push(script.src)\n }\n }\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (var i = 0; i < js_modules.length; i++) {\n var url = js_modules[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n var url = js_exports[name];\n if (skip.indexOf(url) >= 0 || root[name] != null) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.4.3.min.js\", \"https://cdn.holoviz.org/panel/1.4.4/dist/panel.min.js\"];\n var js_modules = [];\n var js_exports = {};\n var css_urls = [];\n var inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (var i = 0; i < inline_js.length; i++) {\n\ttry {\n inline_js[i].call(root, root.Bokeh);\n\t} catch(e) {\n\t if (!reloading) {\n\t throw e;\n\t }\n\t}\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n\tvar NewBokeh = root.Bokeh;\n\tif (Bokeh.versions === undefined) {\n\t Bokeh.versions = new Map();\n\t}\n\tif (NewBokeh.version !== Bokeh.version) {\n\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n\t}\n\troot.Bokeh = Bokeh;\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n\troot.Bokeh = undefined;\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n\trun_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + "if ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n", + " window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n", + "}\n", + "\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", + " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", + " google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n", + " var messages = comm.messages[Symbol.asyncIterator]();\n", + " function processIteratorResult(result) {\n", + " var message = result.value;\n", + " console.log(message)\n", + " var content = {data: message.data, comm_id};\n", + " var buffers = []\n", + " for (var buffer of message.buffers || []) {\n", + " buffers.push(new DataView(buffer))\n", + " }\n", + " var metadata = message.metadata || {};\n", + " var msg = {content, buffers, metadata}\n", + " msg_handler(msg);\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " return messages.next().then(processIteratorResult);\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", + " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", + " var comm_promise = google.colab.kernel.comms.open(comm_id)\n", + " comm_promise.then((comm) => {\n", + " window.PyViz.comms[comm_id] = comm;\n", + " if (msg_handler) {\n", + " var messages = comm.messages[Symbol.asyncIterator]();\n", + " function processIteratorResult(result) {\n", + " var message = result.value;\n", + " var content = {data: message.data};\n", + " var metadata = message.metadata || {comm_id};\n", + " var msg = {content, metadata}\n", + " msg_handler(msg);\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " }) \n", + " var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n", + " return comm_promise.then((comm) => {\n", + " comm.send(data, metadata, buffers, disposeOnDone);\n", + " });\n", + " };\n", + " var comm = {\n", + " send: sendClosure\n", + " };\n", + " }\n", + " window.PyViz.comms[comm_id] = comm;\n", + " return comm;\n", + " }\n", + " window.PyViz.comm_manager = new JupyterCommManager();\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 == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", + " return\n", + " }\n", + " var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", + " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", + " if (id !== undefined) {\n", + " var nchildren = toinsert.length;\n", + " var html_node = toinsert[nchildren-1].children[0];\n", + " html_node.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var scripts = [];\n", + " var nodelist = html_node.querySelectorAll(\"script\");\n", + " for (var i in nodelist) {\n", + " if (nodelist.hasOwnProperty(i)) {\n", + " scripts.push(nodelist[i])\n", + " }\n", + " }\n", + "\n", + " scripts.forEach( function (oldScript) {\n", + " var newScript = document.createElement(\"script\");\n", + " var attrs = [];\n", + " var nodemap = oldScript.attributes;\n", + " for (var j in nodemap) {\n", + " if (nodemap.hasOwnProperty(j)) {\n", + " attrs.push(nodemap[j])\n", + " }\n", + " }\n", + " attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n", + " newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n", + " oldScript.parentNode.replaceChild(newScript, oldScript);\n", + " });\n", + " if (JS_MIME_TYPE in output.data) {\n", + " toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n", + " }\n", + " output_area._hv_plot_id = id;\n", + " if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n", + " window.PyViz.plot_index[id] = Bokeh.index[id];\n", + " } else {\n", + " window.PyViz.plot_index[id] = null;\n", + " }\n", + " } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", + " var bk_div = document.createElement(\"div\");\n", + " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var script_attrs = bk_div.children[0].attributes;\n", + " for (var i = 0; i < script_attrs.length; i++) {\n", + " toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n", + " }\n", + " // store reference to server id on output_area\n", + " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", + " }\n", + "}\n", + "\n", + "/**\n", + " * Handle when an output is cleared or removed\n", + " */\n", + "function handle_clear_output(event, handle) {\n", + " var id = handle.cell.output_area._hv_plot_id;\n", + " var server_id = handle.cell.output_area._bokeh_server_id;\n", + " if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n", + " var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n", + " if (server_id !== null) {\n", + " comm.send({event_type: 'server_delete', 'id': server_id});\n", + " return;\n", + " } else if (comm !== null) {\n", + " comm.send({event_type: 'delete', 'id': id});\n", + " }\n", + " delete PyViz.plot_index[id];\n", + " if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n", + " var doc = window.Bokeh.index[id].model.document\n", + " doc.clear();\n", + " const i = window.Bokeh.documents.indexOf(doc);\n", + " if (i > -1) {\n", + " window.Bokeh.documents.splice(i, 1);\n", + " }\n", + " }\n", + "}\n", + "\n", + "/**\n", + " * Handle kernel restart event\n", + " */\n", + "function handle_kernel_cleanup(event, handle) {\n", + " delete PyViz.comms[\"hv-extension-comm\"];\n", + " window.PyViz.plot_index = {}\n", + "}\n", + "\n", + "/**\n", + " * Handle update_display_data messages\n", + " */\n", + "function handle_update_output(event, handle) {\n", + " handle_clear_output(event, {cell: {output_area: handle.output_area}})\n", + " handle_add_output(event, handle)\n", + "}\n", + "\n", + "function register_renderer(events, OutputArea) {\n", + " function append_mime(data, metadata, element) {\n", + " // create a DOM node to render to\n", + " var toinsert = this.create_output_subarea(\n", + " metadata,\n", + " CLASS_NAME,\n", + " EXEC_MIME_TYPE\n", + " );\n", + " this.keyboard_manager.register_events(toinsert);\n", + " // Render to node\n", + " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", + " render(props, toinsert[0]);\n", + " element.append(toinsert);\n", + " return toinsert\n", + " }\n", + "\n", + " events.on('output_added.OutputArea', handle_add_output);\n", + " events.on('output_updated.OutputArea', handle_update_output);\n", + " events.on('clear_output.CodeCell', handle_clear_output);\n", + " events.on('delete.Cell', handle_clear_output);\n", + " events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n", + "\n", + " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", + " safe: true,\n", + " index: 0\n", + " });\n", + "}\n", + "\n", + "if (window.Jupyter !== undefined) {\n", + " try {\n", + " var events = require('base/js/events');\n", + " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", + " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", + " register_renderer(events, OutputArea);\n", + " }\n", + " } catch(err) {\n", + " }\n", + "}\n" + ], + "application/vnd.holoviews_load.v0+json": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\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 } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\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 } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\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 == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ] + }, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1002" + } + }, + "output_type": "display_data" + } + ], "source": [ "import uxarray as ux" ] @@ -123,7 +722,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "a0c9a0f19efd0633", "metadata": { "collapsed": false, @@ -131,7 +730,1323 @@ "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.UxDataset>\n",
+       "Dimensions:           (n_edge: 19, n_face: 4, n_node: 16)\n",
+       "Dimensions without coordinates: n_edge, n_face, n_node\n",
+       "Data variables:\n",
+       "    random_data_edge  (n_edge) float64 dask.array<chunksize=(19,), meta=np.ndarray>\n",
+       "    random_data_face  (n_face) float64 dask.array<chunksize=(4,), meta=np.ndarray>\n",
+       "    random_data_node  (n_node) float64 dask.array<chunksize=(16,), meta=np.ndarray>
" + ], + "text/plain": [ + "\n", + "Dimensions: (n_edge: 19, n_face: 4, n_node: 16)\n", + "Dimensions without coordinates: n_edge, n_face, n_node\n", + "Data variables:\n", + " random_data_edge (n_edge) float64 dask.array\n", + " random_data_face (n_face) float64 dask.array\n", + " random_data_node (n_node) float64 dask.array" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "grid_path = \"../../test/meshfiles/ugrid/quad-hexagon/grid.nc\"\n", "\n", @@ -161,7 +2076,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "4439706a33f61576", "metadata": { "collapsed": false, @@ -169,7 +2084,104 @@ "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Path.I :Path [Longitude,Latitude]\n", + " .Points.Node_Data :Points [lon,lat] (z)\n", + " .Points.Edge_Data :Points [lon,lat] (z)\n", + " .Points.Face_Data :Points [lon,lat] (z)" + ] + }, + "execution_count": 3, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1011" + } + }, + "output_type": "execute_result" + } + ], "source": [ "(\n", " uxds.uxgrid.plot(line_color=\"black\")\n", @@ -225,7 +2237,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "6cdf74072b2c2b43", "metadata": { "collapsed": false, @@ -240,7 +2252,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "fdea0dda96ebe09d", "metadata": { "collapsed": false, @@ -248,7 +2260,103 @@ "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Path.I :Path [Longitude,Latitude]\n", + " .Points.Node_Data :Points [lon,lat] (z)\n", + " .Points.Node_to_Face_Mean :Points [lon,lat] (z)" + ] + }, + "execution_count": 5, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1164" + } + }, + "output_type": "execute_result" + } + ], "source": [ "(\n", " uxds.uxgrid.plot(line_color=\"black\")\n", @@ -278,7 +2386,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "e7c7780435202338", "metadata": { "collapsed": false, @@ -286,7 +2394,101 @@ "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":DynamicMap []\n", + " :Image [Longitude,Latitude] (Longitude_Latitude random_data_node)" + ] + }, + "execution_count": 6, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1293" + } + }, + "output_type": "execute_result" + } + ], "source": [ "uxda_node_face_agg.plot.polygons(\n", " cmap=\"inferno\",\n", @@ -327,7 +2529,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "db5c4cce296023a", "metadata": { "collapsed": false, @@ -342,7 +2544,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "4a3c7b486e5d78d", "metadata": { "collapsed": false, @@ -350,7 +2552,103 @@ "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Path.I :Path [Longitude,Latitude]\n", + " .Points.Node_Data :Points [lon,lat] (z)\n", + " .Points.Node_to_Edge_Mean :Points [lon,lat] (z)" + ] + }, + "execution_count": 8, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1370" + } + }, + "output_type": "execute_result" + } + ], "source": [ "(\n", " uxds.uxgrid.plot(line_color=\"black\")\n", @@ -419,6 +2717,132 @@ "\"Optional" ] }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b9769693-62a8-4566-95cc-6a8078358bf5", + "metadata": {}, + "outputs": [], + "source": [ + "uxda_edge_face_agg = uxds[\"random_data_edge\"].topological_mean(destination=\"face\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f2bc4883-ea2c-47c6-8305-b5b06224a56e", + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Path.I :Path [Longitude,Latitude]\n", + " .Points.Node_Data :Points [lon,lat] (z)\n", + " .Points.Node_to_Edge_Mean :Points [lon,lat] (z)" + ] + }, + "execution_count": 5, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1011" + } + }, + "output_type": "execute_result" + } + ], + "source": [ + "(\n", + " uxds.uxgrid.plot(line_color=\"black\")\n", + " * uxds[\"random_data_edge\"]\n", + " .plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"circle\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Node Data\")\n", + " * uxda_edge_face_agg.plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"square\", clabel=None, tools=[\"hover\"]\n", + " ).relabel(\"Node to Edge Mean\")\n", + ").opts(title=\"Node to Edge Aggregation (Mean)\", legend_position=\"top_right\")" + ] + }, { "cell_type": "markdown", "id": "7dd482e719e7d775", @@ -489,7 +2913,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.8" + "version": "3.12.7" } }, "nbformat": 4, diff --git a/test/test_topological_agg.py b/test/test_topological_agg.py index f8f4a05a0..8066c8e4f 100644 --- a/test/test_topological_agg.py +++ b/test/test_topological_agg.py @@ -32,7 +32,6 @@ def test_node_to_face_aggs(): assert 'n_face' in grid_reduction.dims - def test_node_to_edge_aggs(): uxds = ux.open_dataset(ds_path, ds_path) @@ -40,3 +39,9 @@ def test_node_to_edge_aggs(): grid_reduction = getattr(uxds['areaTriangle'], agg_func)(destination='edge') assert 'n_edge' in grid_reduction.dims + + +def test_edge_to_face_aggs(): + uxds = ux.open_dataset(ds_path, ds_path) + + test = uxds["cellsOnEdge"].topological_mean("face") diff --git a/uxarray/core/aggregation.py b/uxarray/core/aggregation.py index 23aaa6ba0..4cbfc5842 100644 --- a/uxarray/core/aggregation.py +++ b/uxarray/core/aggregation.py @@ -42,9 +42,11 @@ def _uxda_grid_aggregate(uxda, destination, aggregation, **kwargs): elif uxda._edge_centered(): # aggregation of an edge-centered data variable - raise NotImplementedError( - "Aggregation of edge-centered data variables is not yet supported." - ) + if destination == "face": + return _edge_to_face_aggregation(uxda, aggregation, kwargs) + # raise NotImplementedError( + # "Aggregation of edge-centered data variables is not yet supported." + # ) # if destination == "node": # pass # elif destination == "face": @@ -181,3 +183,64 @@ def _apply_node_to_edge_aggregation_numpy( def _apply_node_to_edge_aggregation_dask(*args, **kwargs): """Applies a Node to Edge topological aggregation on a dask array.""" pass + + +def _apply_edge_to_face_aggregation_numpy( + uxda, aggregation_func, aggregation_func_kwargs +): + """Applies an Edge to Face Topological aggregation on a Numpy array.""" + data = uxda.values + face_edge_conn = uxda.uxgrid.face_edge_connectivity.values + n_nodes_per_face = uxda.uxgrid.n_nodes_per_face.values + + ( + change_ind, + n_nodes_per_face_sorted_ind, + element_sizes, + size_counts, + ) = get_face_node_partitions(n_nodes_per_face) + + result = np.empty(shape=(data.shape[:-1]) + (uxda.uxgrid.n_face,)) + + for e, start, end in zip(element_sizes, change_ind[:-1], change_ind[1:]): + face_inds = n_nodes_per_face_sorted_ind[start:end] + face_nodes_par = face_edge_conn[face_inds, 0:e] + + # apply aggregation function to current face edge partition + aggregation_par = aggregation_func( + data[..., face_nodes_par], axis=-1, **aggregation_func_kwargs + ) + + # store current aggregation + result[..., face_inds] = aggregation_par + + return result + + +def _edge_to_face_aggregation(uxda, aggregation, aggregation_func_kwargs): + """Applies an Edge to Face Topological aggregation.""" + if not uxda._edge_centered(): + raise ValueError( + f"Data Variable must be mapped to the corner nodes of each face, with dimension " + f"{uxda.uxgrid.n_edge}." + ) + + if isinstance(uxda.data, np.ndarray): + # apply aggregation using numpy + aggregated_var = _apply_edge_to_face_aggregation_numpy( + uxda, NUMPY_AGGREGATIONS[aggregation], aggregation_func_kwargs + ) + elif isinstance(uxda.data, da.Array): + # apply aggregation on dask array, TODO: + aggregated_var = _apply_edge_to_face_aggregation_numpy( + uxda, NUMPY_AGGREGATIONS[aggregation], aggregation_func_kwargs + ) + else: + raise ValueError + + return uxarray.core.dataarray.UxDataArray( + uxgrid=uxda.uxgrid, + data=aggregated_var, + dims=uxda.dims, + name=uxda.name, + ).rename({"n_edge": "n_face"}) From 182e7bbb5121e40d803ed48349fd0783d8e8f103 Mon Sep 17 00:00:00 2001 From: Aaron Zedwick Date: Wed, 13 Nov 2024 16:16:25 -0500 Subject: [PATCH 2/8] Updated test cases --- test/test_topological_agg.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/test_topological_agg.py b/test/test_topological_agg.py index 8066c8e4f..9de19b4a8 100644 --- a/test/test_topological_agg.py +++ b/test/test_topological_agg.py @@ -42,6 +42,9 @@ def test_node_to_edge_aggs(): def test_edge_to_face_aggs(): - uxds = ux.open_dataset(ds_path, ds_path) + grid_path = "/Users/aaronzedwick/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc" + data_path = "/Users/aaronzedwick/uxarray/test/meshfiles/ugrid/quad-hexagon/random-edge-data.nc" + + uxds = ux.open_dataset(grid_path, data_path) - test = uxds["cellsOnEdge"].topological_mean("face") + test = uxds["random_data_edge"].topological_mean("face") From edaead714af36c79fc3648defbae1f4988fc73f9 Mon Sep 17 00:00:00 2001 From: Aaron Zedwick Date: Tue, 7 Jan 2025 20:50:22 -0600 Subject: [PATCH 3/8] initial work on face to node --- .../topological-aggregations-checkpoint.ipynb | 2999 +++++++++++++++++ .../user-guide/topological-aggregations.ipynb | 356 +- test/test_topological_agg.py | 12 +- uxarray/conventions/ugrid.py | 6 + uxarray/core/aggregation.py | 81 +- uxarray/grid/connectivity.py | 36 + uxarray/grid/grid.py | 19 +- 7 files changed, 3356 insertions(+), 153 deletions(-) create mode 100644 docs/user-guide/.ipynb_checkpoints/topological-aggregations-checkpoint.ipynb diff --git a/docs/user-guide/.ipynb_checkpoints/topological-aggregations-checkpoint.ipynb b/docs/user-guide/.ipynb_checkpoints/topological-aggregations-checkpoint.ipynb new file mode 100644 index 000000000..704db62a7 --- /dev/null +++ b/docs/user-guide/.ipynb_checkpoints/topological-aggregations-checkpoint.ipynb @@ -0,0 +1,2999 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8c7ebaa6a858cbbb", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "# Topological Aggregations" + ] + }, + { + "cell_type": "markdown", + "id": "5121ff038b3e683e", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "Data variables are typically mapped to either the nodes, edges, or faces of an unstructured grid. The data on each of these elements can be manipulated and aggregated to perform various operations, such as mean, min, max and many others. This section will introduce the concept of Topological Aggregations and how to perform them using UXarray.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "93d83a02a42e21c1", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/aaronzedwick/uxarray/uxarray/io/_fesom2.py:95: SyntaxWarning: invalid escape sequence '\\s'\n", + " sep=\"\\s+\",\n", + "/Users/aaronzedwick/uxarray/uxarray/io/_fesom2.py:126: SyntaxWarning: invalid escape sequence '\\s'\n", + " sep=\"\\s+\",\n", + "/Users/aaronzedwick/uxarray/uxarray/io/_fesom2.py:156: SyntaxWarning: invalid escape sequence '\\s'\n", + " sep=\"\\s+\",\n", + "/Users/aaronzedwick/uxarray/uxarray/io/_fesom2.py:187: SyntaxWarning: invalid escape sequence '\\s'\n", + " sep=\"\\s+\",\n" + ] + }, + { + "data": { + "text/html": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "(function(root) {\n", + " function now() {\n", + " return new Date();\n", + " }\n", + "\n", + " const force = true;\n", + " const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", + " const reloading = false;\n", + " const Bokeh = root.Bokeh;\n", + "\n", + " // Set a timeout for this load but only if we are not already initializing\n", + " if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n", + " root._bokeh_timeout = Date.now() + 5000;\n", + " root._bokeh_failed_load = false;\n", + " }\n", + "\n", + " function run_callbacks() {\n", + " try {\n", + " root._bokeh_onload_callbacks.forEach(function(callback) {\n", + " if (callback != null)\n", + " callback();\n", + " });\n", + " } finally {\n", + " delete root._bokeh_onload_callbacks;\n", + " }\n", + " console.debug(\"Bokeh: all callbacks have finished\");\n", + " }\n", + "\n", + " function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n", + " if (css_urls == null) css_urls = [];\n", + " if (js_urls == null) js_urls = [];\n", + " if (js_modules == null) js_modules = [];\n", + " if (js_exports == null) js_exports = {};\n", + "\n", + " root._bokeh_onload_callbacks.push(callback);\n", + "\n", + " if (root._bokeh_is_loading > 0) {\n", + " // Don't load bokeh if it is still initializing\n", + " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", + " return null;\n", + " } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n", + " // There is nothing to load\n", + " run_callbacks();\n", + " return null;\n", + " }\n", + "\n", + " function on_load() {\n", + " root._bokeh_is_loading--;\n", + " if (root._bokeh_is_loading === 0) {\n", + " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", + " run_callbacks()\n", + " }\n", + " }\n", + " window._bokeh_on_load = on_load\n", + "\n", + " function on_error(e) {\n", + " const src_el = e.srcElement\n", + " console.error(\"failed to load \" + (src_el.href || src_el.src));\n", + " }\n", + "\n", + " const skip = [];\n", + " if (window.requirejs) {\n", + " window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n", + " root._bokeh_is_loading = css_urls.length + 0;\n", + " } else {\n", + " root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n", + " }\n", + "\n", + " const existing_stylesheets = []\n", + " const links = document.getElementsByTagName('link')\n", + " for (let i = 0; i < links.length; i++) {\n", + " const link = links[i]\n", + " if (link.href != null) {\n", + " existing_stylesheets.push(link.href)\n", + " }\n", + " }\n", + " for (let i = 0; i < css_urls.length; i++) {\n", + " const url = css_urls[i];\n", + " const escaped = encodeURI(url)\n", + " if (existing_stylesheets.indexOf(escaped) !== -1) {\n", + " on_load()\n", + " continue;\n", + " }\n", + " const element = document.createElement(\"link\");\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.rel = \"stylesheet\";\n", + " element.type = \"text/css\";\n", + " element.href = url;\n", + " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", + " document.body.appendChild(element);\n", + " } var existing_scripts = []\n", + " const scripts = document.getElementsByTagName('script')\n", + " for (let i = 0; i < scripts.length; i++) {\n", + " var script = scripts[i]\n", + " if (script.src != null) {\n", + " existing_scripts.push(script.src)\n", + " }\n", + " }\n", + " for (let i = 0; i < js_urls.length; i++) {\n", + " const url = js_urls[i];\n", + " const escaped = encodeURI(url)\n", + " if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n", + " if (!window.requirejs) {\n", + " on_load();\n", + " }\n", + " continue;\n", + " }\n", + " const element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " for (let i = 0; i < js_modules.length; i++) {\n", + " const url = js_modules[i];\n", + " const escaped = encodeURI(url)\n", + " if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n", + " if (!window.requirejs) {\n", + " on_load();\n", + " }\n", + " continue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " element.type = \"module\";\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " for (const name in js_exports) {\n", + " const url = js_exports[name];\n", + " const escaped = encodeURI(url)\n", + " if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n", + " if (!window.requirejs) {\n", + " on_load();\n", + " }\n", + " continue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.type = \"module\";\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " element.textContent = `\n", + " import ${name} from \"${url}\"\n", + " window.${name} = ${name}\n", + " window._bokeh_on_load()\n", + " `\n", + " document.head.appendChild(element);\n", + " }\n", + " if (!js_urls.length && !js_modules.length) {\n", + " on_load()\n", + " }\n", + " };\n", + "\n", + " function inject_raw_css(css) {\n", + " const element = document.createElement(\"style\");\n", + " element.appendChild(document.createTextNode(css));\n", + " document.body.appendChild(element);\n", + " }\n", + "\n", + " const js_urls = [\"https://cdn.holoviz.org/panel/1.5.4/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.4/dist/panel.min.js\"];\n", + " const js_modules = [];\n", + " const js_exports = {};\n", + " const css_urls = [];\n", + " const inline_js = [ function(Bokeh) {\n", + " Bokeh.set_log_level(\"info\");\n", + " },\n", + "function(Bokeh) {} // ensure no trailing comma for IE\n", + " ];\n", + "\n", + " function run_inline_js() {\n", + " if ((root.Bokeh !== undefined) || (force === true)) {\n", + " for (let i = 0; i < inline_js.length; i++) {\n", + " try {\n", + " inline_js[i].call(root, root.Bokeh);\n", + " } catch(e) {\n", + " if (!reloading) {\n", + " throw e;\n", + " }\n", + " }\n", + " }\n", + " // Cache old bokeh versions\n", + " if (Bokeh != undefined && !reloading) {\n", + " var NewBokeh = root.Bokeh;\n", + " if (Bokeh.versions === undefined) {\n", + " Bokeh.versions = new Map();\n", + " }\n", + " if (NewBokeh.version !== Bokeh.version) {\n", + " Bokeh.versions.set(NewBokeh.version, NewBokeh)\n", + " }\n", + " root.Bokeh = Bokeh;\n", + " }\n", + " } else if (Date.now() < root._bokeh_timeout) {\n", + " setTimeout(run_inline_js, 100);\n", + " } else if (!root._bokeh_failed_load) {\n", + " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", + " root._bokeh_failed_load = true;\n", + " }\n", + " root._bokeh_is_initializing = false\n", + " }\n", + "\n", + " function load_or_wait() {\n", + " // Implement a backoff loop that tries to ensure we do not load multiple\n", + " // versions of Bokeh and its dependencies at the same time.\n", + " // In recent versions we use the root._bokeh_is_initializing flag\n", + " // to determine whether there is an ongoing attempt to initialize\n", + " // bokeh, however for backward compatibility we also try to ensure\n", + " // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n", + " // before older versions are fully initialized.\n", + " if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n", + " // If the timeout and bokeh was not successfully loaded we reset\n", + " // everything and try loading again\n", + " root._bokeh_timeout = Date.now() + 5000;\n", + " root._bokeh_is_initializing = false;\n", + " root._bokeh_onload_callbacks = undefined;\n", + " root._bokeh_is_loading = 0\n", + " console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n", + " load_or_wait();\n", + " } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n", + " setTimeout(load_or_wait, 100);\n", + " } else {\n", + " root._bokeh_is_initializing = true\n", + " root._bokeh_onload_callbacks = []\n", + " const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n", + " if (!reloading && !bokeh_loaded) {\n", + " if (root.Bokeh) {\n", + " root.Bokeh = undefined;\n", + " }\n", + " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", + " }\n", + " load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n", + " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + " run_inline_js();\n", + " });\n", + " }\n", + " }\n", + " // Give older versions of the autoload script a head-start to ensure\n", + " // they initialize before we start loading newer version.\n", + " setTimeout(load_or_wait, 100)\n", + "}(window));" + ], + "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.5.4/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.4/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + "if ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n", + " window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n", + "}\n", + "\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", + " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", + " google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n", + " var messages = comm.messages[Symbol.asyncIterator]();\n", + " function processIteratorResult(result) {\n", + " var message = result.value;\n", + " console.log(message)\n", + " var content = {data: message.data, comm_id};\n", + " var buffers = []\n", + " for (var buffer of message.buffers || []) {\n", + " buffers.push(new DataView(buffer))\n", + " }\n", + " var metadata = message.metadata || {};\n", + " var msg = {content, buffers, metadata}\n", + " msg_handler(msg);\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " return messages.next().then(processIteratorResult);\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", + " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", + " var comm_promise = google.colab.kernel.comms.open(comm_id)\n", + " comm_promise.then((comm) => {\n", + " window.PyViz.comms[comm_id] = comm;\n", + " if (msg_handler) {\n", + " var messages = comm.messages[Symbol.asyncIterator]();\n", + " function processIteratorResult(result) {\n", + " var message = result.value;\n", + " var content = {data: message.data};\n", + " var metadata = message.metadata || {comm_id};\n", + " var msg = {content, metadata}\n", + " msg_handler(msg);\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " }) \n", + " var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n", + " return comm_promise.then((comm) => {\n", + " comm.send(data, metadata, buffers, disposeOnDone);\n", + " });\n", + " };\n", + " var comm = {\n", + " send: sendClosure\n", + " };\n", + " }\n", + " window.PyViz.comms[comm_id] = comm;\n", + " return comm;\n", + " }\n", + " window.PyViz.comm_manager = new JupyterCommManager();\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 == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", + " return\n", + " }\n", + " var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", + " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", + " if (id !== undefined) {\n", + " var nchildren = toinsert.length;\n", + " var html_node = toinsert[nchildren-1].children[0];\n", + " html_node.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var scripts = [];\n", + " var nodelist = html_node.querySelectorAll(\"script\");\n", + " for (var i in nodelist) {\n", + " if (nodelist.hasOwnProperty(i)) {\n", + " scripts.push(nodelist[i])\n", + " }\n", + " }\n", + "\n", + " scripts.forEach( function (oldScript) {\n", + " var newScript = document.createElement(\"script\");\n", + " var attrs = [];\n", + " var nodemap = oldScript.attributes;\n", + " for (var j in nodemap) {\n", + " if (nodemap.hasOwnProperty(j)) {\n", + " attrs.push(nodemap[j])\n", + " }\n", + " }\n", + " attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n", + " newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n", + " oldScript.parentNode.replaceChild(newScript, oldScript);\n", + " });\n", + " if (JS_MIME_TYPE in output.data) {\n", + " toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n", + " }\n", + " output_area._hv_plot_id = id;\n", + " if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n", + " window.PyViz.plot_index[id] = Bokeh.index[id];\n", + " } else {\n", + " window.PyViz.plot_index[id] = null;\n", + " }\n", + " } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", + " var bk_div = document.createElement(\"div\");\n", + " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var script_attrs = bk_div.children[0].attributes;\n", + " for (var i = 0; i < script_attrs.length; i++) {\n", + " toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n", + " }\n", + " // store reference to server id on output_area\n", + " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", + " }\n", + "}\n", + "\n", + "/**\n", + " * Handle when an output is cleared or removed\n", + " */\n", + "function handle_clear_output(event, handle) {\n", + " var id = handle.cell.output_area._hv_plot_id;\n", + " var server_id = handle.cell.output_area._bokeh_server_id;\n", + " if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n", + " var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n", + " if (server_id !== null) {\n", + " comm.send({event_type: 'server_delete', 'id': server_id});\n", + " return;\n", + " } else if (comm !== null) {\n", + " comm.send({event_type: 'delete', 'id': id});\n", + " }\n", + " delete PyViz.plot_index[id];\n", + " if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n", + " var doc = window.Bokeh.index[id].model.document\n", + " doc.clear();\n", + " const i = window.Bokeh.documents.indexOf(doc);\n", + " if (i > -1) {\n", + " window.Bokeh.documents.splice(i, 1);\n", + " }\n", + " }\n", + "}\n", + "\n", + "/**\n", + " * Handle kernel restart event\n", + " */\n", + "function handle_kernel_cleanup(event, handle) {\n", + " delete PyViz.comms[\"hv-extension-comm\"];\n", + " window.PyViz.plot_index = {}\n", + "}\n", + "\n", + "/**\n", + " * Handle update_display_data messages\n", + " */\n", + "function handle_update_output(event, handle) {\n", + " handle_clear_output(event, {cell: {output_area: handle.output_area}})\n", + " handle_add_output(event, handle)\n", + "}\n", + "\n", + "function register_renderer(events, OutputArea) {\n", + " function append_mime(data, metadata, element) {\n", + " // create a DOM node to render to\n", + " var toinsert = this.create_output_subarea(\n", + " metadata,\n", + " CLASS_NAME,\n", + " EXEC_MIME_TYPE\n", + " );\n", + " this.keyboard_manager.register_events(toinsert);\n", + " // Render to node\n", + " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", + " render(props, toinsert[0]);\n", + " element.append(toinsert);\n", + " return toinsert\n", + " }\n", + "\n", + " events.on('output_added.OutputArea', handle_add_output);\n", + " events.on('output_updated.OutputArea', handle_update_output);\n", + " events.on('clear_output.CodeCell', handle_clear_output);\n", + " events.on('delete.Cell', handle_clear_output);\n", + " events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n", + "\n", + " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", + " safe: true,\n", + " index: 0\n", + " });\n", + "}\n", + "\n", + "if (window.Jupyter !== undefined) {\n", + " try {\n", + " var events = require('base/js/events');\n", + " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", + " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", + " register_renderer(events, OutputArea);\n", + " }\n", + " } catch(err) {\n", + " }\n", + "}\n" + ], + "application/vnd.holoviews_load.v0+json": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\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 } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\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 } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\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 == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ] + }, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "898e82d6-5f7b-448c-adf4-c5f2a8164021" + } + }, + "output_type": "display_data" + } + ], + "source": [ + "import uxarray as ux" + ] + }, + { + "cell_type": "markdown", + "id": "775f787cfb55ef91", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## What are Aggregations?\n", + "\n", + "An aggregation is an operation that processes data and returns a summarized output. In the context of Numpy, this includes functions such as:\n", + "* `np.mean()`: Calculate the average value from an array of elements\n", + "* `np.min()`: Calculate the minimum value from an array of elements\n", + "* `np.std()`: Calculate the standard deviation from an array of elements\n", + "\n", + "In the context of a one-dimensional array, the aggregation is performed over the entire array. Otherwise, it is performed across a specific axis. " + ] + }, + { + "cell_type": "markdown", + "id": "99c4450a10bfa9e9", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## What are Topological Aggregations? \n", + "\n", + "When working with unstructured grids, data variables are mapped to either nodes, edges, or faces and stored as one-dimensional slices in memory, with no spatial relationship between neighbors. This means that performing a regular aggregation as discussed above would not consider the topology of the grid elements. \n", + "\n", + "A topological aggregation can be thought of as performing multiple aggregations on a per-element basis. For example, instead of computing the average across all values, we can compute the average of all the nodes that surround each face and store the result on each face. \n", + "\n", + "By utilizing connectivity information, we can perform the following topological aggregations:\n", + "* **Node to Face:** Applied to the nodes that surround each face\n", + "* **Node to Edge:** Applied to the nodes that saddle each edge\n", + "* **Edge to Node:** Applied to the edges that saddle each node\n", + "* **Edge to Face:** Applied to the edges that surround each face\n", + "* **Face to Node:** Applied to the faces that surround each node\n", + "* **Face to Edge:** Applied to the faces that saddle each edge\n", + "\n", + "UXarray supports the following topological aggregation functions:\n", + "* `UxDataArray.topological_mean()`\n", + "* `UxDataArray.topological_max()`\n", + "* `UxDataArray.topological_min()`\n", + "* `UxDataArray.topological_prod()`\n", + "* `UxDataArray.topological_sum()`\n", + "* `UxDataArray.topological_std()`\n", + "* `UxDataArray.topological_var()`\n", + "* `UxDataArray.topological_median()`\n", + "* `UxDataArray.topological_all()`\n", + "* `UxDataArray.topological_any()`\n", + "\n", + "Each of these aggregations performs the same operation described in Numpy, but is applied on a per-element basis. \n", + "\n", + "For the remainder of this guide, we will be using the `topological_mean` aggregation, but can be swapped for any of the above methods if desired." + ] + }, + { + "cell_type": "markdown", + "id": "db3ce31e96ce3719", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Data\n", + "\n", + "The data used in this section is the quad hexagon mesh, with three random data variables mapped to the nodes, edges, and faces.\n", + "\n", + "```{idea}\n", + "The plots in this notebook are interactive. You can hover over the data points to view their values.\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a0c9a0f19efd0633", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.UxDataset> Size: 312B\n",
+       "Dimensions:           (n_edge: 19, n_face: 4, n_node: 16)\n",
+       "Dimensions without coordinates: n_edge, n_face, n_node\n",
+       "Data variables:\n",
+       "    random_data_edge  (n_edge) float64 152B dask.array<chunksize=(19,), meta=np.ndarray>\n",
+       "    random_data_face  (n_face) float64 32B dask.array<chunksize=(4,), meta=np.ndarray>\n",
+       "    random_data_node  (n_node) float64 128B dask.array<chunksize=(16,), meta=np.ndarray>
" + ], + "text/plain": [ + " Size: 312B\n", + "Dimensions: (n_edge: 19, n_face: 4, n_node: 16)\n", + "Dimensions without coordinates: n_edge, n_face, n_node\n", + "Data variables:\n", + " random_data_edge (n_edge) float64 152B dask.array\n", + " random_data_face (n_face) float64 32B dask.array\n", + " random_data_node (n_node) float64 128B dask.array" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "grid_path = \"../../test/meshfiles/ugrid/quad-hexagon/grid.nc\"\n", + "\n", + "data_paths = [\n", + " \"../../test/meshfiles/ugrid/quad-hexagon/random-node-data.nc\",\n", + " \"../../test/meshfiles/ugrid/quad-hexagon/random-edge-data.nc\",\n", + " \"../../test/meshfiles/ugrid/quad-hexagon/random-face-data.nc\",\n", + "]\n", + "\n", + "uxds = ux.open_mfdataset(grid_path, data_paths)\n", + "\n", + "uxds" + ] + }, + { + "cell_type": "markdown", + "id": "ce3bcc602c52502c", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "We can visualize the data on each element by using different markers:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "4439706a33f61576", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Path.I :Path [Longitude,Latitude]\n", + " .Points.Node_Data :Points [lon,lat] (z)\n", + " .Points.Edge_Data :Points [lon,lat] (z)\n", + " .Points.Face_Data :Points [lon,lat] (z)" + ] + }, + "execution_count": 5, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "7525dfe8-6240-48aa-aef6-0c6bf325d4c3" + } + }, + "output_type": "execute_result" + } + ], + "source": [ + "(\n", + " uxds.uxgrid.plot(line_color=\"black\")\n", + " * uxds[\"random_data_node\"]\n", + " .plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"circle\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Node Data\")\n", + " * uxds[\"random_data_edge\"]\n", + " .plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"square\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Edge Data\")\n", + " * uxds[\"random_data_face\"]\n", + " .plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"triangle\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Face Data\")\n", + ").opts(legend_position=\"top_right\")" + ] + }, + { + "cell_type": "markdown", + "id": "584207e271b49e06", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Node Aggregations\n", + "\n", + "The follow aggregations are for node-centered data." + ] + }, + { + "cell_type": "markdown", + "id": "62136ed1b4e4d52e", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Node to Face\n", + "\n", + "We can aggregate the data from the nodes that surround each face and store the result on each face.\n", + "\n", + "\"Optional" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6cdf74072b2c2b43", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "uxda_node_face_agg = uxds[\"random_data_node\"].topological_mean(destination=\"face\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "fdea0dda96ebe09d", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Path.I :Path [Longitude,Latitude]\n", + " .Points.Node_Data :Points [lon,lat] (z)\n", + " .Points.Node_to_Face_Mean :Points [lon,lat] (z)" + ] + }, + "execution_count": 5, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1164" + } + }, + "output_type": "execute_result" + } + ], + "source": [ + "(\n", + " uxds.uxgrid.plot(line_color=\"black\")\n", + " * uxds[\"random_data_node\"]\n", + " .plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"circle\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Node Data\")\n", + " * uxda_node_face_agg.plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"triangle\", clabel=None, tools=[\"hover\"]\n", + " ).relabel(\"Node to Face Mean\")\n", + ").opts(title=\"Node to Face Aggregation (Mean)\", legend_position=\"top_right\")" + ] + }, + { + "cell_type": "markdown", + "id": "e8f959f893e838ff", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "One use case for aggregating node-centered data to each face is that it allows for the result to be plotted as Polygons." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e7c7780435202338", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":DynamicMap []\n", + " :Image [Longitude,Latitude] (Longitude_Latitude random_data_node)" + ] + }, + "execution_count": 6, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1293" + } + }, + "output_type": "execute_result" + } + ], + "source": [ + "uxda_node_face_agg.plot.polygons(\n", + " cmap=\"inferno\",\n", + " title=\"Polygon Plot of Node to Face Aggregation (Mean)\",\n", + " tools=[\"hover\"],\n", + ").opts(title=\"Node to Face Aggregation (Mean)\")" + ] + }, + { + "cell_type": "markdown", + "id": "71b10f1a1f0a0939", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Node to Edge\n", + "\n", + "We can aggregate the data from the nodes that saddle each edge and store the result on each edge.\n", + "\n", + "\"Optional" + ] + }, + { + "cell_type": "markdown", + "id": "19b624f42cbdc16b", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "For a node-centered data variable, we can set `destination=\"edge\"` to specify that the aggregation should be performed on the nodes that saddle each edge, with the result stored on each edge." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "db5c4cce296023a", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "uxda_node_edge_agg = uxds[\"random_data_node\"].topological_mean(destination=\"edge\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4a3c7b486e5d78d", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Path.I :Path [Longitude,Latitude]\n", + " .Points.Node_Data :Points [lon,lat] (z)\n", + " .Points.Node_to_Edge_Mean :Points [lon,lat] (z)" + ] + }, + "execution_count": 8, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1370" + } + }, + "output_type": "execute_result" + } + ], + "source": [ + "(\n", + " uxds.uxgrid.plot(line_color=\"black\")\n", + " * uxds[\"random_data_node\"]\n", + " .plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"circle\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Node Data\")\n", + " * uxda_node_edge_agg.plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"square\", clabel=None, tools=[\"hover\"]\n", + " ).relabel(\"Node to Edge Mean\")\n", + ").opts(title=\"Node to Edge Aggregation (Mean)\", legend_position=\"top_right\")" + ] + }, + { + "cell_type": "markdown", + "id": "448ac6705a18f85b", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Edge Aggregations\n", + "\n", + "The follow aggregations are for edge-centered data. \n", + "\n", + "```{warning}\n", + "Aggregation of edge-centered data is not yet supported in UXarray. \n", + "```\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "357fe2f645bf3d4e", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Edge to Node\n", + "\n", + "We can aggregate the data from the edges that surround each node and store the result on each node.\n", + "\n", + "\"Optional" + ] + }, + { + "cell_type": "markdown", + "id": "86846522863860f5", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Edge to Face\n", + "\n", + "We can aggregate the data from the edges that surround each face and store the result on each face.\n", + "\n", + "\"Optional" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b9769693-62a8-4566-95cc-6a8078358bf5", + "metadata": {}, + "outputs": [], + "source": [ + "uxda_edge_face_agg = uxds[\"random_data_edge\"].topological_mean(destination=\"face\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f2bc4883-ea2c-47c6-8305-b5b06224a56e", + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Path.I :Path [Longitude,Latitude]\n", + " .Points.Node_Data :Points [lon,lat] (z)\n", + " .Points.Node_to_Edge_Mean :Points [lon,lat] (z)" + ] + }, + "execution_count": 5, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1011" + } + }, + "output_type": "execute_result" + } + ], + "source": [ + "(\n", + " uxds.uxgrid.plot(line_color=\"black\")\n", + " * uxds[\"random_data_edge\"]\n", + " .plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"circle\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Node Data\")\n", + " * uxda_edge_face_agg.plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"square\", clabel=None, tools=[\"hover\"]\n", + " ).relabel(\"Node to Edge Mean\")\n", + ").opts(title=\"Node to Edge Aggregation (Mean)\", legend_position=\"top_right\")" + ] + }, + { + "cell_type": "markdown", + "id": "7dd482e719e7d775", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Face Aggregations\n", + "\n", + "The following aggregations are for face-centered data.\n", + "\n", + "```{warning}\n", + "Aggregation of face-centered data is not yet supported in UXarray. \n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "id": "29ebe5d21bbcc46b", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Face to Node\n", + "\n", + "We can aggregate the data from the faces that surround each node and store the result on each node.\n", + "\n", + "\"Optional" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4fb30cee-69b9-4136-8a65-40cbd0d4f708", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The autoreload extension is already loaded. To reload it, use:\n", + " %reload_ext autoreload\n" + ] + }, + { + "ename": "NameError", + "evalue": "name 'uxds' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[2], line 4\u001b[0m\n\u001b[1;32m 1\u001b[0m get_ipython()\u001b[38;5;241m.\u001b[39mrun_line_magic(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mload_ext\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mautoreload\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 2\u001b[0m get_ipython()\u001b[38;5;241m.\u001b[39mrun_line_magic(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mautoreload\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m2\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m----> 4\u001b[0m uxda_face_node_agg \u001b[38;5;241m=\u001b[39m uxds[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrandom_data_face\u001b[39m\u001b[38;5;124m\"\u001b[39m]\u001b[38;5;241m.\u001b[39mtopological_mean(destination\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnode\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "\u001b[0;31mNameError\u001b[0m: name 'uxds' is not defined" + ] + } + ], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "uxda_face_node_agg = uxds[\"random_data_face\"].topological_mean(destination=\"node\")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "1609e8bef449a334", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Face to Edge\n", + "\n", + "We can aggregate the data from the faces that saddle each edge and store the result on each edge\n", + "\n", + "\"Optional" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/user-guide/topological-aggregations.ipynb b/docs/user-guide/topological-aggregations.ipynb index 73070cd8f..9cd0230be 100644 --- a/docs/user-guide/topological-aggregations.ipynb +++ b/docs/user-guide/topological-aggregations.ipynb @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "93d83a02a42e21c1", "metadata": { "collapsed": false, @@ -37,6 +37,44 @@ } }, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/aaronzedwick/uxarray/uxarray/io/_fesom2.py:95: SyntaxWarning: invalid escape sequence '\\s'\n", + " sep=\"\\s+\",\n", + "/Users/aaronzedwick/uxarray/uxarray/io/_fesom2.py:126: SyntaxWarning: invalid escape sequence '\\s'\n", + " sep=\"\\s+\",\n", + "/Users/aaronzedwick/uxarray/uxarray/io/_fesom2.py:156: SyntaxWarning: invalid escape sequence '\\s'\n", + " sep=\"\\s+\",\n", + "/Users/aaronzedwick/uxarray/uxarray/io/_fesom2.py:187: SyntaxWarning: invalid escape sequence '\\s'\n", + " sep=\"\\s+\",\n" + ] + }, + { + "data": { + "text/html": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "application/javascript": [ @@ -45,12 +83,13 @@ " return new Date();\n", " }\n", "\n", - " var force = true;\n", - " var py_version = '3.4.3'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", - " var reloading = false;\n", - " var Bokeh = root.Bokeh;\n", + " const force = true;\n", + " const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", + " const reloading = false;\n", + " const Bokeh = root.Bokeh;\n", "\n", - " if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n", + " // Set a timeout for this load but only if we are not already initializing\n", + " if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n", " root._bokeh_timeout = Date.now() + 5000;\n", " root._bokeh_failed_load = false;\n", " }\n", @@ -76,16 +115,14 @@ " root._bokeh_onload_callbacks.push(callback);\n", "\n", " if (root._bokeh_is_loading > 0) {\n", + " // Don't load bokeh if it is still initializing\n", " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", " return null;\n", - " }\n", - " if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n", + " } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n", + " // There is nothing to load\n", " run_callbacks();\n", " return null;\n", " }\n", - " if (!reloading) {\n", - " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", - " }\n", "\n", " function on_load() {\n", " root._bokeh_is_loading--;\n", @@ -96,11 +133,12 @@ " }\n", " window._bokeh_on_load = on_load\n", "\n", - " function on_error() {\n", - " console.error(\"failed to load \" + url);\n", + " function on_error(e) {\n", + " const src_el = e.srcElement\n", + " console.error(\"failed to load \" + (src_el.href || src_el.src));\n", " }\n", "\n", - " var skip = [];\n", + " const skip = [];\n", " if (window.requirejs) {\n", " window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n", " root._bokeh_is_loading = css_urls.length + 0;\n", @@ -108,19 +146,20 @@ " root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n", " }\n", "\n", - " var existing_stylesheets = []\n", - " var links = document.getElementsByTagName('link')\n", - " for (var i = 0; i < links.length; i++) {\n", - " var link = links[i]\n", + " const existing_stylesheets = []\n", + " const links = document.getElementsByTagName('link')\n", + " for (let i = 0; i < links.length; i++) {\n", + " const link = links[i]\n", " if (link.href != null) {\n", - "\texisting_stylesheets.push(link.href)\n", + " existing_stylesheets.push(link.href)\n", " }\n", " }\n", - " for (var i = 0; i < css_urls.length; i++) {\n", - " var url = css_urls[i];\n", - " if (existing_stylesheets.indexOf(url) !== -1) {\n", - "\ton_load()\n", - "\tcontinue;\n", + " for (let i = 0; i < css_urls.length; i++) {\n", + " const url = css_urls[i];\n", + " const escaped = encodeURI(url)\n", + " if (existing_stylesheets.indexOf(escaped) !== -1) {\n", + " on_load()\n", + " continue;\n", " }\n", " const element = document.createElement(\"link\");\n", " element.onload = on_load;\n", @@ -131,22 +170,23 @@ " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", " document.body.appendChild(element);\n", " } var existing_scripts = []\n", - " var scripts = document.getElementsByTagName('script')\n", - " for (var i = 0; i < scripts.length; i++) {\n", + " const scripts = document.getElementsByTagName('script')\n", + " for (let i = 0; i < scripts.length; i++) {\n", " var script = scripts[i]\n", " if (script.src != null) {\n", - "\texisting_scripts.push(script.src)\n", + " existing_scripts.push(script.src)\n", " }\n", " }\n", - " for (var i = 0; i < js_urls.length; i++) {\n", - " var url = js_urls[i];\n", - " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", - "\tif (!window.requirejs) {\n", - "\t on_load();\n", - "\t}\n", - "\tcontinue;\n", + " for (let i = 0; i < js_urls.length; i++) {\n", + " const url = js_urls[i];\n", + " const escaped = encodeURI(url)\n", + " if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n", + " if (!window.requirejs) {\n", + " on_load();\n", + " }\n", + " continue;\n", " }\n", - " var element = document.createElement('script');\n", + " const element = document.createElement('script');\n", " element.onload = on_load;\n", " element.onerror = on_error;\n", " element.async = false;\n", @@ -154,13 +194,14 @@ " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", " document.head.appendChild(element);\n", " }\n", - " for (var i = 0; i < js_modules.length; i++) {\n", - " var url = js_modules[i];\n", - " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", - "\tif (!window.requirejs) {\n", - "\t on_load();\n", - "\t}\n", - "\tcontinue;\n", + " for (let i = 0; i < js_modules.length; i++) {\n", + " const url = js_modules[i];\n", + " const escaped = encodeURI(url)\n", + " if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n", + " if (!window.requirejs) {\n", + " on_load();\n", + " }\n", + " continue;\n", " }\n", " var element = document.createElement('script');\n", " element.onload = on_load;\n", @@ -172,12 +213,13 @@ " document.head.appendChild(element);\n", " }\n", " for (const name in js_exports) {\n", - " var url = js_exports[name];\n", - " if (skip.indexOf(url) >= 0 || root[name] != null) {\n", - "\tif (!window.requirejs) {\n", - "\t on_load();\n", - "\t}\n", - "\tcontinue;\n", + " const url = js_exports[name];\n", + " const escaped = encodeURI(url)\n", + " if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n", + " if (!window.requirejs) {\n", + " on_load();\n", + " }\n", + " continue;\n", " }\n", " var element = document.createElement('script');\n", " element.onerror = on_error;\n", @@ -202,11 +244,11 @@ " document.body.appendChild(element);\n", " }\n", "\n", - " var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.4.3.min.js\", \"https://cdn.holoviz.org/panel/1.4.4/dist/panel.min.js\"];\n", - " var js_modules = [];\n", - " var js_exports = {};\n", - " var css_urls = [];\n", - " var inline_js = [ function(Bokeh) {\n", + " const js_urls = [\"https://cdn.holoviz.org/panel/1.5.4/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.4/dist/panel.min.js\"];\n", + " const js_modules = [];\n", + " const js_exports = {};\n", + " const css_urls = [];\n", + " const inline_js = [ function(Bokeh) {\n", " Bokeh.set_log_level(\"info\");\n", " },\n", "function(Bokeh) {} // ensure no trailing comma for IE\n", @@ -214,26 +256,27 @@ "\n", " function run_inline_js() {\n", " if ((root.Bokeh !== undefined) || (force === true)) {\n", - " for (var i = 0; i < inline_js.length; i++) {\n", - "\ttry {\n", + " for (let i = 0; i < inline_js.length; i++) {\n", + " try {\n", " inline_js[i].call(root, root.Bokeh);\n", - "\t} catch(e) {\n", - "\t if (!reloading) {\n", - "\t throw e;\n", - "\t }\n", - "\t}\n", + " } catch(e) {\n", + " if (!reloading) {\n", + " throw e;\n", + " }\n", + " }\n", " }\n", " // Cache old bokeh versions\n", " if (Bokeh != undefined && !reloading) {\n", - "\tvar NewBokeh = root.Bokeh;\n", - "\tif (Bokeh.versions === undefined) {\n", - "\t Bokeh.versions = new Map();\n", - "\t}\n", - "\tif (NewBokeh.version !== Bokeh.version) {\n", - "\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n", - "\t}\n", - "\troot.Bokeh = Bokeh;\n", - " }} else if (Date.now() < root._bokeh_timeout) {\n", + " var NewBokeh = root.Bokeh;\n", + " if (Bokeh.versions === undefined) {\n", + " Bokeh.versions = new Map();\n", + " }\n", + " if (NewBokeh.version !== Bokeh.version) {\n", + " Bokeh.versions.set(NewBokeh.version, NewBokeh)\n", + " }\n", + " root.Bokeh = Bokeh;\n", + " }\n", + " } else if (Date.now() < root._bokeh_timeout) {\n", " setTimeout(run_inline_js, 100);\n", " } else if (!root._bokeh_failed_load) {\n", " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", @@ -251,8 +294,12 @@ " // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n", " // before older versions are fully initialized.\n", " if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n", + " // If the timeout and bokeh was not successfully loaded we reset\n", + " // everything and try loading again\n", + " root._bokeh_timeout = Date.now() + 5000;\n", " root._bokeh_is_initializing = false;\n", " root._bokeh_onload_callbacks = undefined;\n", + " root._bokeh_is_loading = 0\n", " console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n", " load_or_wait();\n", " } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n", @@ -260,13 +307,16 @@ " } else {\n", " root._bokeh_is_initializing = true\n", " root._bokeh_onload_callbacks = []\n", - " var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n", + " const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n", " if (!reloading && !bokeh_loaded) {\n", - "\troot.Bokeh = undefined;\n", + " if (root.Bokeh) {\n", + " root.Bokeh = undefined;\n", + " }\n", + " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", " }\n", " load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n", - "\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", - "\trun_inline_js();\n", + " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + " run_inline_js();\n", " });\n", " }\n", " }\n", @@ -275,7 +325,7 @@ " setTimeout(load_or_wait, 100)\n", "}(window));" ], - "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n var py_version = '3.4.3'.replace('rc', '-rc.').replace('.dev', '-dev.');\n var reloading = false;\n var Bokeh = root.Bokeh;\n\n if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n run_callbacks();\n return null;\n }\n if (!reloading) {\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n var skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n var existing_stylesheets = []\n var links = document.getElementsByTagName('link')\n for (var i = 0; i < links.length; i++) {\n var link = links[i]\n if (link.href != null) {\n\texisting_stylesheets.push(link.href)\n }\n }\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n if (existing_stylesheets.indexOf(url) !== -1) {\n\ton_load()\n\tcontinue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n var scripts = document.getElementsByTagName('script')\n for (var i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n\texisting_scripts.push(script.src)\n }\n }\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (var i = 0; i < js_modules.length; i++) {\n var url = js_modules[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n var url = js_exports[name];\n if (skip.indexOf(url) >= 0 || root[name] != null) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.4.3.min.js\", \"https://cdn.holoviz.org/panel/1.4.4/dist/panel.min.js\"];\n var js_modules = [];\n var js_exports = {};\n var css_urls = [];\n var inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (var i = 0; i < inline_js.length; i++) {\n\ttry {\n inline_js[i].call(root, root.Bokeh);\n\t} catch(e) {\n\t if (!reloading) {\n\t throw e;\n\t }\n\t}\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n\tvar NewBokeh = root.Bokeh;\n\tif (Bokeh.versions === undefined) {\n\t Bokeh.versions = new Map();\n\t}\n\tif (NewBokeh.version !== Bokeh.version) {\n\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n\t}\n\troot.Bokeh = Bokeh;\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n\troot.Bokeh = undefined;\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n\trun_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" + "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.5.4/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.4/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" }, "metadata": {}, "output_type": "display_data" @@ -528,40 +578,16 @@ "metadata": {}, "output_type": "display_data" }, - { - "data": { - "text/html": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, { "data": { "application/vnd.holoviews_exec.v0+json": "", "text/html": [ - "
\n", - "
\n", + "
\n", + "
\n", "
\n", "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": [ - "(function(root) {\n", - " function now() {\n", - " return new Date();\n", - " }\n", - "\n", - " const force = true;\n", - " const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", - " const reloading = false;\n", - " const Bokeh = root.Bokeh;\n", - "\n", - " // Set a timeout for this load but only if we are not already initializing\n", - " if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n", - " root._bokeh_timeout = Date.now() + 5000;\n", - " root._bokeh_failed_load = false;\n", - " }\n", - "\n", - " function run_callbacks() {\n", - " try {\n", - " root._bokeh_onload_callbacks.forEach(function(callback) {\n", - " if (callback != null)\n", - " callback();\n", - " });\n", - " } finally {\n", - " delete root._bokeh_onload_callbacks;\n", - " }\n", - " console.debug(\"Bokeh: all callbacks have finished\");\n", - " }\n", - "\n", - " function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n", - " if (css_urls == null) css_urls = [];\n", - " if (js_urls == null) js_urls = [];\n", - " if (js_modules == null) js_modules = [];\n", - " if (js_exports == null) js_exports = {};\n", - "\n", - " root._bokeh_onload_callbacks.push(callback);\n", - "\n", - " if (root._bokeh_is_loading > 0) {\n", - " // Don't load bokeh if it is still initializing\n", - " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", - " return null;\n", - " } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n", - " // There is nothing to load\n", - " run_callbacks();\n", - " return null;\n", - " }\n", - "\n", - " function on_load() {\n", - " root._bokeh_is_loading--;\n", - " if (root._bokeh_is_loading === 0) {\n", - " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", - " run_callbacks()\n", - " }\n", - " }\n", - " window._bokeh_on_load = on_load\n", - "\n", - " function on_error(e) {\n", - " const src_el = e.srcElement\n", - " console.error(\"failed to load \" + (src_el.href || src_el.src));\n", - " }\n", - "\n", - " const skip = [];\n", - " if (window.requirejs) {\n", - " window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n", - " root._bokeh_is_loading = css_urls.length + 0;\n", - " } else {\n", - " root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n", - " }\n", - "\n", - " const existing_stylesheets = []\n", - " const links = document.getElementsByTagName('link')\n", - " for (let i = 0; i < links.length; i++) {\n", - " const link = links[i]\n", - " if (link.href != null) {\n", - " existing_stylesheets.push(link.href)\n", - " }\n", - " }\n", - " for (let i = 0; i < css_urls.length; i++) {\n", - " const url = css_urls[i];\n", - " const escaped = encodeURI(url)\n", - " if (existing_stylesheets.indexOf(escaped) !== -1) {\n", - " on_load()\n", - " continue;\n", - " }\n", - " const element = document.createElement(\"link\");\n", - " element.onload = on_load;\n", - " element.onerror = on_error;\n", - " element.rel = \"stylesheet\";\n", - " element.type = \"text/css\";\n", - " element.href = url;\n", - " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", - " document.body.appendChild(element);\n", - " } var existing_scripts = []\n", - " const scripts = document.getElementsByTagName('script')\n", - " for (let i = 0; i < scripts.length; i++) {\n", - " var script = scripts[i]\n", - " if (script.src != null) {\n", - " existing_scripts.push(script.src)\n", - " }\n", - " }\n", - " for (let i = 0; i < js_urls.length; i++) {\n", - " const url = js_urls[i];\n", - " const escaped = encodeURI(url)\n", - " if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n", - " if (!window.requirejs) {\n", - " on_load();\n", - " }\n", - " continue;\n", - " }\n", - " const element = document.createElement('script');\n", - " element.onload = on_load;\n", - " element.onerror = on_error;\n", - " element.async = false;\n", - " element.src = url;\n", - " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", - " document.head.appendChild(element);\n", - " }\n", - " for (let i = 0; i < js_modules.length; i++) {\n", - " const url = js_modules[i];\n", - " const escaped = encodeURI(url)\n", - " if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n", - " if (!window.requirejs) {\n", - " on_load();\n", - " }\n", - " continue;\n", - " }\n", - " var element = document.createElement('script');\n", - " element.onload = on_load;\n", - " element.onerror = on_error;\n", - " element.async = false;\n", - " element.src = url;\n", - " element.type = \"module\";\n", - " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", - " document.head.appendChild(element);\n", - " }\n", - " for (const name in js_exports) {\n", - " const url = js_exports[name];\n", - " const escaped = encodeURI(url)\n", - " if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n", - " if (!window.requirejs) {\n", - " on_load();\n", - " }\n", - " continue;\n", - " }\n", - " var element = document.createElement('script');\n", - " element.onerror = on_error;\n", - " element.async = false;\n", - " element.type = \"module\";\n", - " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", - " element.textContent = `\n", - " import ${name} from \"${url}\"\n", - " window.${name} = ${name}\n", - " window._bokeh_on_load()\n", - " `\n", - " document.head.appendChild(element);\n", - " }\n", - " if (!js_urls.length && !js_modules.length) {\n", - " on_load()\n", - " }\n", - " };\n", - "\n", - " function inject_raw_css(css) {\n", - " const element = document.createElement(\"style\");\n", - " element.appendChild(document.createTextNode(css));\n", - " document.body.appendChild(element);\n", - " }\n", - "\n", - " const js_urls = [\"https://cdn.holoviz.org/panel/1.5.4/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.4/dist/panel.min.js\"];\n", - " const js_modules = [];\n", - " const js_exports = {};\n", - " const css_urls = [];\n", - " const inline_js = [ function(Bokeh) {\n", - " Bokeh.set_log_level(\"info\");\n", - " },\n", - "function(Bokeh) {} // ensure no trailing comma for IE\n", - " ];\n", - "\n", - " function run_inline_js() {\n", - " if ((root.Bokeh !== undefined) || (force === true)) {\n", - " for (let i = 0; i < inline_js.length; i++) {\n", - " try {\n", - " inline_js[i].call(root, root.Bokeh);\n", - " } catch(e) {\n", - " if (!reloading) {\n", - " throw e;\n", - " }\n", - " }\n", - " }\n", - " // Cache old bokeh versions\n", - " if (Bokeh != undefined && !reloading) {\n", - " var NewBokeh = root.Bokeh;\n", - " if (Bokeh.versions === undefined) {\n", - " Bokeh.versions = new Map();\n", - " }\n", - " if (NewBokeh.version !== Bokeh.version) {\n", - " Bokeh.versions.set(NewBokeh.version, NewBokeh)\n", - " }\n", - " root.Bokeh = Bokeh;\n", - " }\n", - " } else if (Date.now() < root._bokeh_timeout) {\n", - " setTimeout(run_inline_js, 100);\n", - " } else if (!root._bokeh_failed_load) {\n", - " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", - " root._bokeh_failed_load = true;\n", - " }\n", - " root._bokeh_is_initializing = false\n", - " }\n", - "\n", - " function load_or_wait() {\n", - " // Implement a backoff loop that tries to ensure we do not load multiple\n", - " // versions of Bokeh and its dependencies at the same time.\n", - " // In recent versions we use the root._bokeh_is_initializing flag\n", - " // to determine whether there is an ongoing attempt to initialize\n", - " // bokeh, however for backward compatibility we also try to ensure\n", - " // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n", - " // before older versions are fully initialized.\n", - " if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n", - " // If the timeout and bokeh was not successfully loaded we reset\n", - " // everything and try loading again\n", - " root._bokeh_timeout = Date.now() + 5000;\n", - " root._bokeh_is_initializing = false;\n", - " root._bokeh_onload_callbacks = undefined;\n", - " root._bokeh_is_loading = 0\n", - " console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n", - " load_or_wait();\n", - " } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n", - " setTimeout(load_or_wait, 100);\n", - " } else {\n", - " root._bokeh_is_initializing = true\n", - " root._bokeh_onload_callbacks = []\n", - " const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n", - " if (!reloading && !bokeh_loaded) {\n", - " if (root.Bokeh) {\n", - " root.Bokeh = undefined;\n", - " }\n", - " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", - " }\n", - " load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n", - " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", - " run_inline_js();\n", - " });\n", - " }\n", - " }\n", - " // Give older versions of the autoload script a head-start to ensure\n", - " // they initialize before we start loading newer version.\n", - " setTimeout(load_or_wait, 100)\n", - "}(window));" - ], - "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.5.4/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.4/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": [ - "\n", - "if ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n", - " window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n", - "}\n", - "\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", - " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", - " google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n", - " var messages = comm.messages[Symbol.asyncIterator]();\n", - " function processIteratorResult(result) {\n", - " var message = result.value;\n", - " console.log(message)\n", - " var content = {data: message.data, comm_id};\n", - " var buffers = []\n", - " for (var buffer of message.buffers || []) {\n", - " buffers.push(new DataView(buffer))\n", - " }\n", - " var metadata = message.metadata || {};\n", - " var msg = {content, buffers, metadata}\n", - " msg_handler(msg);\n", - " return messages.next().then(processIteratorResult);\n", - " }\n", - " return messages.next().then(processIteratorResult);\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", - " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", - " var comm_promise = google.colab.kernel.comms.open(comm_id)\n", - " comm_promise.then((comm) => {\n", - " window.PyViz.comms[comm_id] = comm;\n", - " if (msg_handler) {\n", - " var messages = comm.messages[Symbol.asyncIterator]();\n", - " function processIteratorResult(result) {\n", - " var message = result.value;\n", - " var content = {data: message.data};\n", - " var metadata = message.metadata || {comm_id};\n", - " var msg = {content, metadata}\n", - " msg_handler(msg);\n", - " return messages.next().then(processIteratorResult);\n", - " }\n", - " return messages.next().then(processIteratorResult);\n", - " }\n", - " }) \n", - " var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n", - " return comm_promise.then((comm) => {\n", - " comm.send(data, metadata, buffers, disposeOnDone);\n", - " });\n", - " };\n", - " var comm = {\n", - " send: sendClosure\n", - " };\n", - " }\n", - " window.PyViz.comms[comm_id] = comm;\n", - " return comm;\n", - " }\n", - " window.PyViz.comm_manager = new JupyterCommManager();\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 == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", - " return\n", - " }\n", - " var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", - " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", - " if (id !== undefined) {\n", - " var nchildren = toinsert.length;\n", - " var html_node = toinsert[nchildren-1].children[0];\n", - " html_node.innerHTML = output.data[HTML_MIME_TYPE];\n", - " var scripts = [];\n", - " var nodelist = html_node.querySelectorAll(\"script\");\n", - " for (var i in nodelist) {\n", - " if (nodelist.hasOwnProperty(i)) {\n", - " scripts.push(nodelist[i])\n", - " }\n", - " }\n", - "\n", - " scripts.forEach( function (oldScript) {\n", - " var newScript = document.createElement(\"script\");\n", - " var attrs = [];\n", - " var nodemap = oldScript.attributes;\n", - " for (var j in nodemap) {\n", - " if (nodemap.hasOwnProperty(j)) {\n", - " attrs.push(nodemap[j])\n", - " }\n", - " }\n", - " attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n", - " newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n", - " oldScript.parentNode.replaceChild(newScript, oldScript);\n", - " });\n", - " if (JS_MIME_TYPE in output.data) {\n", - " toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n", - " }\n", - " output_area._hv_plot_id = id;\n", - " if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n", - " window.PyViz.plot_index[id] = Bokeh.index[id];\n", - " } else {\n", - " window.PyViz.plot_index[id] = null;\n", - " }\n", - " } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", - " var bk_div = document.createElement(\"div\");\n", - " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", - " var script_attrs = bk_div.children[0].attributes;\n", - " for (var i = 0; i < script_attrs.length; i++) {\n", - " toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n", - " }\n", - " // store reference to server id on output_area\n", - " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", - " }\n", - "}\n", - "\n", - "/**\n", - " * Handle when an output is cleared or removed\n", - " */\n", - "function handle_clear_output(event, handle) {\n", - " var id = handle.cell.output_area._hv_plot_id;\n", - " var server_id = handle.cell.output_area._bokeh_server_id;\n", - " if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n", - " var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n", - " if (server_id !== null) {\n", - " comm.send({event_type: 'server_delete', 'id': server_id});\n", - " return;\n", - " } else if (comm !== null) {\n", - " comm.send({event_type: 'delete', 'id': id});\n", - " }\n", - " delete PyViz.plot_index[id];\n", - " if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n", - " var doc = window.Bokeh.index[id].model.document\n", - " doc.clear();\n", - " const i = window.Bokeh.documents.indexOf(doc);\n", - " if (i > -1) {\n", - " window.Bokeh.documents.splice(i, 1);\n", - " }\n", - " }\n", - "}\n", - "\n", - "/**\n", - " * Handle kernel restart event\n", - " */\n", - "function handle_kernel_cleanup(event, handle) {\n", - " delete PyViz.comms[\"hv-extension-comm\"];\n", - " window.PyViz.plot_index = {}\n", - "}\n", - "\n", - "/**\n", - " * Handle update_display_data messages\n", - " */\n", - "function handle_update_output(event, handle) {\n", - " handle_clear_output(event, {cell: {output_area: handle.output_area}})\n", - " handle_add_output(event, handle)\n", - "}\n", - "\n", - "function register_renderer(events, OutputArea) {\n", - " function append_mime(data, metadata, element) {\n", - " // create a DOM node to render to\n", - " var toinsert = this.create_output_subarea(\n", - " metadata,\n", - " CLASS_NAME,\n", - " EXEC_MIME_TYPE\n", - " );\n", - " this.keyboard_manager.register_events(toinsert);\n", - " // Render to node\n", - " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", - " render(props, toinsert[0]);\n", - " element.append(toinsert);\n", - " return toinsert\n", - " }\n", - "\n", - " events.on('output_added.OutputArea', handle_add_output);\n", - " events.on('output_updated.OutputArea', handle_update_output);\n", - " events.on('clear_output.CodeCell', handle_clear_output);\n", - " events.on('delete.Cell', handle_clear_output);\n", - " events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n", - "\n", - " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", - " safe: true,\n", - " index: 0\n", - " });\n", - "}\n", - "\n", - "if (window.Jupyter !== undefined) {\n", - " try {\n", - " var events = require('base/js/events');\n", - " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", - " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", - " register_renderer(events, OutputArea);\n", - " }\n", - " } catch(err) {\n", - " }\n", - "}\n" - ], - "application/vnd.holoviews_load.v0+json": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\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 } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\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 } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\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 == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.holoviews_exec.v0+json": "", - "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ] - }, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "898e82d6-5f7b-448c-adf4-c5f2a8164021" - } - }, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import uxarray as ux" ] @@ -748,7 +123,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "a0c9a0f19efd0633", "metadata": { "collapsed": false, @@ -756,1341 +131,7 @@ "outputs_hidden": false } }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.UxDataset> Size: 312B\n",
-       "Dimensions:           (n_edge: 19, n_face: 4, n_node: 16)\n",
-       "Dimensions without coordinates: n_edge, n_face, n_node\n",
-       "Data variables:\n",
-       "    random_data_edge  (n_edge) float64 152B dask.array<chunksize=(19,), meta=np.ndarray>\n",
-       "    random_data_face  (n_face) float64 32B dask.array<chunksize=(4,), meta=np.ndarray>\n",
-       "    random_data_node  (n_node) float64 128B dask.array<chunksize=(16,), meta=np.ndarray>
" - ], - "text/plain": [ - " Size: 312B\n", - "Dimensions: (n_edge: 19, n_face: 4, n_node: 16)\n", - "Dimensions without coordinates: n_edge, n_face, n_node\n", - "Data variables:\n", - " random_data_edge (n_edge) float64 152B dask.array\n", - " random_data_face (n_face) float64 32B dask.array\n", - " random_data_node (n_node) float64 128B dask.array" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "grid_path = \"../../test/meshfiles/ugrid/quad-hexagon/grid.nc\"\n", "\n", @@ -2120,7 +161,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "4439706a33f61576", "metadata": { "collapsed": false, @@ -2128,104 +169,7 @@ "outputs_hidden": false } }, - "outputs": [ - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.holoviews_exec.v0+json": "", - "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ], - "text/plain": [ - ":Overlay\n", - " .Path.I :Path [Longitude,Latitude]\n", - " .Points.Node_Data :Points [lon,lat] (z)\n", - " .Points.Edge_Data :Points [lon,lat] (z)\n", - " .Points.Face_Data :Points [lon,lat] (z)" - ] - }, - "execution_count": 5, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "7525dfe8-6240-48aa-aef6-0c6bf325d4c3" - } - }, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "(\n", " uxds.uxgrid.plot(line_color=\"black\")\n", @@ -2281,7 +225,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "6cdf74072b2c2b43", "metadata": { "collapsed": false, @@ -2296,7 +240,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "fdea0dda96ebe09d", "metadata": { "collapsed": false, @@ -2304,103 +248,7 @@ "outputs_hidden": false } }, - "outputs": [ - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.holoviews_exec.v0+json": "", - "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ], - "text/plain": [ - ":Overlay\n", - " .Path.I :Path [Longitude,Latitude]\n", - " .Points.Node_Data :Points [lon,lat] (z)\n", - " .Points.Node_to_Face_Mean :Points [lon,lat] (z)" - ] - }, - "execution_count": 5, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "p1164" - } - }, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "(\n", " uxds.uxgrid.plot(line_color=\"black\")\n", @@ -2430,7 +278,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "e7c7780435202338", "metadata": { "collapsed": false, @@ -2438,101 +286,7 @@ "outputs_hidden": false } }, - "outputs": [ - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.holoviews_exec.v0+json": "", - "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ], - "text/plain": [ - ":DynamicMap []\n", - " :Image [Longitude,Latitude] (Longitude_Latitude random_data_node)" - ] - }, - "execution_count": 6, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "p1293" - } - }, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "uxda_node_face_agg.plot.polygons(\n", " cmap=\"inferno\",\n", @@ -2573,7 +327,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "db5c4cce296023a", "metadata": { "collapsed": false, @@ -2588,7 +342,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "4a3c7b486e5d78d", "metadata": { "collapsed": false, @@ -2596,103 +350,7 @@ "outputs_hidden": false } }, - "outputs": [ - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.holoviews_exec.v0+json": "", - "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ], - "text/plain": [ - ":Overlay\n", - " .Path.I :Path [Longitude,Latitude]\n", - " .Points.Node_Data :Points [lon,lat] (z)\n", - " .Points.Node_to_Edge_Mean :Points [lon,lat] (z)" - ] - }, - "execution_count": 8, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "p1370" - } - }, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "(\n", " uxds.uxgrid.plot(line_color=\"black\")\n", @@ -2763,7 +421,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "b9769693-62a8-4566-95cc-6a8078358bf5", "metadata": {}, "outputs": [], @@ -2773,106 +431,10 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "f2bc4883-ea2c-47c6-8305-b5b06224a56e", "metadata": {}, - "outputs": [ - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.holoviews_exec.v0+json": "", - "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ], - "text/plain": [ - ":Overlay\n", - " .Path.I :Path [Longitude,Latitude]\n", - " .Points.Node_Data :Points [lon,lat] (z)\n", - " .Points.Node_to_Edge_Mean :Points [lon,lat] (z)" - ] - }, - "execution_count": 5, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "p1011" - } - }, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "(\n", " uxds.uxgrid.plot(line_color=\"black\")\n", @@ -2899,11 +461,7 @@ "source": [ "## Face Aggregations\n", "\n", - "The following aggregations are for face-centered data.\n", - "\n", - "```{warning}\n", - "Aggregation of face-centered data is not yet supported in UXarray. \n", - "```\n" + "The following aggregations are for face-centered data." ] }, { @@ -2925,40 +483,32 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "4fb30cee-69b9-4136-8a65-40cbd0d4f708", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The autoreload extension is already loaded. To reload it, use:\n", - " %reload_ext autoreload\n" - ] - }, - { - "ename": "IndexError", - "evalue": "index 5 is out of bounds for axis 0 with size 4", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[7], line 4\u001b[0m\n\u001b[1;32m 1\u001b[0m get_ipython()\u001b[38;5;241m.\u001b[39mrun_line_magic(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mload_ext\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mautoreload\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 2\u001b[0m get_ipython()\u001b[38;5;241m.\u001b[39mrun_line_magic(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mautoreload\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m2\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m----> 4\u001b[0m uxda_face_node_agg \u001b[38;5;241m=\u001b[39m uxds[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrandom_data_face\u001b[39m\u001b[38;5;124m\"\u001b[39m]\u001b[38;5;241m.\u001b[39mtopological_mean(destination\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnode\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", - "File \u001b[0;32m~/uxarray/uxarray/core/dataarray.py:474\u001b[0m, in \u001b[0;36mUxDataArray.topological_mean\u001b[0;34m(self, destination, **kwargs)\u001b[0m\n\u001b[1;32m 432\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mtopological_mean\u001b[39m(\n\u001b[1;32m 433\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 434\u001b[0m destination: Literal[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnode\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124medge\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m\"\u001b[39m],\n\u001b[1;32m 435\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[1;32m 436\u001b[0m ):\n\u001b[1;32m 437\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Performs a topological mean aggregation.\u001b[39;00m\n\u001b[1;32m 438\u001b[0m \n\u001b[1;32m 439\u001b[0m \u001b[38;5;124;03m See Also\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 472\u001b[0m \u001b[38;5;124;03m New UxDataArray with ``mean`` applied to its data.\u001b[39;00m\n\u001b[1;32m 473\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 474\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m _uxda_grid_aggregate(\u001b[38;5;28mself\u001b[39m, destination, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmean\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n", - "File \u001b[0;32m~/uxarray/uxarray/core/aggregation.py:63\u001b[0m, in \u001b[0;36m_uxda_grid_aggregate\u001b[0;34m(uxda, destination, aggregation, **kwargs)\u001b[0m\n\u001b[1;32m 57\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m uxda\u001b[38;5;241m.\u001b[39m_face_centered():\n\u001b[1;32m 58\u001b[0m \u001b[38;5;66;03m# aggregation of a face-centered data variable\u001b[39;00m\n\u001b[1;32m 59\u001b[0m \u001b[38;5;66;03m# raise NotImplementedError(\u001b[39;00m\n\u001b[1;32m 60\u001b[0m \u001b[38;5;66;03m# \"Aggregation of face-centered data variables is not yet supported.\"\u001b[39;00m\n\u001b[1;32m 61\u001b[0m \u001b[38;5;66;03m# )\u001b[39;00m\n\u001b[1;32m 62\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m destination \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnode\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m---> 63\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m _face_to_node_aggregation(uxda, aggregation, kwargs)\n\u001b[1;32m 64\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m destination \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124medge\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 65\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n", - "File \u001b[0;32m~/uxarray/uxarray/core/aggregation.py:296\u001b[0m, in \u001b[0;36m_face_to_node_aggregation\u001b[0;34m(uxda, aggregation, aggregation_func_kwargs)\u001b[0m\n\u001b[1;32m 291\u001b[0m aggregated_var \u001b[38;5;241m=\u001b[39m _apply_edge_to_face_aggregation_numpy(\n\u001b[1;32m 292\u001b[0m uxda, NUMPY_AGGREGATIONS[aggregation], aggregation_func_kwargs\n\u001b[1;32m 293\u001b[0m )\n\u001b[1;32m 294\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(uxda\u001b[38;5;241m.\u001b[39mdata, da\u001b[38;5;241m.\u001b[39mArray):\n\u001b[1;32m 295\u001b[0m \u001b[38;5;66;03m# apply aggregation on dask array, TODO:\u001b[39;00m\n\u001b[0;32m--> 296\u001b[0m aggregated_var \u001b[38;5;241m=\u001b[39m _apply_edge_to_face_aggregation_numpy(\n\u001b[1;32m 297\u001b[0m uxda, NUMPY_AGGREGATIONS[aggregation], aggregation_func_kwargs\n\u001b[1;32m 298\u001b[0m )\n\u001b[1;32m 299\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 300\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m\n", - "File \u001b[0;32m~/uxarray/uxarray/core/aggregation.py:211\u001b[0m, in \u001b[0;36m_apply_edge_to_face_aggregation_numpy\u001b[0;34m(uxda, aggregation_func, aggregation_func_kwargs)\u001b[0m\n\u001b[1;32m 207\u001b[0m face_nodes_par \u001b[38;5;241m=\u001b[39m face_edge_conn[face_inds, \u001b[38;5;241m0\u001b[39m:e]\n\u001b[1;32m 209\u001b[0m \u001b[38;5;66;03m# apply aggregation function to current face edge partition\u001b[39;00m\n\u001b[1;32m 210\u001b[0m aggregation_par \u001b[38;5;241m=\u001b[39m aggregation_func(\n\u001b[0;32m--> 211\u001b[0m data[\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m, face_nodes_par], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39maggregation_func_kwargs\n\u001b[1;32m 212\u001b[0m )\n\u001b[1;32m 214\u001b[0m \u001b[38;5;66;03m# store current aggregation\u001b[39;00m\n\u001b[1;32m 215\u001b[0m result[\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m, face_inds] \u001b[38;5;241m=\u001b[39m aggregation_par\n", - "\u001b[0;31mIndexError\u001b[0m: index 5 is out of bounds for axis 0 with size 4" - ] - } - ], + "outputs": [], "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "uxda_face_node_agg = uxds[\"random_data_face\"].topological_mean(destination=\"node\")\n", - "\n" + "uxda_face_node_agg = uxds[\"random_data_face\"].topological_mean(destination=\"node\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e68c204f-ae92-4d2a-aada-696239b73f62", + "metadata": {}, + "outputs": [], + "source": [ + "(\n", + " uxds.uxgrid.plot(line_color=\"black\")\n", + " * uxds[\"random_data_face\"]\n", + " .plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"circle\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Face Data\")\n", + " * uxda_face_node_agg.plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"square\", clabel=None, tools=[\"hover\"]\n", + " ).relabel(\"Face to Node Mean\")\n", + ").opts(title=\"Face to Node Aggregation (Mean)\", legend_position=\"top_right\")" ] }, { diff --git a/uxarray/core/aggregation.py b/uxarray/core/aggregation.py index f796856f8..fbd41e284 100644 --- a/uxarray/core/aggregation.py +++ b/uxarray/core/aggregation.py @@ -261,7 +261,7 @@ def _apply_face_to_node_aggregation_numpy( size_counts, ) = get_face_node_partitions(n_nodes_per_face) - result = np.empty(shape=(data.shape[:-1]) + (uxda.uxgrid.n_face,)) + result = np.empty(shape=(data.shape[:-1]) + (uxda.uxgrid.n_node,)) for e, start, end in zip(element_sizes, change_ind[:-1], change_ind[1:]): face_inds = n_nodes_per_face_sorted_ind[start:end] diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index 425deab6d..00bafe8b9 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -54,7 +54,8 @@ _populate_n_nodes_per_face, _populate_node_face_connectivity, _populate_edge_face_connectivity, - _populate_face_face_connectivity, _populate_n_faces_per_node, + _populate_face_face_connectivity, + _populate_n_faces_per_node, ) from uxarray.grid.geometry import ( From 54fec18b1fa80881f1be2c652716a12f832380f8 Mon Sep 17 00:00:00 2001 From: Aaron Zedwick Date: Thu, 9 Jan 2025 13:47:41 -0600 Subject: [PATCH 5/8] Updated notebook --- docs/user-guide/topological-aggregations.ipynb | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/docs/user-guide/topological-aggregations.ipynb b/docs/user-guide/topological-aggregations.ipynb index ee20b4690..05653cbc9 100644 --- a/docs/user-guide/topological-aggregations.ipynb +++ b/docs/user-guide/topological-aggregations.ipynb @@ -377,12 +377,7 @@ "source": [ "## Edge Aggregations\n", "\n", - "The follow aggregations are for edge-centered data. \n", - "\n", - "```{warning}\n", - "Aggregation of edge-centered data is not yet supported in UXarray. \n", - "```\n", - "\n" + "The follow aggregations are for edge-centered data. " ] }, { @@ -442,11 +437,11 @@ " .plot.points(\n", " cmap=\"inferno\", size=150, marker=\"circle\", clabel=None, tools=[\"hover\"]\n", " )\n", - " .relabel(\"Node Data\")\n", + " .relabel(\"Edge Data\")\n", " * uxda_edge_face_agg.plot.points(\n", " cmap=\"inferno\", size=150, marker=\"square\", clabel=None, tools=[\"hover\"]\n", - " ).relabel(\"Node to Edge Mean\")\n", - ").opts(title=\"Node to Edge Aggregation (Mean)\", legend_position=\"top_right\")" + " ).relabel(\"Edge to Face Mean\")\n", + ").opts(title=\"Edge to Face Aggregation (Mean)\", legend_position=\"top_right\")" ] }, { From 816bc16e1800072ab79082c99e5a4dc941ef9c6e Mon Sep 17 00:00:00 2001 From: Aaron Zedwick Date: Thu, 9 Jan 2025 15:19:30 -0600 Subject: [PATCH 6/8] Added face to edge --- uxarray/core/aggregation.py | 78 ++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/uxarray/core/aggregation.py b/uxarray/core/aggregation.py index fbd41e284..ac0ada644 100644 --- a/uxarray/core/aggregation.py +++ b/uxarray/core/aggregation.py @@ -56,13 +56,10 @@ def _uxda_grid_aggregate(uxda, destination, aggregation, **kwargs): elif uxda._face_centered(): # aggregation of a face-centered data variable - # raise NotImplementedError( - # "Aggregation of face-centered data variables is not yet supported." - # ) if destination == "node": return _face_to_node_aggregation(uxda, aggregation, kwargs) elif destination == "edge": - pass + return _face_to_edge_aggregation(uxda, aggregation, kwargs) else: raise ValueError("TODO: ") @@ -249,22 +246,22 @@ def _edge_to_face_aggregation(uxda, aggregation, aggregation_func_kwargs): def _apply_face_to_node_aggregation_numpy( uxda, aggregation_func, aggregation_func_kwargs ): - """Applies an Edge to Face Topological aggregation on a Numpy array.""" + """Applies a Face to Node Topological aggregation on a Numpy array.""" data = uxda.values node_face_conn = uxda.uxgrid.node_face_connectivity.values - n_nodes_per_face = uxda.uxgrid.n_faces_per_node.values + n_faces_per_node = uxda.uxgrid.n_faces_per_node.values ( change_ind, - n_nodes_per_face_sorted_ind, + n_faces_per_node_sorted_ind, element_sizes, size_counts, - ) = get_face_node_partitions(n_nodes_per_face) + ) = get_face_node_partitions(n_faces_per_node) result = np.empty(shape=(data.shape[:-1]) + (uxda.uxgrid.n_node,)) for e, start, end in zip(element_sizes, change_ind[:-1], change_ind[1:]): - face_inds = n_nodes_per_face_sorted_ind[start:end] + face_inds = n_faces_per_node_sorted_ind[start:end] face_nodes_par = node_face_conn[face_inds, 0:e] # apply aggregation function to current node face partition @@ -279,7 +276,7 @@ def _apply_face_to_node_aggregation_numpy( def _face_to_node_aggregation(uxda, aggregation, aggregation_func_kwargs): - """Applies an Edge to Face Topological aggregation.""" + """Applies a Face to Node Topological aggregation.""" if not uxda._face_centered(): raise ValueError( f"Data Variable must be mapped to the face centers of each face, with dimension " @@ -305,3 +302,64 @@ def _face_to_node_aggregation(uxda, aggregation, aggregation_func_kwargs): dims=uxda.dims, name=uxda.name, ).rename({"n_face": "n_node"}) + + +def _apply_face_to_edge_aggregation_numpy( + uxda, aggregation_func, aggregation_func_kwargs +): + """Applies a Face to Edge Topological aggregation on a Numpy array.""" + data = uxda.values + edge_face_conn = uxda.uxgrid.edge_face_connectivity.values + n_faces_per_edge = uxda.uxgrid.n_faces_per_edge.values + + ( + change_ind, + n_faces_per_edge_sorted_ind, + element_sizes, + size_counts, + ) = get_face_node_partitions(n_faces_per_edge) + + result = np.empty(shape=(data.shape[:-1]) + (uxda.uxgrid.n_edge,)) + + for e, start, end in zip(element_sizes, change_ind[:-1], change_ind[1:]): + face_inds = n_faces_per_edge_sorted_ind[start:end] + face_edge_par = edge_face_conn[face_inds, 0:e] + + # apply aggregation function to current node face partition + aggregation_par = aggregation_func( + data[..., face_edge_par], axis=-1, **aggregation_func_kwargs + ) + + # store current aggregation + result[..., face_inds] = aggregation_par + + return result + + +def _face_to_edge_aggregation(uxda, aggregation, aggregation_func_kwargs): + """Applies a Face to Edge Topological aggregation.""" + if not uxda._face_centered(): + raise ValueError( + f"Data Variable must be mapped to the face centers of each face, with dimension " + f"{uxda.uxgrid.n_face}." + ) + + if isinstance(uxda.data, np.ndarray): + # apply aggregation using numpy + aggregated_var = _apply_face_to_edge_aggregation_numpy( + uxda, NUMPY_AGGREGATIONS[aggregation], aggregation_func_kwargs + ) + elif isinstance(uxda.data, da.Array): + # apply aggregation on dask array, TODO: + aggregated_var = _apply_face_to_edge_aggregation_numpy( + uxda, NUMPY_AGGREGATIONS[aggregation], aggregation_func_kwargs + ) + else: + raise ValueError + + return uxarray.core.dataarray.UxDataArray( + uxgrid=uxda.uxgrid, + data=aggregated_var, + dims=uxda.dims, + name=uxda.name, + ).rename({"n_face": "n_edge"}) From 04abf2f66e46faa1461c514f0e50858c4416d24a Mon Sep 17 00:00:00 2001 From: Aaron Zedwick Date: Fri, 10 Jan 2025 13:27:21 -0600 Subject: [PATCH 7/8] Added edge_to_node and connectivity constructions --- .../user-guide/topological-aggregations.ipynb | 70 ++++++++++++++++ uxarray/conventions/ugrid.py | 21 +++++ uxarray/core/aggregation.py | 84 ++++++++++++++++--- uxarray/grid/connectivity.py | 84 +++++++++++++++++-- uxarray/grid/grid.py | 36 +++++++- 5 files changed, 277 insertions(+), 18 deletions(-) diff --git a/docs/user-guide/topological-aggregations.ipynb b/docs/user-guide/topological-aggregations.ipynb index 05653cbc9..cf5d9ee4c 100644 --- a/docs/user-guide/topological-aggregations.ipynb +++ b/docs/user-guide/topological-aggregations.ipynb @@ -397,6 +397,36 @@ "\"Optional" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c462c1c-cf55-4bb5-a78a-5ac28366cc9b", + "metadata": {}, + "outputs": [], + "source": [ + "uxda_edge_face_agg = uxds[\"random_data_edge\"].topological_mean(destination=\"node\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c809274-1b47-42da-8f21-227cd7206fac", + "metadata": {}, + "outputs": [], + "source": [ + "(\n", + " uxds.uxgrid.plot(line_color=\"black\")\n", + " * uxds[\"random_data_edge\"]\n", + " .plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"circle\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Edge Data\")\n", + " * uxda_node_edge_agg.plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"square\", clabel=None, tools=[\"hover\"]\n", + " ).relabel(\"Edge to Node Mean\")\n", + ").opts(title=\"Edge to Node Aggregation (Mean)\", legend_position=\"top_right\")" + ] + }, { "cell_type": "markdown", "id": "86846522863860f5", @@ -421,6 +451,8 @@ "metadata": {}, "outputs": [], "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", "uxda_edge_face_agg = uxds[\"random_data_edge\"].topological_mean(destination=\"face\")" ] }, @@ -522,6 +554,44 @@ "\n", "\"Optional" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1104d1e-97fc-4403-8daa-f2dc33a37900", + "metadata": {}, + "outputs": [], + "source": [ + "uxda_face_edge_agg = uxds[\"random_data_face\"].topological_mean(destination=\"edge\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ff32897f-e555-474a-978a-723929261b38", + "metadata": {}, + "outputs": [], + "source": [ + "(\n", + " uxds.uxgrid.plot(line_color=\"black\")\n", + " * uxds[\"random_data_face\"]\n", + " .plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"circle\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Face Data\")\n", + " * uxda_face_edge_agg.plot.points(\n", + " cmap=\"inferno\", size=150, marker=\"square\", clabel=None, tools=[\"hover\"]\n", + " ).relabel(\"Face to Edge Mean\")\n", + ").opts(title=\"Face to Edge Aggregation (Mean)\", legend_position=\"top_right\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b55ecc0e-2015-4248-b5a6-c5a997faa6d5", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/uxarray/conventions/ugrid.py b/uxarray/conventions/ugrid.py index c25261a92..21fc245f4 100644 --- a/uxarray/conventions/ugrid.py +++ b/uxarray/conventions/ugrid.py @@ -231,6 +231,27 @@ N_FACES_PER_NODE_DIMS = ["n_node"] +N_FACES_PER_EDGE_ATTRS = { + "cf_role": "n_faces_per_edge", + "long name": "Number of faces per edge", +} + +N_FACES_PER_EDGE_DIMS = ["n_edge"] + +N_EDGES_PER_FACE_ATTRS = { + "cf_role": "n_edges_per_face", + "long name": "Number of edges per face", +} + +N_EDGES_PER_FACE_DIMS = ["n_face"] + +N_EDGES_PER_NODE_ATTRS = { + "cf_role": "n_edges_per_node", + "long name": "Number of edges per node", +} + +N_EDGES_PER_NODE_DIMS = ["n_node"] + CONNECTIVITY_NAMES = [ "face_node_connectivity", "face_edge_connectivity", diff --git a/uxarray/core/aggregation.py b/uxarray/core/aggregation.py index ac0ada644..de2b241ed 100644 --- a/uxarray/core/aggregation.py +++ b/uxarray/core/aggregation.py @@ -37,22 +37,20 @@ def _uxda_grid_aggregate(uxda, destination, aggregation, **kwargs): else: raise ValueError( f"Invalid destination for a node-centered data variable. Expected" - f"one of ['face', 'edge' but received {destination}" + f"one of ['face', 'edge'] but received {destination}" ) elif uxda._edge_centered(): # aggregation of an edge-centered data variable if destination == "face": return _edge_to_face_aggregation(uxda, aggregation, kwargs) - # raise NotImplementedError( - # "Aggregation of edge-centered data variables is not yet supported." - # ) - # if destination == "node": - # pass - # elif destination == "face": - # pass - # else: - # raise ValueError("TODO: ) + elif destination == "node": + return _edge_to_node_aggregation(uxda, aggregation, kwargs) + else: + raise ValueError( + f"Invalid destination for an edge-centered data variable. Expected" + f"one of ['face', 'node'] but received {destination}" + ) elif uxda._face_centered(): # aggregation of a face-centered data variable @@ -61,7 +59,10 @@ def _uxda_grid_aggregate(uxda, destination, aggregation, **kwargs): elif destination == "edge": return _face_to_edge_aggregation(uxda, aggregation, kwargs) else: - raise ValueError("TODO: ") + raise ValueError( + f"Invalid destination for a face-centered data variable. Expected" + f"one of ['node', 'edge'] but received {destination}" + ) else: raise ValueError( @@ -243,6 +244,67 @@ def _edge_to_face_aggregation(uxda, aggregation, aggregation_func_kwargs): ).rename({"n_edge": "n_face"}) +def _apply_edge_to_node_aggregation_numpy( + uxda, aggregation_func, aggregation_func_kwargs +): + """Applies an Edge to Node Topological aggregation on a Numpy array.""" + data = uxda.values + node_edge_conn = uxda.uxgrid.node_edge_connectivity.values + n_edges_per_node = uxda.uxgrid.n_edges_per_node.values + + ( + change_ind, + n_edges_per_node_sorted_ind, + element_sizes, + size_counts, + ) = get_face_node_partitions(n_edges_per_node) + + result = np.empty(shape=(data.shape[:-1]) + (uxda.uxgrid.n_node,)) + + for e, start, end in zip(element_sizes, change_ind[:-1], change_ind[1:]): + node_inds = n_edges_per_node_sorted_ind[start:end] + node_edges_par = node_edge_conn[node_inds, 0:e] + + # apply aggregation function to current node edge partition + aggregation_par = aggregation_func( + data[..., node_edges_par], axis=-1, **aggregation_func_kwargs + ) + + # store current aggregation + result[..., node_inds] = aggregation_par + + return result + + +def _edge_to_node_aggregation(uxda, aggregation, aggregation_func_kwargs): + """Applies an Edge to Node Topological aggregation.""" + if not uxda._edge_centered(): + raise ValueError( + f"Data Variable must be mapped to the edge centers of each face, with dimension " + f"{uxda.uxgrid.n_edge}." + ) + + if isinstance(uxda.data, np.ndarray): + # apply aggregation using numpy + aggregated_var = _apply_edge_to_node_aggregation_numpy( + uxda, NUMPY_AGGREGATIONS[aggregation], aggregation_func_kwargs + ) + elif isinstance(uxda.data, da.Array): + # apply aggregation on dask array, TODO: + aggregated_var = _apply_edge_to_node_aggregation_numpy( + uxda, NUMPY_AGGREGATIONS[aggregation], aggregation_func_kwargs + ) + else: + raise ValueError + + return uxarray.core.dataarray.UxDataArray( + uxgrid=uxda.uxgrid, + data=aggregated_var, + dims=uxda.dims, + name=uxda.name, + ).rename({"n_edge": "n_node"}) + + def _apply_face_to_node_aggregation_numpy( uxda, aggregation_func, aggregation_func_kwargs ): diff --git a/uxarray/grid/connectivity.py b/uxarray/grid/connectivity.py index 220876fc4..fc9718e85 100644 --- a/uxarray/grid/connectivity.py +++ b/uxarray/grid/connectivity.py @@ -159,9 +159,9 @@ def _build_n_nodes_per_face(face_nodes, n_face, n_max_face_nodes): def _populate_n_faces_per_node(grid): - """Constructs the connectivity variable (``n_nodes_per_face``) and stores + """Constructs the connectivity variable (``n_faces_per_node``) and stores it within the internal (``Grid._ds``) and through the attribute - (``Grid.n_nodes_per_face``).""" + (``Grid.n_faces_per_node``).""" n_faces_per_node = _build_n_nodes_per_face( grid.node_face_connectivity.values, grid.n_node, grid.n_max_node_faces @@ -175,16 +175,16 @@ def _populate_n_faces_per_node(grid): grid._ds["n_faces_per_node"] = xr.DataArray( data=n_faces_per_node, dims=ugrid.N_FACES_PER_NODE_DIMS, - attrs=ugrid.N_NODES_PER_FACE_ATTRS, + attrs=ugrid.N_FACES_PER_NODE_ATTRS, ) @njit(cache=True) def _build_n_faces_per_node(node_faces, n_nodes, n_max_node_faces): - """Constructs ``n_nodes_per_face``, which contains the number of non-fill- - value nodes for each face in ``face_node_connectivity``""" + """Constructs ``n_faces_per_node``, which contains the number of non-fill- + value nodes for each face in ``node_face_connectivity``""" - # padding to shape [n_face, n_max_face_nodes + 1] + # padding to shape [n_nodes, n_max_node_faces + 1] closed = np.ones((n_nodes, n_max_node_faces + 1), dtype=INT_DTYPE) * INT_FILL_VALUE closed[:, :-1] = node_faces.copy() @@ -194,6 +194,78 @@ def _build_n_faces_per_node(node_faces, n_nodes, n_max_node_faces): return n_faces_per_node +def _populate_n_faces_per_edge(grid): + """Constructs the connectivity variable (``n_faces_per_edge``) and stores + it within the internal (``Grid._ds``) and through the attribute + (``Grid.n_faces_per_edge``).""" + + n_faces_per_edge = _build_n_faces_per_edge( + grid.edge_face_connectivity.values, grid.n_edge, 2 + ) + + if n_faces_per_edge.ndim == 0: + # convert scalar value into a [1, 1] array + n_faces_per_edge = np.expand_dims(n_faces_per_edge, 0) + + # add to internal dataset + grid._ds["n_faces_per_edge"] = xr.DataArray( + data=n_faces_per_edge, + dims=ugrid.N_FACES_PER_EDGE_DIMS, + attrs=ugrid.N_FACES_PER_EDGE_ATTRS, + ) + + +@njit(cache=True) +def _build_n_faces_per_edge(edge_faces, n_edges, n_max_edge_faces): + """Constructs ``n_faces_per_edge``, which contains the number of non-fill- + value nodes for each face in ``edge_face_connectivity``""" + + # padding to shape [n_edges, n_max_edge_faces + 1] + closed = np.ones((n_edges, n_max_edge_faces + 1), dtype=INT_DTYPE) * INT_FILL_VALUE + + closed[:, :-1] = edge_faces.copy() + + n_faces_per_edge = np.argmax(closed == INT_FILL_VALUE, axis=1) + + return n_faces_per_edge + + +def _populate_n_edges_per_node(grid): + """Constructs the connectivity variable (``n_edges_per_node``) and stores + it within the internal (``Grid._ds``) and through the attribute + (``Grid.n_edges_per_node``).""" + + n_edges_per_node = _build_n_edges_per_node( + grid.node_edge_connectivity.values, grid.n_edge, grid.n_max_node_edges + ) + + if n_edges_per_node.ndim == 0: + # convert scalar value into a [1, 1] array + n_edges_per_node = np.expand_dims(n_edges_per_node, 0) + + # add to internal dataset + grid._ds["n_edges_per_node"] = xr.DataArray( + data=n_edges_per_node, + dims=ugrid.N_EDGES_PER_NODE_DIMS, + attrs=ugrid.N_EDGES_PER_NODE_ATTRS, + ) + + +@njit(cache=True) +def _build_n_edges_per_node(edge_nodes, n_edges, n_max_node_edges): + """Constructs ``n_edges_per_node``, which contains the number of non-fill- + value nodes for each face in ``node_edge_connectivity``""" + + # padding to shape [n_edges, n_max_edge_faces + 1] + closed = np.ones((n_edges, n_max_node_edges + 1), dtype=INT_DTYPE) * INT_FILL_VALUE + + closed[:, :-1] = edge_nodes.copy() + + n_edges_per_node = np.argmax(closed == INT_FILL_VALUE, axis=1) + + return n_edges_per_node + + def _populate_edge_node_connectivity(grid): """Constructs the UGRID connectivity variable (``edge_node_connectivity``) and stores it within the internal (``Grid._ds``) and through the attribute diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index 00bafe8b9..cd23459a0 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -55,7 +55,7 @@ _populate_node_face_connectivity, _populate_edge_face_connectivity, _populate_face_face_connectivity, - _populate_n_faces_per_node, + _populate_n_faces_per_node, _populate_n_faces_per_edge, _populate_n_edges_per_face, _populate_n_edges_per_node, ) from uxarray.grid.geometry import ( @@ -866,6 +866,40 @@ def n_faces_per_node(self, value): assert isinstance(value, xr.DataArray) self._ds["n_faces_per_node"] = value + @property + def n_faces_per_edge(self) -> xr.DataArray: + """The number of faces that surround each edge. + + Dimensions: ``(n_face, )`` + """ + if "n_faces_per_edge" not in self._ds: + _populate_n_faces_per_edge(self) + + return self._ds["n_faces_per_edge"] + + @n_faces_per_edge.setter + def n_faces_per_edge(self, value): + """Setter for ``n_faces_per_edge``""" + assert isinstance(value, xr.DataArray) + self._ds["n_faces_per_edge"] = value + + @property + def n_edges_per_node(self) -> xr.DataArray: + """The number of edges that surround each node. + + Dimensions: ``(n_edge, )`` + """ + if "n_edges_per_node" not in self._ds: + _populate_n_edges_per_node(self) + + return self._ds["n_edges_per_node"] + + @n_edges_per_node.setter + def n_edges_per_node(self, value): + """Setter for ``n_edges_per_node``""" + assert isinstance(value, xr.DataArray) + self._ds["n_edges_per_node"] = value + @property def node_lon(self) -> xr.DataArray: """Longitude of each node in degrees. From f7a55894f78d5c1f59c6b53be93aa678ba5478d1 Mon Sep 17 00:00:00 2001 From: Aaron Zedwick Date: Fri, 10 Jan 2025 13:27:37 -0600 Subject: [PATCH 8/8] Update grid.py --- uxarray/grid/grid.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index cd23459a0..5b6489f17 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -55,7 +55,9 @@ _populate_node_face_connectivity, _populate_edge_face_connectivity, _populate_face_face_connectivity, - _populate_n_faces_per_node, _populate_n_faces_per_edge, _populate_n_edges_per_face, _populate_n_edges_per_node, + _populate_n_faces_per_node, + _populate_n_faces_per_edge, + _populate_n_edges_per_node, ) from uxarray.grid.geometry import (