diff --git a/benchmarks/mpas_ocean.py b/benchmarks/mpas_ocean.py index 659c19014..2d47627c1 100644 --- a/benchmarks/mpas_ocean.py +++ b/benchmarks/mpas_ocean.py @@ -56,6 +56,14 @@ def teardown(self, resolution, *args, **kwargs): del self.uxgrid +class FaceAreas(GridBenchmark): + def time_compute_face_areas(self, resolution): + self.uxgrid.compute_face_areas() + + def peakmem_compute_face_areas(self, resolution): + self.uxgrid.compute_face_areas() + + class Gradient(DatasetBenchmark): def time_gradient(self, resolution): self.uxds[data_var].gradient() diff --git a/docs/user-guide/area_calc.ipynb b/docs/user-guide/area_calc.ipynb index 08cf5e441..349b720a4 100644 --- a/docs/user-guide/area_calc.ipynb +++ b/docs/user-guide/area_calc.ipynb @@ -29,9 +29,15 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "collapsed": false, + "execution": { + "iopub.execute_input": "2026-07-17T18:49:15.166870Z", + "iopub.status.busy": "2026-07-17T18:49:15.166704Z", + "iopub.status.idle": "2026-07-17T18:49:16.484474Z", + "shell.execute_reply": "2026-07-17T18:49:16.483964Z" + }, "jupyter": { "outputs_hidden": false } @@ -55,9 +61,15 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": { "collapsed": false, + "execution": { + "iopub.execute_input": "2026-07-17T18:49:16.486070Z", + "iopub.status.busy": "2026-07-17T18:49:16.485935Z", + "iopub.status.idle": "2026-07-17T18:49:16.708579Z", + "shell.execute_reply": "2026-07-17T18:49:16.708080Z" + }, "jupyter": { "outputs_hidden": false } @@ -77,14 +89,38 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": { "collapsed": false, + "execution": { + "iopub.execute_input": "2026-07-17T18:49:16.710491Z", + "iopub.status.busy": "2026-07-17T18:49:16.710286Z", + "iopub.status.idle": "2026-07-17T18:49:16.936544Z", + "shell.execute_reply": "2026-07-17T18:49:16.936125Z" + }, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "OMP: Info #276: omp_set_nested routine deprecated, please use omp_set_max_active_levels instead.\n" + ] + }, + { + "data": { + "text/plain": [ + "np.float64(12.566370614678554)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "t4_area = ugrid.calculate_total_face_area()\n", "t4_area" @@ -108,9 +144,35 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:16.974275Z", + "iopub.status.busy": "2026-07-17T18:49:16.974126Z", + "iopub.status.idle": "2026-07-17T18:49:16.978220Z", + "shell.execute_reply": "2026-07-17T18:49:16.977844Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/mbook/uxarray/uxarray/grid/grid.py:2042: UserWarning: Total face area 12.571403993719983 exceeds the unit-sphere area 12.566370614359172. This usually indicates a malformed grid or incorrect connectivity.\n", + " face_areas, face_jacobian = self._compute_face_areas_and_jacobian(\n" + ] + }, + { + "data": { + "text/plain": [ + "np.float64(12.571403993719983)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "t1_area = ugrid.calculate_total_face_area(quadrature_rule=\"triangular\", order=1)\n", "t1_area" @@ -140,35 +202,1923 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": { "collapsed": false, + "execution": { + "iopub.execute_input": "2026-07-17T18:49:16.979426Z", + "iopub.status.busy": "2026-07-17T18:49:16.979339Z", + "iopub.status.idle": "2026-07-17T18:49:16.985486Z", + "shell.execute_reply": "2026-07-17T18:49:16.984829Z" + }, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'face_areas' (n_face: 5400)> Size: 43kB\n",
+       "array([0.00211174, 0.00211221, 0.00210723, ..., 0.00210723, 0.00211221,\n",
+       "       0.00211174], shape=(5400,))\n",
+       "Dimensions without coordinates: n_face\n",
+       "Attributes:\n",
+       "    cf_role:    face_areas\n",
+       "    long_name:  Area of each face.
" + ], + "text/plain": [ + " Size: 43kB\n", + "array([0.00211174, 0.00211221, 0.00210723, ..., 0.00210723, 0.00211221,\n", + " 0.00211174], shape=(5400,))\n", + "Dimensions without coordinates: n_face\n", + "Attributes:\n", + " cf_role: face_areas\n", + " long_name: Area of each face." + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ugrid.face_areas" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also visualize the face areas directly. Passing `as_uxarray=True` to `Grid.compute_face_areas()` returns a `UxDataArray` paired with the grid, which can be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:16.986823Z", + "iopub.status.busy": "2026-07-17T18:49:16.986711Z", + "iopub.status.idle": "2026-07-17T18:49:21.072910Z", + "shell.execute_reply": "2026-07-17T18:49:21.072397Z" + } + }, + "outputs": [ + { + "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 version = '3.9.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", + " const reloading = false;\n", + " const Bokeh = root.Bokeh;\n", + " const BK_RE = /^https:\\/\\/cdn\\.bokeh\\.org\\/bokeh\\/(release|dev)\\/bokeh-/;\n", + " const PN_RE = /^https:\\/\\/cdn\\.holoviz\\.org\\/panel\\/[^/]+\\/dist\\/panel/i;\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, Bokeh, 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", + " const shouldSkip = skip.includes(escaped) || existing_scripts.includes(escaped)\n", + " const isBokehOrPanel = BK_RE.test(escaped) || PN_RE.test(escaped)\n", + " const missingOrBroken = Bokeh == null || Bokeh.Panel == null || (Bokeh.version != version && !Bokeh.versions?.has(version)) || Bokeh.versions?.get(version)?.Panel == null;\n", + " if (shouldSkip && !(isBokehOrPanel && missingOrBroken)) {\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.8.10/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.9.0.min.js\", \"https://cdn.holoviz.org/panel/1.8.10/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", + " } 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 = Bokeh != null && ((Bokeh.version === version && Bokeh.Panel) || (Bokeh.versions?.has(version) && Bokeh.versions.get(version)?.Panel));\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, Bokeh, function() {\n", + " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + " run_inline_js();\n", + " if (Bokeh != undefined && !reloading) {\n", + " const NewBokeh = root.Bokeh;\n", + " if (Bokeh.versions === undefined) {\n", + " Bokeh.versions = new Map();\n", + " }\n", + " if (NewBokeh.version !== Bokeh.version) {\n", + " Bokeh[NewBokeh.version] = NewBokeh;\n", + " Bokeh.versions.set(NewBokeh.version, NewBokeh);\n", + " }\n", + " root.Bokeh = Bokeh;\n", + " }\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 version = '3.9.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n const BK_RE = /^https:\\/\\/cdn\\.bokeh\\.org\\/bokeh\\/(release|dev)\\/bokeh-/;\n const PN_RE = /^https:\\/\\/cdn\\.holoviz\\.org\\/panel\\/[^/]+\\/dist\\/panel/i;\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, Bokeh, 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 const shouldSkip = skip.includes(escaped) || existing_scripts.includes(escaped)\n const isBokehOrPanel = BK_RE.test(escaped) || PN_RE.test(escaped)\n const missingOrBroken = Bokeh == null || Bokeh.Panel == null || (Bokeh.version != version && !Bokeh.versions?.has(version)) || Bokeh.versions?.get(version)?.Panel == null;\n if (shouldSkip && !(isBokehOrPanel && missingOrBroken)) {\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.8.10/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.9.0.min.js\", \"https://cdn.holoviz.org/panel/1.8.10/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 } 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 = Bokeh != null && ((Bokeh.version === version && Bokeh.Panel) || (Bokeh.versions?.has(version) && Bokeh.versions.get(version)?.Panel));\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, Bokeh, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n if (Bokeh != undefined && !reloading) {\n const NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh[NewBokeh.version] = NewBokeh;\n Bokeh.versions.set(NewBokeh.version, NewBokeh);\n }\n root.Bokeh = Bokeh;\n }\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", + " 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", + " let retries = 0;\n", + " const open = () => {\n", + " if (comm.active) {\n", + " comm.open();\n", + " } else if (retries > 3) {\n", + " console.warn('Comm target never activated')\n", + " } else {\n", + " retries += 1\n", + " setTimeout(open, 500)\n", + " }\n", + " }\n", + " if (comm.active) {\n", + " comm.open();\n", + " } else {\n", + " setTimeout(open, 500)\n", + " }\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 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 let retries = 0;\n const open = () => {\n if (comm.active) {\n comm.open();\n } else if (retries > 3) {\n console.warn('Comm target never activated')\n } else {\n retries += 1\n setTimeout(open, 500)\n }\n }\n if (comm.active) {\n comm.open();\n } else {\n setTimeout(open, 500)\n }\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": "edcd19ac-b738-4301-b1f4-140e0ce9e76a" + } + }, + "output_type": "display_data" + }, + { + "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 = false;\n", + " const version = '3.9.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", + " const reloading = true;\n", + " const Bokeh = root.Bokeh;\n", + " const BK_RE = /^https:\\/\\/cdn\\.bokeh\\.org\\/bokeh\\/(release|dev)\\/bokeh-/;\n", + " const PN_RE = /^https:\\/\\/cdn\\.holoviz\\.org\\/panel\\/[^/]+\\/dist\\/panel/i;\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, Bokeh, 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", + " const shouldSkip = skip.includes(escaped) || existing_scripts.includes(escaped)\n", + " const isBokehOrPanel = BK_RE.test(escaped) || PN_RE.test(escaped)\n", + " const missingOrBroken = Bokeh == null || Bokeh.Panel == null || (Bokeh.version != version && !Bokeh.versions?.has(version)) || Bokeh.versions?.get(version)?.Panel == null;\n", + " if (shouldSkip && !(isBokehOrPanel && missingOrBroken)) {\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.8.10/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.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", + " } 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 = Bokeh != null && ((Bokeh.version === version && Bokeh.Panel) || (Bokeh.versions?.has(version) && Bokeh.versions.get(version)?.Panel));\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, Bokeh, function() {\n", + " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + " run_inline_js();\n", + " if (Bokeh != undefined && !reloading) {\n", + " const NewBokeh = root.Bokeh;\n", + " if (Bokeh.versions === undefined) {\n", + " Bokeh.versions = new Map();\n", + " }\n", + " if (NewBokeh.version !== Bokeh.version) {\n", + " Bokeh[NewBokeh.version] = NewBokeh;\n", + " Bokeh.versions.set(NewBokeh.version, NewBokeh);\n", + " }\n", + " root.Bokeh = Bokeh;\n", + " }\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 = false;\n const version = '3.9.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = true;\n const Bokeh = root.Bokeh;\n const BK_RE = /^https:\\/\\/cdn\\.bokeh\\.org\\/bokeh\\/(release|dev)\\/bokeh-/;\n const PN_RE = /^https:\\/\\/cdn\\.holoviz\\.org\\/panel\\/[^/]+\\/dist\\/panel/i;\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, Bokeh, 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 const shouldSkip = skip.includes(escaped) || existing_scripts.includes(escaped)\n const isBokehOrPanel = BK_RE.test(escaped) || PN_RE.test(escaped)\n const missingOrBroken = Bokeh == null || Bokeh.Panel == null || (Bokeh.version != version && !Bokeh.versions?.has(version)) || Bokeh.versions?.get(version)?.Panel == null;\n if (shouldSkip && !(isBokehOrPanel && missingOrBroken)) {\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.8.10/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.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 } 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 = Bokeh != null && ((Bokeh.version === version && Bokeh.Panel) || (Bokeh.versions?.has(version) && Bokeh.versions.get(version)?.Panel));\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, Bokeh, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n if (Bokeh != undefined && !reloading) {\n const NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh[NewBokeh.version] = NewBokeh;\n Bokeh.versions.set(NewBokeh.version, NewBokeh);\n }\n root.Bokeh = Bokeh;\n }\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", + " 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", + " let retries = 0;\n", + " const open = () => {\n", + " if (comm.active) {\n", + " comm.open();\n", + " } else if (retries > 3) {\n", + " console.warn('Comm target never activated')\n", + " } else {\n", + " retries += 1\n", + " setTimeout(open, 500)\n", + " }\n", + " }\n", + " if (comm.active) {\n", + " comm.open();\n", + " } else {\n", + " setTimeout(open, 500)\n", + " }\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 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 let retries = 0;\n const open = () => {\n if (comm.active) {\n comm.open();\n } else if (retries > 3) {\n console.warn('Comm target never activated')\n } else {\n retries += 1\n setTimeout(open, 500)\n }\n }\n if (comm.active) {\n comm.open();\n } else {\n setTimeout(open, 500)\n }\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": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Image [x,y] (x_y var)" + ] + }, + "execution_count": 6, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "c9629c1a-496f-4418-8ad7-08043a7cd3ce" + } + }, + "output_type": "execute_result" + } + ], + "source": [ + "face_area_da = ugrid.compute_face_areas(as_uxarray=True)\n", + "face_area_da.plot(\n", + " cmap=\"viridis\",\n", + " title=\"Face Areas\",\n", + ")" + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Need face area calculations with custom parameters?\n", "\n", - "For most use cases, the `Grid.face_areas` property provides the recommended approach. For advanced use cases requiring custom quadrature rules and orders, you can access the internal computation method, `Grid._compute_face_areas()`. For example, using `quadrature_rule` as \"gaussian\" and `order` as 4 would give us the following:" + "For most use cases, the `Grid.face_areas` property provides the recommended approach. For advanced use cases requiring custom quadrature rules and orders, use the `Grid.compute_face_areas()` method. By default it returns just the face areas; pass `return_jacobian=True` to also get the per-face Jacobian, or `as_uxarray=True` to get a `UxDataArray` (paired with the grid, so it can be plotted directly). For example, using `quadrature_rule` as \"gaussian\" and `order` as 4 would give us the following:" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:21.074630Z", + "iopub.status.busy": "2026-07-17T18:49:21.074507Z", + "iopub.status.idle": "2026-07-17T18:49:21.083884Z", + "shell.execute_reply": "2026-07-17T18:49:21.083437Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(12.566370614359112)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "all_face_areas, all_face_jacobians = ugrid._compute_face_areas(\n", - " quadrature_rule=\"gaussian\", order=4\n", + "all_face_areas, all_face_jacobians = ugrid.compute_face_areas(\n", + " quadrature_rule=\"gaussian\", order=4, return_jacobian=True\n", ")\n", "g4_area = all_face_areas.sum()\n", "g4_area" @@ -183,9 +2133,29 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 8, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:21.085104Z", + "iopub.status.busy": "2026-07-17T18:49:21.085019Z", + "iopub.status.idle": "2026-07-17T18:49:21.087770Z", + "shell.execute_reply": "2026-07-17T18:49:21.087247Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(np.float64(0.005033379360810386),\n", + " np.float64(3.1938185429680743e-10),\n", + " np.float64(6.039613253960852e-14))" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "actual_area = 4 * np.pi\n", "diff_t4_area = np.abs(t4_area - actual_area)\n", @@ -215,14 +2185,31 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": { "collapsed": false, + "execution": { + "iopub.execute_input": "2026-07-17T18:49:21.089207Z", + "iopub.status.busy": "2026-07-17T18:49:21.089094Z", + "iopub.status.idle": "2026-07-17T18:49:21.407739Z", + "shell.execute_reply": "2026-07-17T18:49:21.407209Z" + }, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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BclCSnkzphlfrTQ+fWDxp3Aw4M0RikWV+6akmSeusd9++yWFkoKqWMJUoJxRpF/72tfO8fl3v/vdNBcp763G7VSo8qytMHcp8p6f7DEGODb+9re/zaEo6RPUPY4dz0ieMRhQf1oNBOODMkjmUIg5fFoh14Fzxv1SanbbbbfkONKPMQ9mCvOHrEAtCjBEjO3Wtu6Ec889N40n7eh5jAfvMY9a25BcpqBR5Ixd7U62m1vGlbagUPu95+w1yLh77rknya6poN1FI3/84x+nNUZ/M9bM53AyUQIZIDzc5TGpTY1LymvZcNSPrTJF/xtP2oOyrB15zmeKdmsfuUQeiQYyhHj1UUr9vx30k35k1JFlHBeef6pc0VaYGxxPVZxlnHCckuQaY4dhIULezoDUVuYbOTIVgpLV2uYBssj3MS/NPfrFOeec84hjP//5zyelP3JrqoyNMhhDxkInfQUYBhH1ss7HWuY+67YGlcF5wQCvWuwCLZCjOdZt74xR/Z/RBhMZjcQee+zBHTbxhz/8oe33iyyyyMRb3/rWtt9ddtll6bden/rUp9oe849//OMRn2244YYT880338S//vWvyc9cw3mOP/74OY6966670udPetKTJn7/+9/P8d2DDz44cf/998/x2V/+8peJZz3rWRPrrbfe5Gc/+clP0jmOO+64OY5dYYUVJhZYYIGJ//znPxOdsNVWW6Xf3nDDDXN87rraLF5//OMfK91zfPeZz3xm8rPFFlts4jnPec7EX//618nPvvrVr6bjXvjCF05MBed/7GMfO/Ge97xn4qGHHpr8/Oijj06/P+WUUyr3dRntjnUv66yzziOO1XflMfKBD3wgjZupcPDBB6fza4/pcOGFF6ZjDz/88CmP096vfe1r57gvvzv99NPn6LdnP/vZE6usssrkZ2ecccbEox71qInvfOc7c5zPWPT7K6+8cqIq2s0X53D+W2655RHH+05bTzVfvv/97z/iOYwfn73rXe+aY/war3PPPffkWPrd73438ehHP3pixRVXnOOce+65Z/p9uT+/+c1vps+8g/O+9KUvnVhmmWXmuIZ7fNGLXjTx7ne/e8q2+PrXv57Od9FFF83x+RFHHJE+v+CCC6b8fbfP2m4sGv9+e9hhhz3ivK3PVMa///3viUUXXXTiHe94x+RnP/zhD9O5ttxyyzmO/ehHP/qIftTe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sL1qMjIyMjIyMjIyMjLGG89+9rOLu+66a0qjaKQMIpEhxtDdd9+d6v0PEkcffXRxwAEHFPfee2/a0PDnP/958Ze//KV417veVSy++OLFRhttNKPzep4PfvCDae+CNdZYo3jd616XnvPAAw9M11KBSknZV77ylckgVJr3q1/9avG3v/0tVWm7/fbbUwWyJkBFHtV4RhlKsCqdrB8XXnjhoukw166//vpihRVWGPatjGUVuqc//enFC1/4whmfY9TmnH3G//Of/ySZZ1PJUcao9d24oF2/0R+U0rdRcEZ9Mcw5R3dQaff//u//im233bZ42tOe1vdrnnDCCanS6eqrr1485SlPKR7/+McXv/3tb9P/L7/88qRf0j1tBXHJJZcU888/f7HQQgsVz3jGM9K2DYsttlj6zbChSqvtFejOUxlEI1VlzkM/+clPTsbAoA0iuPPOO1NZ19NOOy01us0xF1hggeLXv/518eIXv7hYdtlli1e96lVdn5dVywC66KKL0t4kb3rTm9JAZQgts8wyyQBSQtZnBiuhaod5RtSqq646o2sOA7fcckva/X3UccUVVySB8YpXvKJoOu65557i2muvHasNKkcJozbnyGCbOBuPSyyxRDHKGLW+Gxe067crr7wy6QoUyoz6YphzbvPNN086nv0GB7EPHdCl11xzzWKppZZKex1yvi233HLJ0c8QuuOOO5JDlGP0N7/5TQoEMNjooQwkxtDHPvaxFJ1ZccUVh+YErmobNCN00BAwet761rcWf/jDH5LSa8Cwoi+44IJip512KtZdd93iIx/5SLKmWdi33XZbpfOiAR588MFpwBlc8LznPW9ybxLGz5e+9KWkBNj8jaJtkLrue9/73jQId9555+LII49Mv7UPhw0JRa7qZA/fdNNNxahD21911VXFAw88UIwCjGEbXmYMHldffXVykMwGozbnRMx4KMnEUceo9d24oF2/WdutDRn1xrDmnA26be5rb6tBGUPAiPjyl7+c9FY6JGMCM0FEk8OJccbQ2WGHHYojjjii+OIXv1h8+tOfTu833nhjYk2J1H/qU59Km/06lnP/T3/6U1FHZIOoxzBgzj///GT0fO5zn0sbi77lLW9JkRywe7WB4ThWc1U88YlPLLbbbrti5ZVXLr7+9a8n4+dnP/tZcd999yWjhmXOSOJBYKEzol760pcW7373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GueZAKXwRylQgoSQcQxhSKCE14siblFl4Pgc99m7BZcAIZhDKbZQOLffEpA8JgSO4wkOgic40BKiCVSCOo51fffBQKGER66OBFNCz8JFkeSZdE4LqmMII4uzxcy1eXAcJ4JBILoH35ePZVAwDBhkFAftQMC6B4uZhSWOdS5/W6x8F+cNQc9L6hk8m3bxbFGlyWcSLEO4unfHhAGjzVzX87g/C27s/s777Zj427GEtetpj+grnk3ndF/O4W/XZ1h5Noup3znWMzjWy4JaPtZi6Xn1k2fW99rFPWhbv/W3tvNs3ezB0ESqWK9hrOnDpuW6NKHvjEXjlPwZV4MoIAfFPKaw9bXvFFw46KDim297W/G3pzylWJFRdOCBHQ8nX6wl7olsqwsotJRkVDgycosttkifk40R/WLoiDKUQdHmBKMIWwu0O2dfJ0WYEs0pqMw2mUuGU+itM5HfGwZD9Bsan6IN2o0y71jy131ZK62B1krrjHXH7/2WvCZvIl/ROmCOkO3WHr8nw60Xnp+R5N1aZOxAbIwclEDndZ7YlNua4bp0AjLN75586aXFxCWXFM980pOKJx16aLHUJpsU880/f2orBpv12Vo4m4iXiBpjx5rcWrxAtFh7eTZygDGoP7QLo8jzex7P73kYfpHfBfQiv52qbLay6Kecckpx5JFHJsaN+7DxK4fhEUcckdqMERn6QcZoIe9DNCKgGBPGPFAiRU0CAYNuxPMSCanjCIZYVDvq994LvYQFMRcTaCaa0HcoLhTWcaezAsoQxZbjpG99d/XVRfG61xUHFDsUlzxp2eIVT7q1eP6vflUUG26EVzzHof/5z0PF3/9+b4owUGRf+9rXFI95zGOTskqRvv/+fxWPe9x8xT//+Y9innnmTYYT2fawMisvg7PqweLRj35M+o5izwjwW8fMNNLkt15hQHO4YS4wPN7+9relaMAf//inOYroeJbbb78jGTCUehXpKM/hvGodk6J1lHHGBgcgA0hUhdKtj379618VT3va0x9hxE9M3FvMNVe959wjcPfdRXHC8cVji38XWxWHF48uHqahFVddVTy0xBLFiSeemIwHES5OzplCpEuUyj5As420B1WuLDf22Wef9I6uyHhq52A59NBDk3OZAWYfKGwNEUUv45EhK88soznI+xCNGQh/HmpVmQjnJpX+5cEheFDfxtkgolRY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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Vertices\n", "verts = [\n", @@ -288,14 +2275,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": { "collapsed": false, + "execution": { + "iopub.execute_input": "2026-07-17T18:49:21.409243Z", + "iopub.status.busy": "2026-07-17T18:49:21.409120Z", + "iopub.status.idle": "2026-07-17T18:49:21.422995Z", + "shell.execute_reply": "2026-07-17T18:49:21.422343Z" + }, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Percentage correction due to line of constant latitude being one of the edges: 1.2576%\n" + ] + } + ], "source": [ "area = vgrid.calculate_total_face_area()\n", "corrected_area = vgrid.calculate_total_face_area(latitude_adjusted_area=True)\n", @@ -307,14 +2308,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": { "collapsed": false, + "execution": { + "iopub.execute_input": "2026-07-17T18:49:21.424458Z", + "iopub.status.busy": "2026-07-17T18:49:21.424305Z", + "iopub.status.idle": "2026-07-17T18:49:21.427191Z", + "shell.execute_reply": "2026-07-17T18:49:21.426753Z" + }, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Area calculated using Gaussian Quadrature Order 2: 0.022165612979716153\n", + "Percentage difference between gaussian area above and corrected area: 1.2599%\n" + ] + } + ], "source": [ "# Calculate the area of the triangle with lower gaussian quadrature order\n", "area_gaussian = vgrid.calculate_total_face_area(quadrature_rule=\"gaussian\", order=2)\n", @@ -338,9 +2354,15 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": { "collapsed": false, + "execution": { + "iopub.execute_input": "2026-07-17T18:49:21.428495Z", + "iopub.status.busy": "2026-07-17T18:49:21.428394Z", + "iopub.status.idle": "2026-07-17T18:49:21.431001Z", + "shell.execute_reply": "2026-07-17T18:49:21.430669Z" + }, "jupyter": { "outputs_hidden": false } @@ -392,14 +2414,113 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": { "collapsed": false, + "execution": { + "iopub.execute_input": "2026-07-17T18:49:21.432293Z", + "iopub.status.busy": "2026-07-17T18:49:21.432200Z", + "iopub.status.idle": "2026-07-17T18:49:23.959154Z", + "shell.execute_reply": "2026-07-17T18:49:23.958601Z" + }, "jupyter": { "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": [ + ":Path [Longitude,Latitude]" + ] + }, + "execution_count": 13, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "c84400ac-2e64-492e-8be5-59f6143198dd" + } + }, + "output_type": "execute_result" + } + ], "source": [ "verts_grid = ux.open_grid(faces_verts_ndarray, latlon=True)\n", "\n", @@ -409,18 +2530,36 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": { "collapsed": false, + "execution": { + "iopub.execute_input": "2026-07-17T18:49:23.960745Z", + "iopub.status.busy": "2026-07-17T18:49:23.960589Z", + "iopub.status.idle": "2026-07-17T18:49:23.964871Z", + "shell.execute_reply": "2026-07-17T18:49:23.964478Z" + }, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Area: [0.14323746 0.25118746 0.12141312]\n", + "Corrected Area: [0.14354712 0.25042962 0.12190143]\n", + "Total Percentage Difference: [ 0.2161859 -0.30170321 0.40218883]\n" + ] + } + ], "source": [ - "area, _ = verts_grid._compute_face_areas()\n", + "area, _ = verts_grid.compute_face_areas(return_jacobian=True)\n", "print(\"Area:\", area)\n", - "corrected_area, _ = verts_grid._compute_face_areas(latitude_adjusted_area=True)\n", + "corrected_area, _ = verts_grid.compute_face_areas(\n", + " latitude_adjusted_area=True, return_jacobian=True\n", + ")\n", "print(\"Corrected Area:\", corrected_area)\n", "\n", "total_percentage_difference = ((corrected_area - area) / area) * 100\n", @@ -469,9 +2608,25 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 15, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:23.966283Z", + "iopub.status.busy": "2026-07-17T18:49:23.966194Z", + "iopub.status.idle": "2026-07-17T18:49:23.969648Z", + "shell.execute_reply": "2026-07-17T18:49:23.969287Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The area of the spherical rectangle is approximately 1.132592 steradians\n", + "The area of the spherical rectangle is approximately 45971520.05 square kilometers\n" + ] + } + ], "source": [ "def theoretical_spherical_rectangle_area(lons, lats, radius=1):\n", " \"\"\"\n", @@ -520,9 +2675,109 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 16, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:23.971058Z", + "iopub.status.busy": "2026-07-17T18:49:23.970973Z", + "iopub.status.idle": "2026-07-17T18:49:24.034358Z", + "shell.execute_reply": "2026-07-17T18:49:24.033943Z" + } + }, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Path [Longitude,Latitude]" + ] + }, + "execution_count": 16, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "0e1bc905-d6cd-45ad-a2af-5201c9640e2d" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Define face-node connectivity and create/plot a uxarray object face\n", "face_node_connectivity = np.array([[0, 1, 2, 3]])\n", @@ -538,29 +2793,63 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 17, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:24.035786Z", + "iopub.status.busy": "2026-07-17T18:49:24.035680Z", + "iopub.status.idle": "2026-07-17T18:49:24.038938Z", + "shell.execute_reply": "2026-07-17T18:49:24.038325Z" + } + }, "outputs": [], "source": [ - "area_no_correction = face._compute_face_areas(quadrature_rule=\"gaussian\", order=8)" + "area_no_correction = face.compute_face_areas(\n", + " quadrature_rule=\"gaussian\", order=8, return_jacobian=True\n", + ")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 18, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:24.040233Z", + "iopub.status.busy": "2026-07-17T18:49:24.040094Z", + "iopub.status.idle": "2026-07-17T18:49:24.042498Z", + "shell.execute_reply": "2026-07-17T18:49:24.042051Z" + } + }, "outputs": [], "source": [ - "corrected_area = face._compute_face_areas(\n", - " quadrature_rule=\"gaussian\", order=8, latitude_adjusted_area=True\n", + "corrected_area = face.compute_face_areas(\n", + " quadrature_rule=\"gaussian\",\n", + " order=8,\n", + " latitude_adjusted_area=True,\n", + " return_jacobian=True,\n", ")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 19, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:24.043546Z", + "iopub.status.busy": "2026-07-17T18:49:24.043468Z", + "iopub.status.idle": "2026-07-17T18:49:24.045629Z", + "shell.execute_reply": "2026-07-17T18:49:24.045201Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The percentage difference between the corrected and uncorrected areas is 3.73%\n" + ] + } + ], "source": [ "# Calculate the percentage difference between the corrected and uncorrected areas\n", "percentage_difference = (\n", @@ -587,9 +2876,109 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 20, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:24.046808Z", + "iopub.status.busy": "2026-07-17T18:49:24.046705Z", + "iopub.status.idle": "2026-07-17T18:49:24.113897Z", + "shell.execute_reply": "2026-07-17T18:49:24.113305Z" + } + }, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Path [Longitude,Latitude]" + ] + }, + "execution_count": 20, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "b0332e89-4a83-4e88-bf01-9cebd12d3c13" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Define the latitude and longitude points\n", "lon1, lon2, lon3, lon4 = -97.5, -87.5, -82.5, -92.5\n", @@ -667,9 +3056,14 @@ ")\n", "\n", "# Compute the area of each polygon\n", - "small_areas = faces._compute_face_areas(quadrature_rule=\"gaussian\", order=4)\n", - "corrected_small_areas = faces._compute_face_areas(\n", - " quadrature_rule=\"gaussian\", order=4, latitude_adjusted_area=True\n", + "small_areas = faces.compute_face_areas(\n", + " quadrature_rule=\"gaussian\", order=4, return_jacobian=True\n", + ")\n", + "corrected_small_areas = faces.compute_face_areas(\n", + " quadrature_rule=\"gaussian\",\n", + " order=4,\n", + " latitude_adjusted_area=True,\n", + " return_jacobian=True,\n", ")\n", "faces.plot(\n", " backend=\"bokeh\", title=\"Four Polygons With One Edge on a Line of Constant Latitude\"\n", @@ -697,9 +3091,29 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 21, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:24.115129Z", + "iopub.status.busy": "2026-07-17T18:49:24.115035Z", + "iopub.status.idle": "2026-07-17T18:49:24.117544Z", + "shell.execute_reply": "2026-07-17T18:49:24.117189Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Area of each polygon:\n", + "Polygon 1: 0.00387988, Corrected Area: 0.00385858, Percentage Difference: -0.5488%\n", + "Polygon 2: 0.00329592, Corrected Area: 0.00325432, Percentage Difference: -1.2621%\n", + "Polygon 3: 0.00245684, Corrected Area: 0.00249844, Percentage Difference: 1.6932%\n", + "Polygon 4: 0.00178601, Corrected Area: 0.00180730, Percentage Difference: 1.1922%\n", + "Total area of 4 polygons: 0.01141865, Corrected total area of 4 polygons: 0.01141865, Percentage Diff: 0.0000%\n" + ] + } + ], "source": [ "# Print area of all 4 faces:\n", "print(\"Area of each polygon:\")\n", @@ -723,9 +3137,116 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 22, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:24.118808Z", + "iopub.status.busy": "2026-07-17T18:49:24.118722Z", + "iopub.status.idle": "2026-07-17T18:49:24.185089Z", + "shell.execute_reply": "2026-07-17T18:49:24.184668Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Area of the face is: 0.01143206\n" + ] + }, + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Path [Longitude,Latitude]" + ] + }, + "execution_count": 22, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "5b47e672-3cf8-41e2-882d-8e263304bc40" + } + }, + "output_type": "execute_result" + } + ], "source": [ "large_node_lon = np.array([lon1, lon2, lon3, lon4])\n", "large_node_lat = np.array([lat1, lat2, lat3, lat4])\n", @@ -738,7 +3259,9 @@ " fill_value=-1,\n", ")\n", "\n", - "large_area = large_face._compute_face_areas(quadrature_rule=\"gaussian\", order=4)\n", + "large_area = large_face.compute_face_areas(\n", + " quadrature_rule=\"gaussian\", order=4, return_jacobian=True\n", + ")\n", "large_face.plot()\n", "print(f\"Area of the face is: {large_area[0][0]:.8f}\")\n", "large_face.plot(\n", @@ -749,9 +3272,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 23, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:24.186495Z", + "iopub.status.busy": "2026-07-17T18:49:24.186389Z", + "iopub.status.idle": "2026-07-17T18:49:24.188917Z", + "shell.execute_reply": "2026-07-17T18:49:24.188351Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Percentage difference between large face and sum of small faces: 0.1173%\n" + ] + } + ], "source": [ "percentage_diff = (large_area[0][0] - small_areas[0].sum()) / large_area[0][0] * 100\n", "print(\n", @@ -772,9 +3310,31 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 24, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:24.190589Z", + "iopub.status.busy": "2026-07-17T18:49:24.190486Z", + "iopub.status.idle": "2026-07-17T18:49:24.210654Z", + "shell.execute_reply": "2026-07-17T18:49:24.210173Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Data variables:\n", + " face_lat (n_face) float32 780B ...\n", + " face_lon (n_face) float32 780B ...\n", + " gaussian (n_face) float32 780B ...\n", + " inverse_gaussian (n_face) float32 780B ..." + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "uxds = ux.tutorial.open_dataset(\"mpas-dyamond-30km-gradient\")\n", "uxds.data_vars" @@ -782,9 +3342,1840 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 25, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:24.211794Z", + "iopub.status.busy": "2026-07-17T18:49:24.211715Z", + "iopub.status.idle": "2026-07-17T18:49:24.224317Z", + "shell.execute_reply": "2026-07-17T18:49:24.223649Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.UxDataset> Size: 16B\n",
+       "Dimensions:           ()\n",
+       "Data variables:\n",
+       "    face_lat          float32 4B 1.512e+08\n",
+       "    face_lon          float32 4B 1.218e+11\n",
+       "    gaussian          float32 4B 6.714e+10\n",
+       "    inverse_gaussian  float32 4B 8.804e+10
" + ], + "text/plain": [ + " Size: 16B\n", + "Dimensions: ()\n", + "Data variables:\n", + " face_lat float32 4B 1.512e+08\n", + " face_lon float32 4B 1.218e+11\n", + " gaussian float32 4B 6.714e+10\n", + " inverse_gaussian float32 4B 8.804e+10" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Integrate all data variables at once\n", "integrals = uxds.integrate()\n", @@ -800,9 +5191,1825 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 26, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:49:24.225907Z", + "iopub.status.busy": "2026-07-17T18:49:24.225803Z", + "iopub.status.idle": "2026-07-17T18:49:24.234523Z", + "shell.execute_reply": "2026-07-17T18:49:24.233873Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.UxDataArray 'gaussian' ()> Size: 4B\n",
+       "array(6.7136676e+10, dtype=float32)
" + ], + "text/plain": [ + " Size: 4B\n", + "array(6.7136676e+10, dtype=float32)" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "uxds[\"gaussian\"].integrate()" ] @@ -824,7 +7031,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.14.4" + "version": "3.11.13" } }, "nbformat": 4, diff --git a/docs/user-guide/healpix.ipynb b/docs/user-guide/healpix.ipynb index 2be3459dd..dfdea8e1a 100644 --- a/docs/user-guide/healpix.ipynb +++ b/docs/user-guide/healpix.ipynb @@ -15,9 +15,16 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "19631a950304b3ff", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:16.741253Z", + "iopub.status.busy": "2026-07-17T18:51:16.741130Z", + "iopub.status.idle": "2026-07-17T18:51:18.609827Z", + "shell.execute_reply": "2026-07-17T18:51:18.609175Z" + } + }, "outputs": [], "source": [ "import cartopy.crs as ccrs\n", @@ -72,10 +79,672 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "93f2df22a6decb93", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:18.611777Z", + "iopub.status.busy": "2026-07-17T18:51:18.611586Z", + "iopub.status.idle": "2026-07-17T18:51:18.677575Z", + "shell.execute_reply": "2026-07-17T18:51:18.676929Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<uxarray.Grid>\n",
+       "Original Grid Type: HEALPix\n",
+       "Grid Dimensions:\n",
+       "  * n_face: 768\n",
+       "Grid Coordinates (Spherical):\n",
+       "  * face_lon: (768,)\n",
+       "  * face_lat: (768,)\n",
+       "Grid Coordinates (Cartesian):\n",
+       "Grid Connectivity Variables:\n",
+       "Grid Descriptor Variables:\n",
+       "
" + ], + "text/plain": [ + "\n", + "Original Grid Type: HEALPix\n", + "Grid Dimensions:\n", + " * n_face: 768\n", + "Grid Coordinates (Spherical):\n", + " * face_lon: (768,)\n", + " * face_lat: (768,)\n", + "Grid Coordinates (Cartesian):\n", + "Grid Connectivity Variables:\n", + "Grid Descriptor Variables:" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ux.Grid.from_healpix(zoom=3)" ] @@ -97,10 +766,768 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "428a2bf61174e989", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:18.678949Z", + "iopub.status.busy": "2026-07-17T18:51:18.678755Z", + "iopub.status.idle": "2026-07-17T18:51:18.697964Z", + "shell.execute_reply": "2026-07-17T18:51:18.697542Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<uxarray.Grid>\n",
+       "Original Grid Type: HEALPix\n",
+       "Grid Dimensions:\n",
+       "  * n_node: 770\n",
+       "  * n_face: 768\n",
+       "  * n_max_face_nodes: 4\n",
+       "Grid Coordinates (Spherical):\n",
+       "  * node_lon: (770,)\n",
+       "  * node_lat: (770,)\n",
+       "  * face_lon: (768,)\n",
+       "  * face_lat: (768,)\n",
+       "Grid Coordinates (Cartesian):\n",
+       "Grid Connectivity Variables:\n",
+       "  * face_node_connectivity: (768, 4)\n",
+       "Grid Descriptor Variables:\n",
+       "
" + ], + "text/plain": [ + "\n", + "Original Grid Type: HEALPix\n", + "Grid Dimensions:\n", + " * n_node: 770\n", + " * n_face: 768\n", + " * n_max_face_nodes: 4\n", + "Grid Coordinates (Spherical):\n", + " * node_lon: (770,)\n", + " * node_lat: (770,)\n", + " * face_lon: (768,)\n", + " * face_lat: (768,)\n", + "Grid Coordinates (Cartesian):\n", + "Grid Connectivity Variables:\n", + " * face_node_connectivity: (768, 4)\n", + "Grid Descriptor Variables:" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ux.Grid.from_healpix(zoom=3, pixels_only=False)" ] @@ -115,10 +1542,605 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "935e78eda9c2ce8c", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:18.699161Z", + "iopub.status.busy": "2026-07-17T18:51:18.699066Z", + "iopub.status.idle": "2026-07-17T18:51:18.706113Z", + "shell.execute_reply": "2026-07-17T18:51:18.705558Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'face_node_connectivity' (n_face: 768, n_max_face_nodes: 4)> Size: 25kB\n",
+       "array([[103, 537, 207, 146],\n",
+       "       [362, 578, 537, 103],\n",
+       "       [537,  97, 147, 207],\n",
+       "       ...,\n",
+       "       [560, 550, 709, 536],\n",
+       "       [709, 556, 395,  59],\n",
+       "       [550, 673, 556, 709]], shape=(768, 4))\n",
+       "Dimensions without coordinates: n_face, n_max_face_nodes\n",
+       "Attributes:\n",
+       "    cf_role:      face_node_connectivity\n",
+       "    long name:    Maps every face to its corner nodes.\n",
+       "    start_index:  0\n",
+       "    _FillValue:   -9223372036854775808
" + ], + "text/plain": [ + " Size: 25kB\n", + "array([[103, 537, 207, 146],\n", + " [362, 578, 537, 103],\n", + " [537, 97, 147, 207],\n", + " ...,\n", + " [560, 550, 709, 536],\n", + " [709, 556, 395, 59],\n", + " [550, 673, 556, 709]], shape=(768, 4))\n", + "Dimensions without coordinates: n_face, n_max_face_nodes\n", + "Attributes:\n", + " cf_role: face_node_connectivity\n", + " long name: Maps every face to its corner nodes.\n", + " start_index: 0\n", + " _FillValue: -9223372036854775808" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ux.Grid.from_healpix(zoom=3, pixels_only=True).face_node_connectivity" ] @@ -140,10 +2162,1288 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "34c9de64665bcd77", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:18.707456Z", + "iopub.status.busy": "2026-07-17T18:51:18.707353Z", + "iopub.status.idle": "2026-07-17T18:51:25.795662Z", + "shell.execute_reply": "2026-07-17T18:51:25.795159Z" + } + }, + "outputs": [ + { + "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 version = '3.9.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", + " const reloading = false;\n", + " const Bokeh = root.Bokeh;\n", + " const BK_RE = /^https:\\/\\/cdn\\.bokeh\\.org\\/bokeh\\/(release|dev)\\/bokeh-/;\n", + " const PN_RE = /^https:\\/\\/cdn\\.holoviz\\.org\\/panel\\/[^/]+\\/dist\\/panel/i;\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, Bokeh, 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", + " const shouldSkip = skip.includes(escaped) || existing_scripts.includes(escaped)\n", + " const isBokehOrPanel = BK_RE.test(escaped) || PN_RE.test(escaped)\n", + " const missingOrBroken = Bokeh == null || Bokeh.Panel == null || (Bokeh.version != version && !Bokeh.versions?.has(version)) || Bokeh.versions?.get(version)?.Panel == null;\n", + " if (shouldSkip && !(isBokehOrPanel && missingOrBroken)) {\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.8.10/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.9.0.min.js\", \"https://cdn.holoviz.org/panel/1.8.10/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", + " } 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 = Bokeh != null && ((Bokeh.version === version && Bokeh.Panel) || (Bokeh.versions?.has(version) && Bokeh.versions.get(version)?.Panel));\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, Bokeh, function() {\n", + " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + " run_inline_js();\n", + " if (Bokeh != undefined && !reloading) {\n", + " const NewBokeh = root.Bokeh;\n", + " if (Bokeh.versions === undefined) {\n", + " Bokeh.versions = new Map();\n", + " }\n", + " if (NewBokeh.version !== Bokeh.version) {\n", + " Bokeh[NewBokeh.version] = NewBokeh;\n", + " Bokeh.versions.set(NewBokeh.version, NewBokeh);\n", + " }\n", + " root.Bokeh = Bokeh;\n", + " }\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 version = '3.9.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n const BK_RE = /^https:\\/\\/cdn\\.bokeh\\.org\\/bokeh\\/(release|dev)\\/bokeh-/;\n const PN_RE = /^https:\\/\\/cdn\\.holoviz\\.org\\/panel\\/[^/]+\\/dist\\/panel/i;\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, Bokeh, 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 const shouldSkip = skip.includes(escaped) || existing_scripts.includes(escaped)\n const isBokehOrPanel = BK_RE.test(escaped) || PN_RE.test(escaped)\n const missingOrBroken = Bokeh == null || Bokeh.Panel == null || (Bokeh.version != version && !Bokeh.versions?.has(version)) || Bokeh.versions?.get(version)?.Panel == null;\n if (shouldSkip && !(isBokehOrPanel && missingOrBroken)) {\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.8.10/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.9.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.9.0.min.js\", \"https://cdn.holoviz.org/panel/1.8.10/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 } 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 = Bokeh != null && ((Bokeh.version === version && Bokeh.Panel) || (Bokeh.versions?.has(version) && Bokeh.versions.get(version)?.Panel));\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, Bokeh, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n if (Bokeh != undefined && !reloading) {\n const NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh[NewBokeh.version] = NewBokeh;\n Bokeh.versions.set(NewBokeh.version, NewBokeh);\n }\n root.Bokeh = Bokeh;\n }\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", + " 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", + " let retries = 0;\n", + " const open = () => {\n", + " if (comm.active) {\n", + " comm.open();\n", + " } else if (retries > 3) {\n", + " console.warn('Comm target never activated')\n", + " } else {\n", + " retries += 1\n", + " setTimeout(open, 500)\n", + " }\n", + " }\n", + " if (comm.active) {\n", + " comm.open();\n", + " } else {\n", + " setTimeout(open, 500)\n", + " }\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 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 let retries = 0;\n const open = () => {\n if (comm.active) {\n comm.open();\n } else if (retries > 3) {\n console.warn('Comm target never activated')\n } else {\n retries += 1\n setTimeout(open, 500)\n }\n }\n if (comm.active) {\n comm.open();\n } else {\n setTimeout(open, 500)\n }\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": "8f5636a7-1e8b-4a50-be4d-cb337b0011da" + } + }, + "output_type": "display_data" + }, + { + "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 = false;\n", + " const version = '3.9.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", + " const reloading = true;\n", + " const Bokeh = root.Bokeh;\n", + " const BK_RE = /^https:\\/\\/cdn\\.bokeh\\.org\\/bokeh\\/(release|dev)\\/bokeh-/;\n", + " const PN_RE = /^https:\\/\\/cdn\\.holoviz\\.org\\/panel\\/[^/]+\\/dist\\/panel/i;\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, Bokeh, 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", + " const shouldSkip = skip.includes(escaped) || existing_scripts.includes(escaped)\n", + " const isBokehOrPanel = BK_RE.test(escaped) || PN_RE.test(escaped)\n", + " const missingOrBroken = Bokeh == null || Bokeh.Panel == null || (Bokeh.version != version && !Bokeh.versions?.has(version)) || Bokeh.versions?.get(version)?.Panel == null;\n", + " if (shouldSkip && !(isBokehOrPanel && missingOrBroken)) {\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.8.10/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.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", + " } 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 = Bokeh != null && ((Bokeh.version === version && Bokeh.Panel) || (Bokeh.versions?.has(version) && Bokeh.versions.get(version)?.Panel));\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, Bokeh, function() {\n", + " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + " run_inline_js();\n", + " if (Bokeh != undefined && !reloading) {\n", + " const NewBokeh = root.Bokeh;\n", + " if (Bokeh.versions === undefined) {\n", + " Bokeh.versions = new Map();\n", + " }\n", + " if (NewBokeh.version !== Bokeh.version) {\n", + " Bokeh[NewBokeh.version] = NewBokeh;\n", + " Bokeh.versions.set(NewBokeh.version, NewBokeh);\n", + " }\n", + " root.Bokeh = Bokeh;\n", + " }\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 = false;\n const version = '3.9.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = true;\n const Bokeh = root.Bokeh;\n const BK_RE = /^https:\\/\\/cdn\\.bokeh\\.org\\/bokeh\\/(release|dev)\\/bokeh-/;\n const PN_RE = /^https:\\/\\/cdn\\.holoviz\\.org\\/panel\\/[^/]+\\/dist\\/panel/i;\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, Bokeh, 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 const shouldSkip = skip.includes(escaped) || existing_scripts.includes(escaped)\n const isBokehOrPanel = BK_RE.test(escaped) || PN_RE.test(escaped)\n const missingOrBroken = Bokeh == null || Bokeh.Panel == null || (Bokeh.version != version && !Bokeh.versions?.has(version)) || Bokeh.versions?.get(version)?.Panel == null;\n if (shouldSkip && !(isBokehOrPanel && missingOrBroken)) {\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.8.10/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.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 } 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 = Bokeh != null && ((Bokeh.version === version && Bokeh.Panel) || (Bokeh.versions?.has(version) && Bokeh.versions.get(version)?.Panel));\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, Bokeh, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n if (Bokeh != undefined && !reloading) {\n const NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh[NewBokeh.version] = NewBokeh;\n Bokeh.versions.set(NewBokeh.version, NewBokeh);\n }\n root.Bokeh = Bokeh;\n }\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", + " 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", + " let retries = 0;\n", + " const open = () => {\n", + " if (comm.active) {\n", + " comm.open();\n", + " } else if (retries > 3) {\n", + " console.warn('Comm target never activated')\n", + " } else {\n", + " retries += 1\n", + " setTimeout(open, 500)\n", + " }\n", + " }\n", + " if (comm.active) {\n", + " comm.open();\n", + " } else {\n", + " setTimeout(open, 500)\n", + " }\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 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 let retries = 0;\n const open = () => {\n if (comm.active) {\n comm.open();\n } else if (retries > 3) {\n console.warn('Comm target never activated')\n } else {\n retries += 1\n setTimeout(open, 500)\n }\n }\n if (comm.active) {\n comm.open();\n } else {\n setTimeout(open, 500)\n }\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": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Layout\n", + " .Path.I :Path [Longitude,Latitude]\n", + " .Path.II :Path [Longitude,Latitude]\n", + " .Path.III :Path [Longitude,Latitude]" + ] + }, + "execution_count": 5, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "f1df3494-1f85-4d25-b623-d6c841f8485b" + } + }, + "output_type": "execute_result" + } + ], "source": [ "(\n", " ux.Grid.from_healpix(zoom=2).plot(\n", @@ -170,9 +3470,16 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "eb4879c32c78a4a0", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:25.799224Z", + "iopub.status.busy": "2026-07-17T18:51:25.799102Z", + "iopub.status.idle": "2026-07-17T18:51:27.732690Z", + "shell.execute_reply": "2026-07-17T18:51:27.732098Z" + } + }, "outputs": [], "source": [ "source_uxds = ux.tutorial.open_dataset(\"outCSne30-vortex\")\n", @@ -196,10 +3503,112 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "5adb8285-1886-4992-9f4e-7a89fdaa8986", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:27.734518Z", + "iopub.status.busy": "2026-07-17T18:51:27.734396Z", + "iopub.status.idle": "2026-07-17T18:51:34.294116Z", + "shell.execute_reply": "2026-07-17T18:51:34.293579Z" + } + }, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Layout\n", + " .Path.I :Path [Longitude,Latitude]\n", + " .Path.II :Path [Longitude,Latitude]" + ] + }, + "execution_count": 7, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "1d5ce0d8-3818-4a6c-a84d-6a00b67c2660" + } + }, + "output_type": "execute_result" + } + ], "source": [ "source_uxds.uxgrid.plot(\n", " projection=ccrs.Orthographic(), title=\"Source Grid (CSne30)\"\n", @@ -216,10 +3625,1708 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "1e5cfa34f3bdb8b1", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:34.306443Z", + "iopub.status.busy": "2026-07-17T18:51:34.306284Z", + "iopub.status.idle": "2026-07-17T18:51:34.584817Z", + "shell.execute_reply": "2026-07-17T18:51:34.584152Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "OMP: Info #276: omp_set_nested routine deprecated, please use omp_set_max_active_levels instead.\n", + "/Users/mbook/uxarray/uxarray/remap/utils.py:87: UserWarning: No spatial coordinate variables found in `source`.\n", + " output_coords = coords_remapper.construct_output_coords()\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.UxDataArray 'psi' (n_face: 12288)> Size: 98kB\n",
+       "array([0.631794, 0.634404, 0.666049, ..., 1.424628, 1.437251, 1.437746],\n",
+       "      shape=(12288,))\n",
+       "Dimensions without coordinates: n_face
" + ], + "text/plain": [ + " Size: 98kB\n", + "array([0.631794, 0.634404, 0.666049, ..., 1.424628, 1.437251, 1.437746],\n", + " shape=(12288,))\n", + "Dimensions without coordinates: n_face" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "psi_hp = source_uxds[\"psi\"].remap.nearest_neighbor(destination_grid=hp_grid)\n", "psi_hp" @@ -235,10 +5342,110 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "1c41d4d1-ade5-4a94-8072-27f3c3161344", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:34.586124Z", + "iopub.status.busy": "2026-07-17T18:51:34.586018Z", + "iopub.status.idle": "2026-07-17T18:51:35.201210Z", + "shell.execute_reply": "2026-07-17T18:51:35.200632Z" + } + }, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Image [Longitude,Latitude] (Longitude_Latitude psi)" + ] + }, + "execution_count": 9, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "50eabfcb-4f58-4fff-8c0d-2965dc1b2e02" + } + }, + "output_type": "execute_result" + } + ], "source": [ "psi_hp.plot(cmap=\"inferno\", projection=ccrs.Orthographic(), title=\"Remapped Data\")" ] @@ -256,9 +5463,16 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "3c6c05475b2455cd", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:35.202715Z", + "iopub.status.busy": "2026-07-17T18:51:35.202617Z", + "iopub.status.idle": "2026-07-17T18:51:35.208773Z", + "shell.execute_reply": "2026-07-17T18:51:35.208218Z" + } + }, "outputs": [], "source": [ "psi_hp.to_dataset().to_xarray(grid_format=\"HEALPix\").to_netcdf(\"psi_healpix.nc\")" @@ -296,10 +5510,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "healpix_area_demo", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:35.210089Z", + "iopub.status.busy": "2026-07-17T18:51:35.209984Z", + "iopub.status.idle": "2026-07-17T18:51:35.218299Z", + "shell.execute_reply": "2026-07-17T18:51:35.214701Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Standard deviation: 2.22e-16\n", + "All pixels have area: 1.047198 steradians\n" + ] + } + ], "source": [ "import numpy as np\n", "\n", @@ -319,27 +5549,172 @@ "source": [ "### Still need geometric face area calculations for a HEALPix mesh?\n", "\n", - "For most use cases, the `Grid.face_areas` property provides the recommended approach for accessing face areas. However, if you specifically need geometric calculations of individual HEALPix faces as they are represented in UXarray (rather than the theoretical equal areas), you may want to access the internal computation method, `Grid._compute_face_areas()`. Note that this approach may not preserve HEALPix's equal-area property due to geometric representation differences. \n", + "For most use cases, the `Grid.face_areas` property provides the recommended approach for accessing face areas. However, if you specifically need geometric calculations of individual HEALPix faces as they are represented in UXarray (rather than the theoretical equal areas), use the `Grid.compute_face_areas()` method. Note that this approach may not preserve HEALPix's equal-area property due to geometric representation differences.\n", "\n", "Look at the following example:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "id": "dfd494b5-0b3e-472f-8896-bd3566747bdb", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:35.219749Z", + "iopub.status.busy": "2026-07-17T18:51:35.219638Z", + "iopub.status.idle": "2026-07-17T18:51:35.234196Z", + "shell.execute_reply": "2026-07-17T18:51:35.233682Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Geometric std deviation: 1.50e-01 (vs theoretical equal areas)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/mbook/uxarray/uxarray/grid/grid.py:2042: UserWarning: Total face area 12.596138677284442 exceeds the unit-sphere area 12.566370614359172. This usually indicates a malformed grid or incorrect connectivity.\n", + " face_areas, face_jacobian = self._compute_face_areas_and_jacobian(\n" + ] + } + ], "source": [ "# For advanced use cases: access geometric calculations (may not preserve equal-area property)\n", - "hp_geometric_areas, face_jacobians = grid._compute_face_areas(\n", - " quadrature_rule=\"triangular\", order=4\n", + "hp_geometric_areas, face_jacobians = grid.compute_face_areas(\n", + " quadrature_rule=\"triangular\", order=4, return_jacobian=True\n", ")\n", "print(\n", " f\"Geometric std deviation: {hp_geometric_areas.std():.2e} (vs theoretical equal areas)\"\n", ")" ] }, + { + "cell_type": "markdown", + "id": "ad31a291", + "metadata": {}, + "source": [ + "We can visualize how the geometric face areas deviate from the theoretical HEALPix equal areas by plotting their ratio across the grid. `as_uxarray=True` returns a `UxDataArray`, so the ratio can be plotted directly:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "60f80236", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:35.235425Z", + "iopub.status.busy": "2026-07-17T18:51:35.235344Z", + "iopub.status.idle": "2026-07-17T18:51:36.404198Z", + "shell.execute_reply": "2026-07-17T18:51:36.403690Z" + } + }, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Image [x,y] (x_y var)" + ] + }, + "execution_count": 13, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "9694e7d1-1a52-41ce-98a9-7d88eec8dce4" + } + }, + "output_type": "execute_result" + } + ], + "source": [ + "geometric = grid.compute_face_areas(as_uxarray=True)\n", + "(geometric / grid.face_areas).plot(\n", + " cmap=\"viridis\",\n", + " title=\"Geometric / theoretical HEALPix face-area ratio\",\n", + ")" + ] + }, { "cell_type": "markdown", "id": "healpix_error_analysis", @@ -373,10 +5748,1681 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "7a9e21366632177d", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:36.405589Z", + "iopub.status.busy": "2026-07-17T18:51:36.405493Z", + "iopub.status.idle": "2026-07-17T18:51:36.412659Z", + "shell.execute_reply": "2026-07-17T18:51:36.412234Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.UxDataset> Size: 98kB\n",
+       "Dimensions:  (n_face: 12288)\n",
+       "Dimensions without coordinates: n_face\n",
+       "Data variables:\n",
+       "    psi      (n_face) float64 98kB ...
" + ], + "text/plain": [ + " Size: 98kB\n", + "Dimensions: (n_face: 12288)\n", + "Dimensions without coordinates: n_face\n", + "Data variables:\n", + " psi (n_face) float64 98kB ..." + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "uxds = ux.UxDataset.from_healpix(\"psi_healpix.nc\")\n", "uxds" @@ -392,10 +7438,581 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "de7711664bee2fb2", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:36.414069Z", + "iopub.status.busy": "2026-07-17T18:51:36.413969Z", + "iopub.status.idle": "2026-07-17T18:51:36.418381Z", + "shell.execute_reply": "2026-07-17T18:51:36.417767Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset> Size: 98kB\n",
+       "Dimensions:  (cell: 12288)\n",
+       "Dimensions without coordinates: cell\n",
+       "Data variables:\n",
+       "    psi      (cell) float64 98kB ...
" + ], + "text/plain": [ + " Size: 98kB\n", + "Dimensions: (cell: 12288)\n", + "Dimensions without coordinates: cell\n", + "Data variables:\n", + " psi (cell) float64 98kB ..." + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import xarray as xr\n", "\n", @@ -419,10 +8036,594 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "bbc527a7e5e006d5", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-17T18:51:36.419808Z", + "iopub.status.busy": "2026-07-17T18:51:36.419699Z", + "iopub.status.idle": "2026-07-17T18:51:36.423401Z", + "shell.execute_reply": "2026-07-17T18:51:36.422988Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<uxarray.Grid>\n",
+       "Original Grid Type: HEALPix\n",
+       "Grid Dimensions:\n",
+       "  * n_face: 12288\n",
+       "Grid Coordinates (Spherical):\n",
+       "  * face_lon: (12288,)\n",
+       "  * face_lat: (12288,)\n",
+       "Grid Coordinates (Cartesian):\n",
+       "Grid Connectivity Variables:\n",
+       "Grid Descriptor Variables:\n",
+       "
" + ], + "text/plain": [ + "\n", + "Original Grid Type: HEALPix\n", + "Grid Dimensions:\n", + " * n_face: 12288\n", + "Grid Coordinates (Spherical):\n", + " * face_lon: (12288,)\n", + " * face_lat: (12288,)\n", + "Grid Coordinates (Cartesian):\n", + "Grid Connectivity Variables:\n", + "Grid Descriptor Variables:" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "uxds.uxgrid" ] @@ -440,7 +8641,16 @@ ], "metadata": { "language_info": { - "name": "python" + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.13" } }, "nbformat": 4, diff --git a/test/grid/grid/test_areas.py b/test/grid/grid/test_areas.py index e3f1a54c6..4e720ef04 100644 --- a/test/grid/grid/test_areas.py +++ b/test/grid/grid/test_areas.py @@ -56,7 +56,7 @@ def test_calculate_total_face_area_respects_quadrature_kwargs(mesh_constants): def test_face_areas_compute_face_areas_geoflow_small(gridpath): """Checks if the GeoFlow Small can generate a face areas output.""" grid_geoflow = ux.open_grid(gridpath("ugrid", "geoflow-small", "grid.nc")) - grid_geoflow._compute_face_areas() + grid_geoflow._compute_face_areas_and_jacobian() class TestFaceAreas: diff --git a/test/test_helpers.py b/test/test_helpers.py index 5c032a92a..63de3e294 100644 --- a/test/test_helpers.py +++ b/test/test_helpers.py @@ -25,7 +25,7 @@ def test_face_area_coords(mesh_constants): face_nodes = np.array([[0, 1, 2]]) face_dimension = np.array([3], dtype=INT_DTYPE) - area, _ = ux.grid.area.get_all_face_area_from_coords( + area, _ = ux.grid.area._get_all_face_area_from_coords( x, y, z, face_nodes, face_dimension) nt.assert_almost_equal(area, mesh_constants['TRI_AREA'], decimal=5) @@ -60,7 +60,7 @@ def test_calculate_face_area(mesh_constants): fill_value=-1, ) - area, _ = grid._compute_face_areas() + area, _ = grid._compute_face_areas_and_jacobian() nt.assert_almost_equal(area, mesh_constants['TRI_AREA'], decimal=5) def test_quadrature(): diff --git a/uxarray/grid/area.py b/uxarray/grid/area.py index 02af8741d..171b29c8a 100644 --- a/uxarray/grid/area.py +++ b/uxarray/grid/area.py @@ -1,5 +1,5 @@ import numpy as np -from numba import njit +from numba import njit, prange from uxarray.constants import ERROR_TOLERANCE @@ -44,17 +44,31 @@ def calculate_face_area( area : double jacobian: double """ - area = 0.0 # set area to 0 - jacobian = 0.0 # set jacobian to 0 - order = order - if quadrature_rule == "gaussian": dG, dW = get_gauss_quadrature_dg(order) + is_gaussian = True elif quadrature_rule == "triangular": dG, dW = get_tri_quadrature_dg(order) + is_gaussian = False else: raise ValueError("Invalid quadrature rule, specify gaussian or triangular") + return _face_area_from_quadrature( + x, y, z, dG, dW, is_gaussian, latitude_adjusted_area + ) + + +@njit(cache=True) +def _face_area_from_quadrature(x, y, z, dG, dW, is_gaussian, latitude_adjusted_area): + """Compute one face's area/jacobian from precomputed quadrature points. + + Split out of :func:`calculate_face_area` so the quadrature points ``dG``/``dW`` + (which depend only on the rule and order, not on the face) can be computed once + and reused across every face, instead of being rebuilt per face. + """ + area = 0.0 # set area to 0 + jacobian = 0.0 # set jacobian to 0 + num_nodes = len(x) # num triangles is two less than the total number of nodes @@ -62,25 +76,28 @@ def calculate_face_area( # Using tempestremap GridElements: https://github.com/ClimateGlobalChange/tempestremap/blob/master/src/GridElements.cpp # loop through all sub-triangles of face total_correction = 0.0 + # node1 (the fan apex, vertex 0) is shared by every sub-triangle, so build it + # once instead of per triangle. + node1 = np.array([x[0], y[0], z[0]], dtype=x.dtype) + n_weights = len(dW) for j in range(0, num_triangles): - node1 = np.array([x[0], y[0], z[0]], dtype=x.dtype) node2 = np.array([x[j + 1], y[j + 1], z[j + 1]], dtype=x.dtype) node3 = np.array([x[j + 2], y[j + 2], z[j + 2]], dtype=x.dtype) - for p in range(len(dW)): - if quadrature_rule == "gaussian": - for q in range(len(dW)): + for p in range(n_weights): + if is_gaussian: + for q in range(n_weights): dA = dG[0][p] dB = dG[0][q] - jacobian = calculate_spherical_triangle_jacobian( + jacobian = _calculate_spherical_triangle_jacobian( node1, node2, node3, dA, dB ) area += dW[p] * dW[q] * jacobian jacobian += jacobian - elif quadrature_rule == "triangular": + else: dA = dG[p][0] dB = dG[p][1] - jacobian = calculate_spherical_triangle_jacobian_barycentric( + jacobian = _calculate_spherical_triangle_jacobian_barycentric( node1, node2, node3, dA, dB ) area += dW[p] * jacobian @@ -108,7 +125,7 @@ def calculate_face_area( continue # Check if the edge passes through a pole - passes_through_pole = edge_passes_through_pole(node1, node2) + passes_through_pole = _edge_passes_through_pole(node1, node2) if passes_through_pole: # Skip the edge if it passes through a pole continue @@ -131,7 +148,7 @@ def calculate_face_area( continue # Calculate the correction term - correction = area_correction(node1, node2) + correction = _area_correction(node1, node2) # Check if the longitude is increasing in the northern hemisphere or decreasing in the southern hemisphere if (z_sign > 0 and lon_diff > 0) or (z_sign < 0 and lon_diff < 0): @@ -146,7 +163,7 @@ def calculate_face_area( @njit(cache=True) -def edge_passes_through_pole(node1, node2): +def _edge_passes_through_pole(node1, node2): """ Check if the edge passes through a pole. @@ -175,8 +192,8 @@ def edge_passes_through_pole(node1, node2): ) -@njit(cache=True) -def get_all_face_area_from_coords( +@njit(cache=True, parallel=True, nogil=True) +def _get_all_face_area_from_coords( x, y, z, @@ -224,22 +241,38 @@ def get_all_face_area_from_coords( n_face, n_max_face_nodes = face_nodes.shape + # Compute the quadrature points once (they depend only on the rule and order, + # not on the face) and reuse them across all faces, instead of rebuilding them + # inside every face's area computation. + if quadrature_rule == "gaussian": + dG, dW = get_gauss_quadrature_dg(order) + is_gaussian = True + elif quadrature_rule == "triangular": + dG, dW = get_tri_quadrature_dg(order) + is_gaussian = False + else: + raise ValueError("Invalid quadrature rule, specify gaussian or triangular") + # set initial area of each face to 0 area = np.zeros(n_face) jacobian = np.zeros(n_face) - for face_idx, max_nodes in enumerate(face_geometry): + # Each face is independent, so the loop is parallelized with prange. Every + # iteration writes a distinct area[face_idx]/jacobian[face_idx], so there is + # no shared-write race. + for face_idx in prange(n_face): + max_nodes = face_geometry[face_idx] face_x = x[face_nodes[face_idx, 0:max_nodes]] face_y = y[face_nodes[face_idx, 0:max_nodes]] face_z = z[face_nodes[face_idx, 0:max_nodes]] - # After getting all the nodes of a face assembled call the cal. face area routine - face_area, face_jacobian = calculate_face_area( + face_area, face_jacobian = _face_area_from_quadrature( face_x, face_y, face_z, - quadrature_rule, - order, + dG, + dW, + is_gaussian, latitude_adjusted_area, ) # store current face area @@ -250,7 +283,7 @@ def get_all_face_area_from_coords( @njit(cache=True) -def area_correction(node1, node2): +def _area_correction(node1, node2): """ Calculate the area correction A using the given formula. @@ -281,7 +314,7 @@ def area_correction(node1, node2): @njit(cache=True) -def calculate_spherical_triangle_jacobian(node1, node2, node3, d_a, d_b): +def _calculate_spherical_triangle_jacobian(node1, node2, node3, d_a, d_b): """Calculate Jacobian of a spherical triangle. This is a helper function for calculating face area. @@ -371,7 +404,7 @@ def calculate_spherical_triangle_jacobian(node1, node2, node3, d_a, d_b): @njit(cache=True) -def calculate_spherical_triangle_jacobian_barycentric(node1, node2, node3, d_a, d_b): +def _calculate_spherical_triangle_jacobian_barycentric(node1, node2, node3, d_a, d_b): """Calculate Jacobian of a spherical triangle. This is a helper function for calculating face area. diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index 0385bed7b..e13e5fa74 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -17,7 +17,7 @@ from uxarray.core.utils import _open_dataset_with_fallback from uxarray.cross_sections import GridCrossSectionAccessor from uxarray.formatting_html import grid_repr -from uxarray.grid.area import get_all_face_area_from_coords +from uxarray.grid.area import _get_all_face_area_from_coords from uxarray.grid.bounds import _populate_face_bounds from uxarray.grid.connectivity import ( _populate_edge_face_connectivity, @@ -1505,7 +1505,9 @@ def face_areas(self) -> xr.DataArray: Notes ----- For HEALPix grids, this property returns theoretical equal areas to preserve - the equal-area property. For other grid types, areas are computed geometrically. + the equal-area property. For other grid types, areas are computed geometrically, + and results are equivalent to calling :py:meth:`~uxarray.Grid.compute_face_areas()` + with no arguments. Either way, the computed areas are cached for future use. """ from uxarray.conventions.descriptors import FACE_AREAS_ATTRS, FACE_AREAS_DIMS @@ -1518,7 +1520,9 @@ def face_areas(self) -> xr.DataArray: self._ds["face_areas"] = _compute_healpix_face_areas(self._ds) else: # For other grids, use calculated areas - face_areas, self._face_jacobian = self._compute_face_areas() + face_areas, self._face_jacobian = ( + self._compute_face_areas_and_jacobian() + ) self._ds["face_areas"] = xr.DataArray( data=face_areas, dims=FACE_AREAS_DIMS, attrs=FACE_AREAS_ATTRS ) @@ -1944,21 +1948,27 @@ def calculate_total_face_area( order: int | None = 4, latitude_adjusted_area: bool | None = False, ) -> float: - """Function to calculate the total surface area of all the faces in a - mesh. + """Calculate the total surface area of all the faces in a mesh. + + Equivalent to ``self.compute_face_areas(...).sum()``; provided as a + convenience. Parameters ---------- quadrature_rule : str, optional - Quadrature rule to use. Defaults to "triangular". + Quadrature rule to use, one of ``"triangular"`` or ``"gaussian"``. + Defaults to ``"triangular"``. See :meth:`compute_face_areas` for + what these mean and the supported orders. order : int, optional - Order of quadrature rule. Defaults to 4. + Order of the quadrature rule. Defaults to 4. latitude_adjusted_area : bool, optional - If True, corrects the area of the faces accounting for lines of constant lattitude. Defaults to False. + If True, corrects the area of faces that have edges lying along a + line of constant latitude. Defaults to False. Returns ------- - Sum of area of all the faces in the mesh : float + float + Sum of the area of all faces in the mesh. """ # Default parameters match the cached ``face_areas`` property, which also # preserves the equal-area values used for HEALPix grids; reuse it to avoid @@ -1971,63 +1981,105 @@ def calculate_total_face_area( ): return np.sum(self.face_areas.values) - face_areas, _ = self._compute_face_areas( - quadrature_rule, order, latitude_adjusted_area + return np.sum( + self.compute_face_areas( + quadrature_rule=quadrature_rule, + order=order, + latitude_adjusted_area=latitude_adjusted_area, + ) ) - return np.sum(face_areas) def compute_face_areas( self, quadrature_rule: str | None = "triangular", order: int | None = 4, latitude_adjusted_area: bool | None = False, + return_jacobian: bool = False, + as_uxarray: bool = False, ): - """Face areas calculation function for grid class, calculates area of - all faces in the grid. + """Compute the area of each face in the grid. - .. deprecated:: 2025.10.0 - Use the `face_areas` property instead for better performance and caching. - This method will be removed in a future version. + Unlike the cached :attr:`face_areas` property (which always uses the + default quadrature), this method computes areas with a caller-specified + quadrature rule and order. For repeated access with default settings, + prefer the :attr:`face_areas` property, which caches its result. Parameters ---------- quadrature_rule : str, optional - Quadrature rule to use. Defaults to "triangular". + Quadrature rule used to integrate each face, one of: + + - ``"triangular"`` (default): symmetric quadrature over each + spherical sub-triangle; supported orders are 1, 4, 8, 10, 12. + - ``"gaussian"``: tensor-product Gauss-Legendre quadrature; + supported orders are 1 to 10. + + A quadrature rule is the set of sample points and weights used to + numerically approximate the surface integral; a higher order uses + more points for a more accurate area at greater cost. order : int, optional - Order of quadrature rule. Defaults to 4. + Order of the quadrature rule. Defaults to 4. latitude_adjusted_area : bool, optional - If True, corrects the area of the faces accounting for lines of constant lattitude. Defaults to False. + If True, corrects the area of faces that have edges lying along a + line of constant latitude (a small-circle edge encloses slightly + more area than the great-circle arc between the same endpoints). + Defaults to False. + return_jacobian : bool, optional + If True, also return the per-face Jacobian of the integration. + Defaults to False. + as_uxarray : bool, optional + If True, return the areas as a :class:`~uxarray.UxDataArray` paired + with this grid (``result.uxgrid = self``), so the result can be + plotted directly, instead of a raw :class:`numpy.ndarray`. Defaults + to False. Returns ------- - 1. Area of all the faces in the mesh : np.ndarray - 2. Jacobian of all the faces in the mesh : np.ndarray + numpy.ndarray or uxarray.UxDataArray + Area of each face, shape ``(n_face,)``. Returned as a + :class:`~uxarray.UxDataArray` when ``as_uxarray=True``. + numpy.ndarray + Per-face Jacobian, only returned when ``return_jacobian=True``. Notes ----- - This method performs geometric integration to compute face areas. For HEALPix grids, - this may not preserve the equal-area property due to differences between algorithmic - pixel definitions and geometric representation. For HEALPix grids, use the - ``face_areas`` property instead, which ensures mathematical correctness by using - theoretical equal areas. + This method performs geometric integration. For HEALPix grids this may + not preserve the equal-area property, since it integrates the polygonal + face representation rather than the theoretical pixel areas; use the + :attr:`face_areas` property for HEALPix equal areas. """ - import warnings - - warnings.warn( - "compute_face_areas() is deprecated. Use the face_areas property instead for better performance and caching.", - DeprecationWarning, - stacklevel=2, + face_areas, face_jacobian = self._compute_face_areas_and_jacobian( + quadrature_rule, order, latitude_adjusted_area ) - return self._compute_face_areas(quadrature_rule, order, latitude_adjusted_area) - def _compute_face_areas( + if as_uxarray: + from uxarray.conventions.descriptors import ( + FACE_AREAS_ATTRS, + FACE_AREAS_DIMS, + ) + from uxarray.core.dataarray import UxDataArray + + face_areas = UxDataArray( + data=face_areas, + dims=FACE_AREAS_DIMS, + attrs=FACE_AREAS_ATTRS, + uxgrid=self, + ) + + if return_jacobian: + return face_areas, face_jacobian + return face_areas + + def _compute_face_areas_and_jacobian( self, quadrature_rule: str | None = "triangular", order: int | None = 4, latitude_adjusted_area: bool | None = False, ): - """Internal face areas calculation function for grid class, calculates area of - all faces in the grid. + """Internal worker: compute per-face areas and Jacobians. + + Returns both the areas and the Jacobians; :meth:`compute_face_areas` is + the public entry point and selects what to return. Parameters ---------- @@ -2042,14 +2094,6 @@ def _compute_face_areas( ------- 1. Area of all the faces in the mesh : np.ndarray 2. Jacobian of all the faces in the mesh : np.ndarray - - Notes - ----- - This method performs geometric integration to compute face areas. For HEALPix grids, - this may not preserve the equal-area property due to differences between algorithmic - pixel definitions and geometric representation. For HEALPix grids, use the - ``face_areas`` property instead, which ensures mathematical correctness by using - theoretical equal areas. """ # if self._face_areas is None: # this allows for using the cached result, # but is not the expected behavior behavior as we are in need to recompute if this function is called with different quadrature_rule or order @@ -2071,7 +2115,7 @@ def _compute_face_areas( n_nodes_per_face = self.n_nodes_per_face.values # call function to get area of all the faces as a np array - self._face_areas, self._face_jacobian = get_all_face_area_from_coords( + self._face_areas, self._face_jacobian = _get_all_face_area_from_coords( x, y, z,