From 53c64a2d15cfab8b25a12d5d0b0bd66106e5d2a2 Mon Sep 17 00:00:00 2001 From: reint-fischer Date: Wed, 26 Nov 2025 08:06:53 +0100 Subject: [PATCH 1/7] rewrite unitconverter guide --- .../examples_v3/tutorial_unitconverters.ipynb | 359 +++++++++++++++--- 1 file changed, 314 insertions(+), 45 deletions(-) diff --git a/docs/user_guide/examples_v3/tutorial_unitconverters.ipynb b/docs/user_guide/examples_v3/tutorial_unitconverters.ipynb index 838c21e5ac..8aa5799a7e 100644 --- a/docs/user_guide/examples_v3/tutorial_unitconverters.ipynb +++ b/docs/user_guide/examples_v3/tutorial_unitconverters.ipynb @@ -28,9 +28,181 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "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 py_version = '3.8.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.8.2/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.8.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.8.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.8.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.8.0.min.js\", \"https://cdn.holoviz.org/panel/1.8.2/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));", + "application/vnd.holoviews_load.v0+json": "" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "\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", + "application/vnd.holoviews_load.v0+json": "" + }, + "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": "9ce5ab11-2cdd-4d2b-b60b-c5402fb7c279" + } + }, + "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 py_version = '3.8.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = true;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.8.2/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 // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));", + "application/vnd.holoviews_load.v0+json": "" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "\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", + "application/vnd.holoviews_load.v0+json": "" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/fd/p7j_05zx409dzmtfjjjnjgfh0000gn/T/ipykernel_28627/1357681003.py:4: UserWarning: This is an alpha version of Parcels v4. The API is not stable and may change without deprecation warnings.\n", + " import parcels\n" + ] + } + ], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", @@ -40,20 +212,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ - "xdim, ydim = (10, 20)\n", - "data = {\n", - " \"U\": np.ones((ydim, xdim), dtype=np.float32),\n", - " \"V\": np.ones((ydim, xdim), dtype=np.float32),\n", - " \"temp\": 20 * np.ones((ydim, xdim), dtype=np.float32),\n", - "}\n", - "dims = {\n", - " \"lon\": np.linspace(-15, 5, xdim, dtype=np.float32),\n", - " \"lat\": np.linspace(35, 60, ydim, dtype=np.float32),\n", - "}" + "from parcels._datasets.structured.generated import simple_UV_dataset\n", + "nlat = 10\n", + "nlon = 18\n", + "ds = simple_UV_dataset(dims=(1, 1, nlat, nlon), mesh=\"spherical\").isel(time=0, depth=0)\n", + "ds[\"temperature\"] = ds[\"U\"] + 20 # add temperature field of 20 deg\n", + "ds[\"U\"].data[:] = 1.0 # set U to 1 m/s\n", + "ds[\"V\"].data[:] = 1.0 # set V to 1 m/s\n", + "grid = parcels.XGrid.from_dataset(ds, mesh=\"spherical\")\n", + "U = parcels.Field(\"U\", ds[\"U\"], grid, interp_method=parcels.interpolators.XLinear)\n", + "V = parcels.Field(\"V\", ds[\"V\"], grid, interp_method=parcels.interpolators.XLinear)\n", + "UV = parcels.VectorField(\"UV\", U, V)\n", + "temperature = parcels.Field(\"temperature\", ds[\"temperature\"], grid, interp_method=parcels.interpolators.XLinear)\n", + "fieldset = parcels.FieldSet([U, V, UV, temperature])" ] }, { @@ -68,12 +243,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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CxYskGEYevDBBzV79my31wMEBQWpW7duSkxM9HmQAADgArhVumZVrwSIi4tTUlKSAgMDGyQoAACA2tTrCbsDBgxw/fnEiRM6deqU2/ehoaHeRQUAAOrGB8tGZqm81GvD7g8//KDU1FR16tRJbdq0UXh4uFsDAACNjLuNzu/xxx/XRx99pEWLFslut+ull17S7NmzFRMTo+XLl/s6RgAAAJd6LRu9++67Wr58uW688UY9+OCD6t+/vy6++GLFxsZqxYoV+vnPf+7rOAEAwPk0ow279aq8fP/994qLi5N0Zn/L999/L0m6/vrrtWHDBt9FBwAAPFJ1q7S3zQzqlbx0795d+/btkyRdfvnl+r//+z9JZyoyZ98+DQAA4Gv1Sl4eeOABffHFF5Kk9PR0196XKVOm6IknnvBpgAAAAGer156XKVOmuP48cOBAff3119q6das6duyoZcuW+Sw4AADgIfa81E3Xrl01YsQIhYaG6tVXX/XFKQEAQB2w5wUAAMBP1WvZCAAA+CGTVE68RfICAIAVNKM9L3VKXkaMGHHe748dO+ZNLAAAABdUp+TlQs9wCQsL0+jRo70KCAAA1J0vNtyaZcNunZIXboMGAMBPNaNlI+42AgAApsKGXQAALKA5LRtReQEAwAoMH7U6WrRokeLi4hQcHKyEhARt3LjRo+M++eQTtWzZUr17967zNUleAABAvaxatUqTJ0/WjBkzlJeXp/79+2vo0KHKz88/73ElJSUaPXq0br755npdl+QFAAAraILKy/z58zV27FiNGzdOl112mRYsWKAuXbpo8eLF5z3u4Ycf1qhRo5SYmFi3C/6I5AUAAAvw5buNSktL3ZrD4ah2vYqKCuXm5io5OdmtPzk5WZs3b641zmXLlunbb7/VrFmz6j1XkhcAAKzAh5WXLl26KCwszNWysrKqXe7w4cOqrKxUZGSkW39kZKSKiopqDHHXrl2aPn26VqxYoZYt63/PEHcbAQAANwUFBQoNDXV9ttvttY612Wxunw3DqNYnSZWVlRo1apRmz56tXr16eRUfyQsAAFbgw4fUhYaGuiUvNYmIiFBAQEC1KktxcXG1aowklZWVaevWrcrLy1Nqaqokyel0yjAMtWzZUmvXrtVNN93kUZgkLwAAWEBjP+clKChICQkJys7O1l133eXqz87O1h133FFtfGhoqLZt2+bWt2jRIn300Ud64403FBcX5/G1SV4AAEC9pKWl6f7771ffvn2VmJioF198Ufn5+ZowYYIkKT09XQcPHtTy5cvVokULxcfHux3fqVMnBQcHV+u/EMts2N23b5/Gjh2ruLg4hYSEqEePHpo1a5YqKircxtlstmptyZIlTRQ1AAA+0gS3So8cOVILFixQZmamevfurQ0bNmjNmjWKjY2VJBUWFl7wmS/1YZnKy9dffy2n06k//elPuvjii7V9+3aNHz9ex48f17PPPus2dtmyZRoyZIjr84Xelg0AgL9rqtcDTJw4URMnTqzxu1deeeW8x2ZkZCgjI6PO17RM8jJkyBC3hKR79+7697//rcWLF1dLXtq1a6eoqKjGDhEAAPiAZZaNalJSUqL27dtX609NTVVERISuvvpqLVmyRE6n87zncTgc1R7YAwCAX2midxs1BctUXs717bff6vnnn9e8efPc+n/3u9/p5ptvVkhIiD788ENNnTpVhw8f1syZM2s9V1ZWlmbPnt3QIQMAUH8+vFXa3/l95SUjI6PGTbZnt61bt7od891332nIkCG6++67NW7cOLfvZs6cqcTERPXu3VtTp05VZmamnnnmmfPGkJ6erpKSElcrKCjw+TwBAIBn/L7ykpqaqnvvvfe8Y7p16+b683fffaeBAwe6btm6kH79+qm0tFSHDh2q8aE60pknC57v6YIAADQ124/N23OYgd8nLxEREYqIiPBo7MGDBzVw4EAlJCRo2bJlatHiwoWlvLw8BQcHq127dl5GCgBAE2pGy0Z+n7x46rvvvtONN96orl276tlnn9V//vMf13dVdxa9++67KioqUmJiokJCQvTxxx9rxowZeuihh6isAABMralulW4Klkle1q5dq927d2v37t266KKL3L4zjDP/GoGBgVq0aJHS0tLkdDrVvXt3ZWZm6pFHHmmKkAEAQD1YJnkZM2aMxowZc94x5z4LBgAAy2DZCAAAmI5Jkg9v+f2t0gAAAGej8gIAgAWwYRcAAJhLM9rzwrIRAAAwFSovAABYAMtGAADAXFg2AgAA8E9UXgAAsACWjQAAgLk0o2UjkhcAAKygGSUv7HkBAACmQuUFAAALYM8LAAAwF5aNAAAA/BOVFwAALMBmGLIZ3pVOvD2+sZC8AABgBSwbAQAA+CcqLwAAWAB3GwEAAHNh2QgAAMA/UXkBAMACWDYCAADm0oyWjUheAACwgOZUeWHPCwAAMBUqLwAAWAHLRgAAwGzMsuzjLZaNAACAqVB5AQDACgzjTPP2HCZA8gIAgAVwtxEAAICfovICAIAVcLcRAAAwE5vzTPP2HGbAshEAADAVKi8AAFhBM1o2slTlpVu3brLZbG5t+vTpbmPy8/M1fPhwtW7dWhEREZo0aZIqKiqaKGIAAHyj6m4jb5sZWK7ykpmZqfHjx7s+t2nTxvXnyspKDRs2TB07dtSmTZt05MgRpaSkyDAMPf/8800RLgAAvsFzXsyrbdu2ioqKqvG7tWvX6quvvlJBQYFiYmIkSfPmzdOYMWM0d+5chYaGNmaoAACgHiy1bCRJv//979WhQwf17t1bc+fOdVsSysnJUXx8vCtxkaTBgwfL4XAoNze31nM6HA6Vlpa6NQAA/AnLRib12GOPqU+fPgoPD9c///lPpaena+/evXrppZckSUVFRYqMjHQ7Jjw8XEFBQSoqKqr1vFlZWZo9e3aDxg4AgFfYsOs/MjIyqm3CPbdt3bpVkjRlyhQNGDBAV155pcaNG6clS5Zo6dKlOnLkiOt8Nput2jUMw6ixv0p6erpKSkpcraCgwPcTBQAAHvH7yktqaqruvffe847p1q1bjf39+vWTJO3evVsdOnRQVFSUPvvsM7cxR48e1alTp6pVZM5mt9tlt9vrFjgAAI2oOb3byO+Tl4iICEVERNTr2Ly8PElSdHS0JCkxMVFz585VYWGhq2/t2rWy2+1KSEjwTcAAADQF7jYyn5ycHH366acaOHCgwsLCtGXLFk2ZMkW33367unbtKklKTk7W5Zdfrvvvv1/PPPOMvv/+e02bNk3jx4/nTiMAAEzCMsmL3W7XqlWrNHv2bDkcDsXGxmr8+PF64oknXGMCAgL097//XRMnTtR1112nkJAQjRo1Ss8++2wTRg4AgPdYNjKhPn366NNPP73guK5du+pvf/tbI0QEAEAj4m4jAAAA/2SZygsAAM0Zy0YAAMBcnMaZ5u05TIDkBQAAK2DPCwAAgH+i8gIAgAXY5IM9Lz6JpOGRvAAAYAXN6Am7LBsBAABTIXkBAMACqm6V9rbV1aJFixQXF6fg4GAlJCRo48aNtY596623NGjQIHXs2FGhoaFKTEzU+++/X+drkrwAAGAFho9aHaxatUqTJ0/WjBkzlJeXp/79+2vo0KHKz8+vcfyGDRs0aNAgrVmzRrm5uRo4cKCGDx/uepGyp2yGYZIFLj9SWlqqsLAwHf2mu0LbBjR1OAAAP1VaVqnwXntUUlLSYC8ArvpNun5ghlq2DPbqXKdPn9SmjzM8jvfaa69Vnz59tHjxYlffZZddpjvvvFNZWVkeXfMnP/mJRo4cqd/+9rcex0nlBQAAC7AZhk+adCYhOrs5HI5q16uoqFBubq6Sk5Pd+pOTk7V582aPYnY6nSorK1P79u3rNFeSFwAArMDpoyapS5cuCgsLc7WaqiiHDx9WZWWlIiMj3fojIyNVVFTkUcjz5s3T8ePHdc8999RpqtwqDQAA3BQUFLgtG9nt9lrH2mzuT4cxDKNaX01ef/11ZWRk6K9//as6depUp/hIXgAAsICzl328OYckhYaGXnDPS0REhAICAqpVWYqLi6tVY861atUqjR07Vn/5y190yy231DlOlo0AALCCRr7bKCgoSAkJCcrOznbrz87OVlJSUq3Hvf766xozZoxee+01DRs2zPMLnoXKCwAAVtAET9hNS0vT/fffr759+yoxMVEvvvii8vPzNWHCBElSenq6Dh48qOXLl0s6k7iMHj1af/jDH9SvXz9X1SYkJERhYWEeX5fkBQAA1MvIkSN15MgRZWZmqrCwUPHx8VqzZo1iY2MlSYWFhW7PfPnTn/6k06dP65FHHtEjjzzi6k9JSdErr7zi8XV5zks98JwXAIAnGvM5LwOSfuOT57ys3/y7Bo3XF6i8AABgBbyYEQAAwD9ReQEAwAJszjPN23OYAckLAABWwLIRAACAf6LyAgCAFdTxIXO1nsMESF4AALAAX74ewN+xbAQAAEyFygsAAFbQjDbskrwAAGAFhiRvb3U2R+5C8gIAgBWw5wUAAMBPUXkBAMAKDPlgz4tPImlwJC8AAFhBM9qwy7IRAAAwFSovAABYgVOSzQfnMAHLVF7WrVsnm81WY9uyZYtrXE3fL1mypAkjBwDAe1V3G3nbzMAylZekpCQVFha69f3mN7/RBx98oL59+7r1L1u2TEOGDHF9DgsLa5QYAQCA9yyTvAQFBSkqKsr1+dSpU3rnnXeUmpoqm829jtauXTu3sQAAmB4bds3vnXfe0eHDhzVmzJhq36WmpioiIkJXX321lixZIqfTJIt8AADUpip58baZgGUqL+daunSpBg8erC5durj1/+53v9PNN9+skJAQffjhh5o6daoOHz6smTNn1nouh8Mhh8Ph+lxaWtpgcQMAgPPz+8pLRkZGrRtxq9rWrVvdjjlw4IDef/99jR07ttr5Zs6cqcTERPXu3VtTp05VZmamnnnmmfPGkJWVpbCwMFc7NyECAKDJUXnxH6mpqbr33nvPO6Zbt25un5ctW6YOHTro9ttvv+D5+/Xrp9LSUh06dEiRkZE1jklPT1daWprrc2lpKQkMAMC/NKNbpf0+eYmIiFBERITH4w3D0LJlyzR69GgFBgZecHxeXp6Cg4PVrl27WsfY7XbZ7XaPYwAAoLE1pxcz+n3yUlcfffSR9u7dW+OS0bvvvquioiIlJiYqJCREH3/8sWbMmKGHHnqI5AQAAJOwXPKydOlSJSUl6bLLLqv2XWBgoBYtWqS0tDQ5nU51795dmZmZeuSRR5ogUgAAfKgZ3SptueTltddeq/W7IUOGuD2cDgAAy3Aaks3L5MNpjuTF7+82AgAAOJvlKi8AADRLLBsBAABz8cVzWsyRvLBsBAAATIXKCwAAVsCyEQAAMBWnIa+XfbjbCAAAwPeovAAAYAWG80zz9hwmQPICAIAVsOcFAACYCnteAAAA/BOVFwAArIBlIwAAYCqGfJC8+CSSBseyEQAAMBUqLwAAWAHLRgAAwFScTklePqfFaY7nvLBsBAAATIXKCwAAVsCyEQAAMJVmlLywbAQAAEyFygsAAFbQjF4PQPICAIAFGIZThpdvhfb2+MZC8gIAgBUYhveVE/a8AAAA+B6VFwAArMDwwZ4Xk1ReSF4AALACp1OyeblnxSR7Xlg2AgAApkLlBQAAK2DZCAAAmInhdMrwctnILLdKs2wEAABMhcoLAABWwLIRAAAwFach2ZpH8sKyEQAAMBUqLwAAWIFhSPL2OS/mqLyQvAAAYAGG05Dh5bKRQfICAAAajeGU95UXbpX2qblz5yopKUmtWrVSu3btahyTn5+v4cOHq3Xr1oqIiNCkSZNUUVHhNmbbtm0aMGCAQkJC1LlzZ2VmZpom0wQAwN8sWrRIcXFxCg4OVkJCgjZu3Hje8evXr1dCQoKCg4PVvXt3LVmypM7XNE3yUlFRobvvvlu//OUva/y+srJSw4YN0/Hjx7Vp0yatXLlSb775pqZOneoaU1paqkGDBikmJkZbtmzR888/r2effVbz589vrGkAANAgDKfhk1YXq1at0uTJkzVjxgzl5eWpf//+Gjp0qPLz82scv3fvXt16663q37+/8vLy9Otf/1qTJk3Sm2++Wafr2gyTlR1eeeUVTZ48WceOHXPr/8c//qHbbrtNBQUFiomJkSStXLlSY8aMUXFxsUJDQ7V48WKlp6fr0KFDstvtkqSnnnpKzz//vA4cOCCbzeZRDKWlpQoLC9PRb7ortG2AT+cHALCO0rJKhffao5KSEoWGhjbMNX78TbpRd6ilLdCrc502Tmmd/upxvNdee6369OmjxYsXu/ouu+wy3XnnncrKyqo2/le/+pXeeecd7dy509U3YcIEffHFF8rJyfE4TsvsecnJyVF8fLwrcZGkwYMHy+FwKDc3VwMHDlROTo4GDBjgSlyqxqSnp2vfvn2Ki4ur8dwOh0MOh8P1uaSkRJJUWm6OtUEAQNOo+p1ojDrBaZ3y+hl1p3VK0pmE6Gx2u93tt1M6syKSm5ur6dOnu/UnJydr8+bNNZ4/JydHycnJbn2DBw/W0qVLderUKQUGepZ8WSZ5KSoqUmRkpFtfeHi4goKCVFRU5BrTrVs3tzFVxxQVFdWavGRlZWn27NnV+mP77PM+cACA5ZWVlSksLKxBzh0UFKSoqChtKlrjk/O1adNGXbp0ceubNWuWMjIy3PoOHz6sysrKar+9kZGRrt/dc9X0Wx0ZGanTp0/r8OHDio6O9ijGJk1eMjIyakwKzrZlyxb17dvXo/PVtOxjGIZb/7ljqrLh8y0ZpaenKy0tzfXZ6XTq+++/V4cOHTxeamoKpaWl6tKliwoKChqsXNmYmI9/Yz7+jfk0DcMwVFZW5rYq4GvBwcHau3dvtRtU6uvc301J1aouZ6vpd/V8v431+R0+V5MmL6mpqbr33nvPO+bcSkltoqKi9Nlnn7n1HT16VKdOnXJleVFRUdWyweLiYkmqlgmeraZyWW13PPmj0NBQv/6Pu66Yj39jPv6N+TS+hqq4nC04OFjBwcENfp2zRUREKCAgoMbf1dp+U2v7HW7ZsqU6dOjg8bWbNHmJiIhQRESET86VmJiouXPnqrCw0FV2Wrt2rex2uxISElxjfv3rX6uiokJBQUGuMTExMR4nSQAA4MxyVUJCgrKzs3XXXXe5+rOzs3XHHXfUeExiYqLeffddt761a9eqb9++Hu93kUx0q3R+fr4+//xz5efnq7KyUp9//rk+//xzlZeXSzqzQejyyy/X/fffr7y8PH344YeaNm2axo8f78rKR40aJbvdrjFjxmj79u1avXq1nnzySaWlpfn18g8AAP4oLS1NL730kl5++WXt3LlTU6ZMUX5+viZMmCDpzLaL0aNHu8ZPmDBB+/fvV1pamnbu3KmXX35ZS5cu1bRp0+p2YcMkUlJSqt717dY+/vhj15j9+/cbw4YNM0JCQoz27dsbqampxsmTJ93O8+WXXxr9+/c37Ha7ERUVZWRkZBhOp7ORZ9M4Tp48acyaNava34FZMR//xnz8G/NBQ3nhhReM2NhYIygoyOjTp4+xfv1613cpKSnGgAED3MavW7fO+OlPf2oEBQUZ3bp1MxYvXlzna5ruOS8AAKB5M82yEQAAgETyAgAATIbkBQAAmArJCwAAMBWSFwuYO3eukpKS1KpVq1ofnmez2aq1c19Dvm3bNg0YMEAhISHq3LmzMjMzG+V9HOfyZD75+fkaPny4WrdurYiICE2aNKna0yX9ZT416datW7V/j3PfD+LJHP3FokWLFBcXp+DgYCUkJGjjxo1NHZJHMjIyqv07REVFub43DEMZGRmKiYlRSEiIbrzxRu3YsaMJI3a3YcMGDR8+XDExMbLZbHr77bfdvvckfofDoUcffVQRERFq3bq1br/9dh04cKARZ/FfF5rPmDFjqv179evXz22MP80HDYfkxQIqKip0991365e//OV5xy1btkyFhYWulpKS4vqutLRUgwYNUkxMjLZs2aLnn39ezz77rObPn9/Q4VdzoflUVlZq2LBhOn78uDZt2qSVK1fqzTff1NSpU11j/Gk+tcnMzHT795g5c6brO0/m6C9WrVqlyZMna8aMGcrLy1P//v01dOhQ5efnN3VoHvnJT37i9u+wbds213dPP/205s+fr4ULF2rLli2KiorSoEGDVFZW1oQR/9fx48d11VVXaeHChTV+70n8kydP1urVq7Vy5Upt2rRJ5eXluu2221RZWdlY03C50HwkaciQIW7/XmvWuL/Px5/mgwZU/zu74W+WLVtmhIWF1fidJGP16tW1Hrto0SIjLCzM7ZkJWVlZRkxMTJM9B6e2+axZs8Zo0aKFcfDgQVff66+/btjtdqOkpMQwDP+cz9liY2ON5557rtbvPZmjv7jmmmuMCRMmuPVdeumlxvTp05soIs/NmjXLuOqqq2r8zul0GlFRUcZTTz3l6jt58qQRFhZmLFmypJEi9Ny5/417Ev+xY8eMwMBAY+XKla4xBw8eNFq0aGG89957jRZ7TWr6/1kpKSnGHXfcUesx/jwf+BaVl2YkNTVVERERuvrqq7VkyRI5nU7Xdzk5ORowYIDbO5wGDx6s7777Tvv27WuCaGuXk5Oj+Ph4txedDR48WA6HQ7m5ua4x/j6f3//+9+rQoYN69+6tuXPnui0JeTJHf1BRUaHc3Nxqr7hPTk7W5s2bmyiqutm1a5diYmIUFxene++9V3v27JEk7d27V0VFRW5zs9vtGjBggCnm5kn8ubm5OnXqlNuYmJgYxcfH++0c161bp06dOqlXr14aP3686/10kjnng/pp0ncbofH87ne/080336yQkBB9+OGHmjp1qg4fPuxaqigqKqr2fqeqF2sVFRUpLi6usUOuVU2vVA8PD1dQUJDrhV/+Pp/HHntMffr0UXh4uP75z38qPT1de/fu1UsvveSK8UJz9AeHDx9WZWVlja+496c4a3Pttddq+fLl6tWrlw4dOqQ5c+YoKSlJO3bscMVf09z279/fFOHWiSfxFxUVKSgoSOHh4dXG+OO/39ChQ3X33XcrNjZWe/fu1W9+8xvddNNNys3Nld1uN918UH9UXvxUTRsJz21bt271+HwzZ85UYmKievfuralTpyozM1PPPPOM2xhfvKa8seZTU0zGOa9hb8j51KQuc5wyZYoGDBigK6+8UuPGjdOSJUu0dOlSHTlypNb4q+bgj+/hqunv2h/jPNfQoUP1s5/9TFdccYVuueUW/f3vf5ckvfrqq64xZp1blfrE769zHDlypIYNG6b4+HgNHz5c//jHP/TNN9+4/t1q46/zQf1RefFTqampuvfee887xps3Yffr10+lpaU6dOiQIiMja31NuVT9f7nVhy/nExUVpc8++8yt7+jRozp16pQr1oaeT028mWPVHRO7d+9Whw4dPJqjP4iIiFBAQECNf9f+FKenWrdurSuuuEK7du3SnXfeKelMdaLqTfWSeeZWddfU+eKPiopSRUWFjh496latKC4uVlJSUuMGXA/R0dGKjY3Vrl27JJl/PvAclRc/FRERoUsvvfS8LTg4uN7nz8vLU3BwsOtW5MTERG3YsMFt38XatWsVExPjVZJUxZfzSUxM1Pbt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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "fieldset = parcels.FieldSet.from_data(data, dims, mesh=\"spherical\")\n", - "plt.pcolormesh(fieldset.U.lon, fieldset.U.lat, fieldset.U.data[0, :, :], vmin=0, vmax=1)\n", + "# fieldset = parcels.FieldSet.from_data(data, dims, mesh=\"spherical\")\n", + "plt.pcolormesh(fieldset.U.grid.lon, fieldset.U.grid.lat, fieldset.U.data[0, 0, :, :], vmin=0, vmax=1, shading=\"nearest\")\n", + "plt.ylabel(\"Latitude\")\n", + "plt.xlabel(\"Longitude\")\n", "plt.colorbar()\n", "plt.show()" ] @@ -88,12 +276,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(array([1.17477258e-05]), array([8.99928006e-06]))\n", + "[20.]\n" + ] + } + ], "source": [ - "print(fieldset.UV[0, 0, 40, -5])\n", - "print(fieldset.temp[0, 0, 40, -5])" + "print(fieldset.UV[np.array([0]), np.array([0]), np.array([40]), np.array([-5])])\n", + "print(fieldset.temperature[np.array([0]), np.array([0]), np.array([40]), np.array([-5])])" ] }, { @@ -108,11 +305,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "U: \n", + "V: \n", + "temperature: \n" + ] + } + ], "source": [ - "for fld in [fieldset.U, fieldset.V, fieldset.temp]:\n", + "for fld in [fieldset.U, fieldset.V, fieldset.temperature]:\n", " print(f\"{fld.name}: {fld.units}\")" ] }, @@ -128,11 +335,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1.]\n" + ] + } + ], "source": [ - "u, v = fieldset.UV[0, 0, 40, -5]\n", + "u, v = fieldset.UV[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]\n", "print(v * 1852 * 60)" ] }, @@ -146,11 +361,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 43, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(array([1.]), array([1.]))\n" + ] + } + ], "source": [ - "print(fieldset.UV.eval(0, 0, 40, -5, applyConversion=False))" + "print(fieldset.UV.eval(np.array([0]), np.array([0]), np.array([40]), np.array([-5]), applyConversion=False))" ] }, { @@ -171,24 +394,55 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 49, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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MTExiomJ0ahRo2o83O5MDofDZ3v88cc9Y/r371/j/XPduK0xYh87dmyNuHr37u01pime95MnT+r//u//1L17d7Vo0UKJiYkaPXq0Dh065DXOivNuxWOjN2zYoMsvv1xOp1OXX365Nm3a5FeMgYh948aNGjRokC688EJFR0crLS1Nr732mteYVatW+fzunzhxwra4t27d6jOmjz/+2GtcUzznvv4+OhwOXXHFFZ4xjXXOt2/frmHDhikxMVEOh0MvvvjiObdpSt/1kOT39TNB5te//rVZtGiRycjI8PlwOF/mz59vWrVqZTZs2GB2795tRowYYRISEkxFRYVnzMSJE81FF11kcnJyzHvvvWcGDBhgrrrqKnPq1KmAxT5kyBDTrVs3k5uba3Jzc023bt3MLbfcctZtiouLvdqKFSuMw+EwX3zxhWdMv379zIQJE7zGHT16NGBxNzT2MWPGmCFDhnjFdfjwYa8xTfG8Hz161Nx0001m/fr15uOPPzY7d+40vXr1MqmpqV7jAn3e161bZ8LDw80f//hHs2fPHvPAAw+YFi1amP379/scv2/fPnPBBReYBx54wOzZs8f88Y9/NOHh4ebPf/6zZ0xubq4JCwsz8+bNM3v37jXz5s0zzZs3N++8806D4wxE7A888IB57LHHzD//+U/z6aefmszMTBMeHm7ee+89z5iVK1ea6OjoGn8H7Iz7zTffNJLMJ5984hXTd7+vTfWcHz161CvmoqIi07ZtW68HfjXGOTfGmM2bN5tZs2aZDRs2GElm06ZNZx3flL7roSrkko1qtT2J9kxut9vEx8eb+fPne/pOnDhhYmJizLJly4wxp/8ShoeHm3Xr1nnGHDx40DRr1sy8+uqrAYl3z549RpLXF3/nzp1Gkvn444/rvJ9bb73VDBw40KuvX79+5oEHHghInL40NPYxY8aYW2+9tdb3g+m8//Of/zSSvP5HHujz/v3vf99MnDjRq++yyy4zDz30kM/xDz74oLnsssu8+u677z7Tu3dvz+u77rrLDBkyxGvM4MGDzd133x2gqE+rb+y+XH755WbOnDme13X9O+6P+sZdnWwcOXKk1n0GyznftGmTcTgc5ssvv/T0NcY5P1Ndko2m9F0PVSE3jVJfBQUFKikpUXp6uqfP6XSqX79+ys3NlSTl5eXp5MmTXmMSExPVrVs3zxh/7dy5UzExMerVq5enr3fv3oqJianzMb766iu98sorGjduXI331qxZo9jYWF1xxRWaOXOmjh07FpC4/Y1969atat++vbp06aIJEyZ4nosjBc95l6Ty8nI5HI4aU3eBOu+VlZXKy8vzOhfS6bv01hbnzp07a4wfPHiw3n33XZ08efKsYwJ1fhsa+5ncbreOHTumtm3bevUfP35cycnJuvjii3XLLbcoPz+/ScR9zTXXKCEhQTfeeKPefPNNr/eC5ZwvX75cN910k5KTk736rTznDdVUvuuhzNZHzAeD6ofP+Hq87v79+z1jIiIi1KZNmxpjantkb0PiaN++fY3+9u3b1/kYzz33nFq1aqU77rjDq/+ee+5RSkqK4uPj9eGHHyozM1P/+te/lJOTY2vsQ4cO1Z133qnk5GQVFBToV7/6lQYOHKi8vDw5nc6gOe8nTpzQQw89pJEjR3o9bTKQ592qx0bXNiZQ57ehsZ9p4cKF+uabb3TXXXd5+i677DKtWrVK3bt3V0VFhX7/+9/ruuuu07/+9S9dcskltsSdkJCgZ555RqmpqXK5XPp//+//6cYbb9TWrVt1ww03SKr9v0tTOufFxcX629/+prVr13r1W33OG6qpfNdD2XmRbAT6sfe+1PdxvHUdU9fYfcVQ12NUW7Fihe65554ajzSeMGGC58/dunXTJZdcop49e+q9995Tjx49bIt9xIgRXnH17NlTycnJeuWVV2okTPXZb2PEXu3kyZO6++675Xa7tWTJEq/3Gnrez8aKx0Y35LvfEA09zvPPP6+srCz95S9/8UoMe/fu7bWg+LrrrlOPHj30xBNP6A9/+IMtcV966aW69NJLPa/T0tJUVFSkBQsWeJKN+u7THw09zqpVq9S6dWvddtttXv2Ndc4boil910PReZFsBPKx92eKj4+XdDozrn4yreT9ON74+HhVVlbqyJEjXv/KLi0tVZ8+fQIS+wcffKCvvvqqxnv//ve/a30s8Hft2LFDn3zyidavX3/OsT169FB4eLg+++yzs/7oNVbs1RISEpScnKzPPvtMUtM/7ydPntRdd92lgoICvfHGG15VDV/qet59seqx0bWNqc9/Nytir7Z+/XqNGzdOL7zwgm666aazjm3WrJmuvfZaz/fHX/7E/V29e/fWn/70J8/rpn7OjTFasWKFRo0a5XkSd20Cfc4bqql810Na4y8TaRrqu0D0scce8/S5XC6fC0TXr1/vGXPo0CFLFir+4x//8PS98847dV6oOGbMmBpXQ9Rm9+7dRpLXg3n84W/s1crKyozT6TTPPfecMaZpn/fKykpz2223mSuuuMKUlpbW6Vj+nvfvf//75uc//7lXX9euXc+6QLRr165efRMnTqyxaG7o0KFeY4YMGWLJYsX6xG6MMWvXrjWRkZHnXBxYze12m549e5p7773Xn1C9NCTuM/3whz80AwYM8LxuyufcmP8tct29e/c5j2HFOT+T6rhAtKl810NVyCUb+/fvN/n5+WbOnDmmZcuWJj8/3+Tn55tjx455xlx66aVm48aNntfz5883MTExZuPGjWb37t3mxz/+sc9LXy+++GLz+uuvm/fee88MHDjQkkswr7zySrNz506zc+dO07179xqXYJ4ZuzHGlJeXmwsuuMDnU/o+//xzM2fOHLNr1y5TUFBgXnnlFXPZZZeZa665xtbYjx07ZmbMmGFyc3NNQUGBefPNN01aWpq56KKLmvx5P3nypBk+fLi5+OKLzfvvv+91CaDL5TLGWHPeqy9lXL58udmzZ4+ZNm2aadGihedqgYceesiMGjXKM776csDp06ebPXv2mOXLl9e4HPDtt982YWFhZv78+Wbv3r1m/vz5ll6GWdfY165da5o3b26eeuqpWi8dzsrKMq+++qr54osvTH5+vrn33ntN8+bNvRLHxo77d7/7ndm0aZP59NNPzYcffmgeeughI8ls2LDBM6apnvNqP/nJT0yvXr187rMxzrkxp///UP3/bklm0aJFJj8/33O1V1P+roeqkEs26vLYe0lm5cqVntdut9vMnj3bxMfHG6fTaW644YYaWf23335rpkyZYtq2bWuioqLMLbfcYgoLCwMa++HDh80999xjWrVqZVq1amXuueeeGpfQnRm7McY8/fTTJioqyuc9HAoLC80NN9xg2rZtayIiIkznzp3N1KlTa9zPorFj/89//mPS09PNhRdeaMLDw02HDh3MmDFjapzTpnjeCwoKfH7Hvvs9s+q8W/HY6BdeeMFceumlJjw83Fx22WVeP4yBVJ/Y+/Xr5/P8jhkzxjNm2rRppkOHDiYiIsJceOGFJj093eTm5toa92OPPWY6d+5sIiMjTZs2bcz1119vXnnllRr7bIrn3JjT1cSoqCjzzDPP+NxfY53z6upKbf/9m/p3PRTxiHkAAGAp7rMBAAAsRbIBAAAsRbIBAAAsRbIBAAAsRbIBAAAsRbIBAAAsRbIBAAAsRbIBAAgq27dv17Bhw5SYmCiHw6EXX3yx3vswxmjBggXq0qWLnE6nkpKSNG/evMAHC0nnyYPYAACh45tvvtFVV12le++9Vz/84Q8btI8HHnhAW7Zs0YIFC9S9e3eVl5errKwswJGiGncQBQAELYfDoU2bNnk97r6yslIPP/yw1qxZo6NHj6pbt2567LHH1L9/f0nS3r17deWVV+rDDz/UpZdeak/gIYZpFADAeeXee+/V22+/rXXr1umDDz7QnXfeqSFDhngedf/yyy+rU6dO+utf/6qUlBR17NhR48eP19dff21z5Ocvkg0AwHnjiy++0PPPP68XXnhBffv2VefOnTVz5kxdf/31WrlypSRp37592r9/v1544QWtXr1aq1atUl5enn70ox/ZHP35izUbAIDzxnvvvSdjjLp06eLV73K51K5dO0mS2+2Wy+XS6tWrPeOWL1+u1NRUffLJJ0ytWIBkAwBw3nC73QoLC1NeXp7CwsK83mvZsqUkKSEhQc2bN/dKSLp27SpJKiwsJNmwAMkGAOC8cc0116iqqkqlpaXq27evzzHXXXedTp06pS+++EKdO3eWJH366aeSpOTk5EaLNZRwNQoAIKgcP35cn3/+uaTTycWiRYs0YMAAtW3bVh06dNBPfvITvf3221q4cKGuueYalZWV6Y033lD37t31gx/8QG63W9dee61atmypxYsXy+12a/LkyYqOjtaWLVts/nTnJ5INAEBQ2bp1qwYMGFCjf8yYMVq1apVOnjypRx55RKtXr9bBgwfVrl07paWlac6cOerevbsk6dChQ7r//vu1ZcsWtWjRQkOHDtXChQvVtm3bxv44IYFkAwAAWIpLXwEAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKX+P5wZg0+tCUrrAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Velocities: (array([1.]), array([1.]))\n", + "U: \n", + "V: \n", + "temperature: \n" + ] + } + ], "source": [ - "fieldset_flat = parcels.FieldSet.from_data(data, dims, mesh=\"flat\")\n", + "ds = simple_UV_dataset(dims=(1, 1, nlat, nlon), mesh=\"flat\").isel(time=0, depth=0)\n", + "ds[\"temperature\"] = ds[\"U\"] + 20 # add temperature field of 20 deg\n", + "ds[\"U\"].data[:] = 1.0 # set U to 1 m/s\n", + "ds[\"V\"].data[:] = 1.0 # set V to 1 m/s\n", + "grid = parcels.XGrid.from_dataset(ds, mesh=\"flat\")\n", + "U = parcels.Field(\"U\", ds[\"U\"], grid, interp_method=parcels.interpolators.XLinear)\n", + "V = parcels.Field(\"V\", ds[\"V\"], grid, interp_method=parcels.interpolators.XLinear)\n", + "UV = parcels.VectorField(\"UV\", U, V)\n", + "temperature = parcels.Field(\"temperature\", ds[\"temperature\"], grid, interp_method=parcels.interpolators.XLinear)\n", + "fieldset_flat = parcels.FieldSet([U, V, UV, temperature])\n", "\n", "plt.pcolormesh(\n", - " fieldset_flat.U.lon,\n", - " fieldset_flat.U.lat,\n", - " fieldset_flat.U.data[0, :, :],\n", + " fieldset_flat.U.grid.lon,\n", + " fieldset_flat.U.grid.lat,\n", + " fieldset_flat.U.data[0, 0, :, :],\n", " vmin=0,\n", " vmax=1,\n", + " shading=\"nearest\"\n", ")\n", "plt.colorbar()\n", "plt.show()\n", "\n", - "print(\"Velocities:\", fieldset_flat.UV[0, 0, 40, -5])\n", - "for fld in [fieldset_flat.U, fieldset_flat.V, fieldset_flat.temp]:\n", + "print(\"Velocities:\", fieldset_flat.UV[np.array([0]), np.array([0]), np.array([40]), np.array([-5])])\n", + "for fld in [fieldset_flat.U, fieldset_flat.V, fieldset_flat.temperature]:\n", " print(f\"{fld.name}: {fld.units}\")" ] }, @@ -218,9 +472,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 52, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "ValueError", + "evalue": "Expected `data` to be a uxarray.UxDataArray or xarray.DataArray object, got .", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[52]\u001b[39m\u001b[32m, line 5\u001b[39m\n\u001b[32m 1\u001b[39m kh_zonal = \u001b[32m100\u001b[39m \u001b[38;5;66;03m# in m^2/s\u001b[39;00m\n\u001b[32m 2\u001b[39m kh_meridional = \u001b[32m100\u001b[39m \u001b[38;5;66;03m# in m^2/s\u001b[39;00m\n\u001b[32m 4\u001b[39m fieldset.add_field(\n\u001b[32m----> \u001b[39m\u001b[32m5\u001b[39m \u001b[43mparcels\u001b[49m\u001b[43m.\u001b[49m\u001b[43mField\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 6\u001b[39m \u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mKh_zonal\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 7\u001b[39m \u001b[43m \u001b[49m\u001b[43mkh_zonal\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[43m.\u001b[49m\u001b[43mones\u001b[49m\u001b[43m(\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnlat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnlon\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdtype\u001b[49m\u001b[43m=\u001b[49m\u001b[43mnp\u001b[49m\u001b[43m.\u001b[49m\u001b[43mfloat32\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 8\u001b[39m \u001b[43m \u001b[49m\u001b[43mgrid\u001b[49m\u001b[43m=\u001b[49m\u001b[43mfieldset\u001b[49m\u001b[43m.\u001b[49m\u001b[43mU\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgrid\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9\u001b[39m \u001b[43m \u001b[49m\u001b[43minterp_method\u001b[49m\u001b[43m=\u001b[49m\u001b[43mparcels\u001b[49m\u001b[43m.\u001b[49m\u001b[43minterpolators\u001b[49m\u001b[43m.\u001b[49m\u001b[43mXLinear\u001b[49m\n\u001b[32m 10\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 11\u001b[39m )\n\u001b[32m 12\u001b[39m fieldset.add_field(\n\u001b[32m 13\u001b[39m parcels.Field(\n\u001b[32m 14\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mKh_meridional\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 18\u001b[39m )\n\u001b[32m 19\u001b[39m )\n\u001b[32m 21\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m fld \u001b[38;5;129;01min\u001b[39;00m [fieldset.Kh_zonal, fieldset.Kh_meridional]:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Documents/UU/parcels/Parcels/src/parcels/_core/field.py:94\u001b[39m, in \u001b[36mField.__init__\u001b[39m\u001b[34m(self, name, data, grid, interp_method)\u001b[39m\n\u001b[32m 86\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__init__\u001b[39m(\n\u001b[32m 87\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m 88\u001b[39m name: \u001b[38;5;28mstr\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 91\u001b[39m interp_method: Callable,\n\u001b[32m 92\u001b[39m ):\n\u001b[32m 93\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(data, (ux.UxDataArray, xr.DataArray)):\n\u001b[32m---> \u001b[39m\u001b[32m94\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m 95\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mExpected `data` to be a uxarray.UxDataArray or xarray.DataArray object, got \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mtype\u001b[39m(data)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 96\u001b[39m )\n\u001b[32m 98\u001b[39m _assert_str_and_python_varname(name)\n\u001b[32m 100\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(grid, (UxGrid, XGrid)):\n", + "\u001b[31mValueError\u001b[39m: Expected `data` to be a uxarray.UxDataArray or xarray.DataArray object, got ." + ] + } + ], "source": [ "kh_zonal = 100 # in m^2/s\n", "kh_meridional = 100 # in m^2/s\n", @@ -228,20 +495,22 @@ "fieldset.add_field(\n", " parcels.Field(\n", " \"Kh_zonal\",\n", - " kh_zonal * np.ones((ydim, xdim), dtype=np.float32),\n", + " kh_zonal * np.ones((nlat, nlon), dtype=np.float32),\n", " grid=fieldset.U.grid,\n", + " interp_method=parcels.interpolators.XLinear\n", " )\n", ")\n", "fieldset.add_field(\n", " parcels.Field(\n", " \"Kh_meridional\",\n", - " kh_meridional * np.ones((ydim, xdim), dtype=np.float32),\n", + " kh_meridional * np.ones((nlat, nlon), dtype=np.float32),\n", " grid=fieldset.U.grid,\n", + " interp_method=parcels.interpolators.XLinear\n", " )\n", ")\n", "\n", "for fld in [fieldset.Kh_zonal, fieldset.Kh_meridional]:\n", - " print(f\"{fld.name}: {fld[0, 0, 40, -5]:e} {fld.units}\")" + " print(f\"{fld.name}: {fld[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]:e} {fld.units}\")" ] }, { @@ -330,7 +599,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "test-notebooks", "language": "python", "name": "python3" }, @@ -344,7 +613,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.11.0" } }, "nbformat": 4, From 0e4af1be6c0bae43392a32a0103de739e389af2c Mon Sep 17 00:00:00 2001 From: reint-fischer Date: Wed, 26 Nov 2025 10:43:16 +0100 Subject: [PATCH 2/7] rewrite unitconversion tutorial --- .../examples_v3/tutorial_unitconverters.ipynb | 443 +++++------------- 1 file changed, 124 insertions(+), 319 deletions(-) diff --git a/docs/user_guide/examples_v3/tutorial_unitconverters.ipynb b/docs/user_guide/examples_v3/tutorial_unitconverters.ipynb index 8aa5799a7e..e28d0b694a 100644 --- a/docs/user_guide/examples_v3/tutorial_unitconverters.ipynb +++ b/docs/user_guide/examples_v3/tutorial_unitconverters.ipynb @@ -15,7 +15,7 @@ "source": [ "In most applications, Parcels works with `spherical` meshes, where longitude and latitude are given in degrees, while depth is given in meters. But it is also possible to use `flat` meshes, where longitude and latitude are given in meters (note that the dimensions are then still called `longitude` and `latitude` for consistency reasons).\n", "\n", - "In all cases, velocities are given in m/s. So Parcels seamlessly converts between meters and degrees, under the hood. For transparency, this tutorial explain how this works.\n" + "In all cases, velocities are given in m/s. Parcels seamlessly converts between meters and degrees, under the hood. For transparency, this tutorial explains how this works.\n" ] }, { @@ -23,212 +23,37 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Let's first import the relevant modules, and create dictionaries for the `U`, `V` and `temp` data arrays, with the velocities 1 m/s and the temperature 20C.\n" + "Let's first import the relevant modules, and generate a simple dataset on a 2D spherical mesh, with `U`, `V` and `temperature` data arrays, with the velocities 1 m/s and the temperature 20C.\n" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "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 py_version = '3.8.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.8.2/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.8.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.8.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.8.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.8.0.min.js\", \"https://cdn.holoviz.org/panel/1.8.2/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));", - "application/vnd.holoviews_load.v0+json": "" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": "\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", - "application/vnd.holoviews_load.v0+json": "" - }, - "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": "9ce5ab11-2cdd-4d2b-b60b-c5402fb7c279" - } - }, - "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 py_version = '3.8.0'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = true;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.8.2/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 // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));", - "application/vnd.holoviews_load.v0+json": "" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": "\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", - "application/vnd.holoviews_load.v0+json": "" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/var/folders/fd/p7j_05zx409dzmtfjjjnjgfh0000gn/T/ipykernel_28627/1357681003.py:4: UserWarning: This is an alpha version of Parcels v4. The API is not stable and may change without deprecation warnings.\n", - " import parcels\n" - ] - } - ], + "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", + "import xarray as xr\n", "\n", "import parcels" ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from parcels._datasets.structured.generated import simple_UV_dataset\n", + "\n", "nlat = 10\n", "nlon = 18\n", "ds = simple_UV_dataset(dims=(1, 1, nlat, nlon), mesh=\"spherical\").isel(time=0, depth=0)\n", - "ds[\"temperature\"] = ds[\"U\"] + 20 # add temperature field of 20 deg\n", - "ds[\"U\"].data[:] = 1.0 # set U to 1 m/s\n", - "ds[\"V\"].data[:] = 1.0 # set V to 1 m/s\n", - "grid = parcels.XGrid.from_dataset(ds, mesh=\"spherical\")\n", - "U = parcels.Field(\"U\", ds[\"U\"], grid, interp_method=parcels.interpolators.XLinear)\n", - "V = parcels.Field(\"V\", ds[\"V\"], grid, interp_method=parcels.interpolators.XLinear)\n", - "UV = parcels.VectorField(\"UV\", U, V)\n", - "temperature = parcels.Field(\"temperature\", ds[\"temperature\"], grid, interp_method=parcels.interpolators.XLinear)\n", - "fieldset = parcels.FieldSet([U, V, UV, temperature])" + "ds[\"temperature\"] = ds[\"U\"] + 20 # add temperature field of 20 deg\n", + "ds[\"U\"].data[:] = 1.0 # set U to 1 m/s\n", + "ds[\"V\"].data[:] = 1.0 # set V to 1 m/s\n", + "ds" ] }, { @@ -236,30 +61,38 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We can convert these data and dims to a FieldSet object using `FieldSet.from_data`. We add the argument `mesh='spherical'` (this is the default option) to signal that all longitudes and latitudes are in degrees.\n", + "To create a `parcels.FieldSet` object, we define the `parcels.Field`s and the structured grid (`parcels.XGrid`) the fields are defined on. We add the argument `mesh='spherical'` to the `parcels.XGrid` to signal that all longitudes and latitudes are in degrees.\n", + "\n", + "```{note}\n", + "When using a `FieldSet` method for a specific dataset, such as `from_copernicusmarine()`, the grid information is known and parsed by **Parcels**, so we do not have to add the `mesh` argument.\n", + "```\n", "\n", "Plotting the `U` field indeed shows a uniform 1m/s eastward flow.\n" ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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CxYskGEYevDBBzV79my31wMEBQWpW7duSkxM9HmQAADgArhVumZVrwSIi4tTUlKSAgMDGyQoAACA2tTrCbsDBgxw/fnEiRM6deqU2/ehoaHeRQUAAOrGB8tGZqm81GvD7g8//KDU1FR16tRJbdq0UXh4uFsDAACNjLuNzu/xxx/XRx99pEWLFslut+ull17S7NmzFRMTo+XLl/s6RgAAAJd6LRu9++67Wr58uW688UY9+OCD6t+/vy6++GLFxsZqxYoV+vnPf+7rOAEAwPk0ow279aq8fP/994qLi5N0Zn/L999/L0m6/vrrtWHDBt9FBwAAPFJ1q7S3zQzqlbx0795d+/btkyRdfvnl+r//+z9JZyoyZ98+DQAA4Gv1Sl4eeOABffHFF5Kk9PR0196XKVOm6IknnvBpgAAAAGer156XKVOmuP48cOBAff3119q6das6duyoZcuW+Sw4AADgIfa81E3Xrl01YsQIhYaG6tVXX/XFKQEAQB2w5wUAAMBP1WvZCAAA+CGTVE68RfICAIAVNKM9L3VKXkaMGHHe748dO+ZNLAAAABdUp+TlQs9wCQsL0+jRo70KCAAA1J0vNtyaZcNunZIXboMGAMBPNaNlI+42AgAApsKGXQAALKA5LRtReQEAwAoMH7U6WrRokeLi4hQcHKyEhARt3LjRo+M++eQTtWzZUr17967zNUleAABAvaxatUqTJ0/WjBkzlJeXp/79+2vo0KHKz88/73ElJSUaPXq0br755npdl+QFAAAraILKy/z58zV27FiNGzdOl112mRYsWKAuXbpo8eLF5z3u4Ycf1qhRo5SYmFi3C/6I5AUAAAvw5buNSktL3ZrD4ah2vYqKCuXm5io5OdmtPzk5WZs3b641zmXLlunbb7/VrFmz6j1XkhcAAKzAh5WXLl26KCwszNWysrKqXe7w4cOqrKxUZGSkW39kZKSKiopqDHHXrl2aPn26VqxYoZYt63/PEHcbAQAANwUFBQoNDXV9ttvttY612Wxunw3DqNYnSZWVlRo1apRmz56tXr16eRUfyQsAAFbgw4fUhYaGuiUvNYmIiFBAQEC1KktxcXG1aowklZWVaevWrcrLy1Nqaqokyel0yjAMtWzZUmvXrtVNN93kUZgkLwAAWEBjP+clKChICQkJys7O1l133eXqz87O1h133FFtfGhoqLZt2+bWt2jRIn300Ud64403FBcX5/G1SV4AAEC9pKWl6f7771ffvn2VmJioF198Ufn5+ZowYYIkKT09XQcPHtTy5cvVokULxcfHux3fqVMnBQcHV+u/EMts2N23b5/Gjh2ruLg4hYSEqEePHpo1a5YqKircxtlstmptyZIlTRQ1AAA+0gS3So8cOVILFixQZmamevfurQ0bNmjNmjWKjY2VJBUWFl7wmS/1YZnKy9dffy2n06k//elPuvjii7V9+3aNHz9ex48f17PPPus2dtmyZRoyZIjr84Xelg0AgL9rqtcDTJw4URMnTqzxu1deeeW8x2ZkZCgjI6PO17RM8jJkyBC3hKR79+7697//rcWLF1dLXtq1a6eoqKjGDhEAAPiAZZaNalJSUqL27dtX609NTVVERISuvvpqLVmyRE6n87zncTgc1R7YAwCAX2midxs1BctUXs717bff6vnnn9e8efPc+n/3u9/p5ptvVkhIiD788ENNnTpVhw8f1syZM2s9V1ZWlmbPnt3QIQMAUH8+vFXa3/l95SUjI6PGTbZnt61bt7od891332nIkCG6++67NW7cOLfvZs6cqcTERPXu3VtTp05VZmamnnnmmfPGkJ6erpKSElcrKCjw+TwBAIBn/L7ykpqaqnvvvfe8Y7p16+b683fffaeBAwe6btm6kH79+qm0tFSHDh2q8aE60pknC57v6YIAADQ124/N23OYgd8nLxEREYqIiPBo7MGDBzVw4EAlJCRo2bJlatHiwoWlvLw8BQcHq127dl5GCgBAE2pGy0Z+n7x46rvvvtONN96orl276tlnn9V//vMf13dVdxa9++67KioqUmJiokJCQvTxxx9rxowZeuihh6isAABMralulW4Klkle1q5dq927d2v37t266KKL3L4zjDP/GoGBgVq0aJHS0tLkdDrVvXt3ZWZm6pFHHmmKkAEAQD1YJnkZM2aMxowZc94x5z4LBgAAy2DZCAAAmI5Jkg9v+f2t0gAAAGej8gIAgAWwYRcAAJhLM9rzwrIRAAAwFSovAABYAMtGAADAXFg2AgAA8E9UXgAAsACWjQAAgLk0o2UjkhcAAKygGSUv7HkBAACmQuUFAAALYM8LAAAwF5aNAAAA/BOVFwAALMBmGLIZ3pVOvD2+sZC8AABgBSwbAQAA+CcqLwAAWAB3GwEAAHNh2QgAAMA/UXkB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//xz5efnq7KyUp9//rk+//xzlZeXSzqzQejyyy/X/fffr7y8PH344YeaNm2axo8f78rKR40aJbvdrjFjxmj79u1avXq1nnzySaWlpfn18g8AAP4oLS1NL730kl5++WXt3LlTU6ZMUX5+viZMmCDpzLaL0aNHu8ZPmDBB+/fvV1pamnbu3KmXX35ZS5cu1bRp0+p2YcMkUlJSqt717dY+/vhj15j9+/cbw4YNM0JCQoz27dsbqampxsmTJ93O8+WXXxr9+/c37Ha7ERUVZWRkZBhOp7ORZ9M4Tp48acyaNava34FZMR//xnz8G/NBQ3nhhReM2NhYIygoyOjTp4+xfv1613cpKSnGgAED3MavW7fO+OlPf2oEBQUZ3bp1MxYvXlzna5ruOS8AAKB5M82yEQAAgETyAgAATIbkBQAAmArJCwAAMBWSFwuYO3eukpKS1KpVq1ofnmez2aq1c19Dvm3bNg0YMEAhISHq3LmzMjMzG+V9HOfyZD75+fkaPny4WrdurYiICE2aNKna0yX9ZT416datW7V/j3PfD+LJHP3FokWLFBcXp+DgYCUkJGjjxo1NHZJHMjIyqv07REVFub43DEMZGRmKiYlRSEiIbrzxRu3YsaMJI3a3YcMGDR8+XDExMbLZbHr77bfdvvckfofDoUcffVQRERFq3bq1br/9dh04cKARZ/FfF5rPmDFjqv179evXz22MP80HDYfkxQIqKip0991365e//OV5xy1btkyFhYWulpKS4vqutLRUgwYNUkxMjLZs2aLnn39ezz77rObPn9/Q4VdzoflUVlZq2LBhOn78uDZt2qSVK1fqzTff1NSpU11j/Gk+tcnMzHT795g5c6brO0/m6C9WrVqlyZMna8aMGcrLy1P//v01dOhQ5efnN3VoHvnJT37i9u+wbds213dPP/205s+fr4ULF2rLli2KiorSoEGDVFZW1oQR/9fx48d11VVXaeHChTV+70n8kydP1urVq7Vy5Upt2rRJ5eXluu2221RZWdlY03C50HwkaciQIW7/XmvWuL/Px5/mgwZU/zu74W+WLVtmhIWF1fidJGP16tW1Hrto0SIjLCzM7ZkJWVlZRkxMTJM9B6e2+axZs8Zo0aKFcfDgQVff66+/btjtdqOkpMQwDP+cz9liY2ON5557rtbvPZmjv7jmmmuMCRMmuPVdeumlxvTp05soIs/NmjXLuOqqq2r8zul0GlFRUcZTTz3l6jt58qQRFhZmLFmypJEi9Ny5/417Ev+xY8eMwMBAY+XKla4xBw8eNFq0aGG89957jRZ7TWr6/1kpKSnGHXfcUesx/jwf+BaVl2YkNTVVERERuvrqq7VkyRI5nU7Xdzk5ORowYIDbO5wGDx6s7777Tvv27WuCaGuXk5Oj+Ph4txedDR48WA6HQ7m5ua4x/j6f3//+9+rQoYN69+6tuXPnui0JeTJHf1BRUaHc3Nxqr7hPTk7W5s2bmyiqutm1a5diYmIUFxene++9V3v27JEk7d27V0VFRW5zs9vtGjBggCnm5kn8ubm5OnXqlNuYmJgYxcfH++0c161bp06dOqlXr14aP3686/10kjnng/pp0ncbofH87ne/080336yQkBB9+OGHmjp1qg4fPuxaqigqKqr2fqeqF2sVFRUpLi6usUOuVU2vVA8PD1dQUJDrhV/+Pp/HHntMffr0UXh4uP75z38qPT1de/fu1UsvveSK8UJz9AeHDx9WZWVlja+496c4a3Pttddq+fLl6tWrlw4dOqQ5c+YoKSlJO3bscMVf09z279/fFOHWiSfxFxUVKSgoSOHh4dXG+OO/39ChQ3X33XcrNjZWe/fu1W9+8xvddNNNys3Nld1uN918UH9UXvxUTRsJz21bt271+HwzZ85UYmKievfuralTpyozM1PPPPOM2xhfvKa8seZTU0zGOa9hb8j51KQuc5wyZYoGDBigK6+8UuPGjdOSJUu0dOlSHTlypNb4q+bgj+/hqunv2h/jPNfQoUP1s5/9TFdccYVuueUW/f3vf5ckvfrqq64xZp1blfrE769zHDlypIYNG6b4+HgNHz5c//jHP/TNN9+4/t1q46/zQf1RefFTqampuvfee887xps3Yffr10+lpaU6dOiQIiMja31NuVT9f7nVhy/nExUVpc8++8yt7+jRozp16pQr1oaeT028mWPVHRO7d+9Whw4dPJqjP4iIiFBAQECNf9f+FKenWrdurSuuuEK7du3SnXfeKelMdaLqTfWSeeZWddfU+eKPiopSRUWFjh496latKC4uVlJSUuMGXA/R0dGKjY3Vrl27JJl/PvAclRc/FRERoUsvvfS8LTg4uN7nz8vLU3BwsOtW5MTERG3YsMFt38XatWsVExPjVZJUxZfzSUxM1Pbt21VYWOgWq91uV0JCQqPMpybezDEvL0+SXD8ynszRHwQFBSkhIUHZ2dlu/dnZ2ab8sXA4HNq5c6eio6MVFxenqKgot7lVVFRo/fr1ppibJ/EnJCQoMDDQbUxhYaG2b99uijkeOXJEBQUFrv9uzD4f1EGTbRWGz+zfv9/Iy8szZs+ebbRp08bIy8sz8vLyjLKyMsMwDOOdd94xXnzxRWPbtm3G7t27jf/5n/8xQkNDjUmTJrnOcezYMSMyMtK47777jG3bthlvvfWWERoaajz77LN+N5/Tp08b8fHxxs0332z861//Mj744APjoosuMlJTU/1yPufavHmzMX/+fCMvL8/Ys2ePsWrVKiMmJsa4/fbbXWM8maO/WLlypREYGGgsXbrU+Oqrr4zJkycbrVu3Nvbt29fUoV3Q1KlTjXXr1hl79uwxPv30U+O2224z2rZt64r9qaeeMsLCwoy33nrL2LZtm3HfffcZ0dHRRmlpaRNHfkZZWZnrvw9Jrv+72r9/v2EYnsU/YcIE46KLLjI++OAD41//+pdx0003GVdddZVx+vRpv5pPWVmZMXXqVGPz5s3G3r17jY8//thITEw0Onfu7LfzQcMhebGAlJQUQ1K19vHHHxuGYRj/+Mc/jN69extt2rQxWrVqZcTHxxsLFiwwTp065XaeL7/80ujfv79ht9uNqKgoIyMjo0luK77QfAzjTIIzbNgwIyQkxGjfvr2Rmprqdlu0YfjPfM6Vm5trXHvttUZYWJgRHBxsXHLJJcasWbOM48ePu43zZI7+4oUXXjBiY2ONoKAgo0+fPsb69eubOiSPjBw50oiOjjYCAwONmJgYY8SIEcaOHTtc3zudTmPWrFlGVFSUYbfbjRtuuMHYtm1bE0bs7uOPP67xv5WUlBTDMDyL/8SJE0ZqaqrRvn17IyQkxLjtttuM/Pz8JpjN+efzww8/GMnJyUbHjh2NwMBAo2vXrkZKSkq1WP1pPmg4NsPwk0eOAgAAeIA9LwAAwFRIXgAAgKmQvAAAAFMheQEAAKZC8gIAAEyF5AUAAJgKyQsAADAVkhcAPrFv3z7ZbDZ9/vnnDXJ+m82mt99+u0HODcBcSF4AixgzZozrZYJNoUuXLiosLFR8fLwkad26dbLZbDp27FiTxQTAmnirNACfCAgIcL3JGAAaEpUXoBlYv369rrnmGtntdkVHR2v69Ok6ffq06/sbb7xRkyZN0hNPPKH27dsrKipKGRkZbuf4+uuvdf311ys4OFiXX365PvjgA7elnLOXjfbt26eBAwdKksLDw2Wz2TRmzBhJUrdu3bRgwQK3c/fu3dvtert27dINN9zguta5b62WpIMHD2rkyJEKDw9Xhw4ddMcdd2jfvn3e/lUBMAGSF8DiDh48qFtvvVVXX321vvjiCy1evFhLly7VnDlz3Ma9+uqrat26tT777DM9/fTTyszMdCUNTqdTd955p1q1aqXPPvtML774ombMmFHrNbt06aI333xTkvTvf/9bhYWF+sMf/uBRvE6nUyNGjFBAQIA+/fRTLVmyRL/61a/cxvzwww8aOHCg2rRpow0bNmjTpk1q06aNhgwZooqKirr89QAwIZaNAItbtGiRunTpooULF8pms+nSSy/Vd999p1/96lf67W9/qxYtzvxvmCuvvFKzZs2SJPXs2VMLFy7Uhx9+qEGDBmnt2rX69ttvtW7dOtfS0Ny5czVo0KAarxkQEKD27dtLkjp16qR27dp5HO8HH3ygnTt3at++fbroooskSU8++aSGDh3qGrNy5Uq1aNFCL730kmw2myRp2bJlateundatW6fk5OS6/SUBMBWSF8Didu7cqcTERNePvCRdd911Ki8v14EDB9S1a1dJZ5KXs0VHR6u4uFjSmepJly5d3Pa0XHPNNQ0Wb9euXV2JiyQlJia6jcnNzdXu3bvVtm1bt/6TJ0/q22+/bZC4APgPkhfA4gzDcEtcqvokufUHBga6jbHZbHI6nbWeo75atGjhun6VU6dOVYvt3FjO5nQ6lZCQoBUrVlQb27FjR5/ECcB/kbwAFnf55ZfrzTffdEtANm/erLZt26pz584enePSSy9Vfn6+Dh06pMjISEnSli1bzntMUFCQJKmystKtv2PHjiosLHR9Li0t1d69e93izc/P13fffaeYmBhJUk5Ojts5+vTpo1WrVqlTp04KDQ31aA4ArIMNu4CFlJSU6PPPP3drDz30kAoKCvToo4/q66+/1l//+lfNmjVLaWlprv0uFzJo0CD16NFDKSkp+vLLL/XJJ5+4NuzWVpGJjY2VzWbT3/72N/3nP/9ReXm5JOmmm27S//7v/2rjxo3avn27UlJSFBAQ4Drulltu0SWXXKLRo0friy++0MaNG6ttDv75z3+uiIgI3XHHHdq4caP27t2r9evX67HHHtOBAwfq81cHwERIXgALWbdunX7605+6tVmzZmnNmjX65z//qauuukoTJkzQ2LFjNXPmTI/PGxAQoLffflvl5eW6+uqrNW7cONfxwcHBNR7TuXNnzZ49W9OnT1dkZKRSU1MlSenp6brhhht022236dZbb9Wdd96pHj16uI5r0aKFVq9eLYfDoWuuuUbjxo3T3Llz3c7dqlUrbdiwQV27dtWIESN02WWX6cEHH9SJEyeoxADNgM2oaYEZAC7gk08+0fXXX6/du3e7JR8A0NBIXgB4ZPXq1WrTpo169uyp3bt367HHHlN4eLg2bdrU1KEBaGbYsAvAI2VlZXriiSdUUFCgiIgI3XLLLZo3b15ThwWgGaLyAgAATIUNuwAAwFRIXgAAgKmQvAAAAFMheQEAAKZC8gIAAEyF5AUAAJgKyQsAADAVkhcAAGAqJC8AAMBU/j8Nr9FxMw+j+AAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "# fieldset = parcels.FieldSet.from_data(data, dims, mesh=\"spherical\")\n", - "plt.pcolormesh(fieldset.U.grid.lon, fieldset.U.grid.lat, fieldset.U.data[0, 0, :, :], vmin=0, vmax=1, shading=\"nearest\")\n", + "grid = parcels.XGrid.from_dataset(ds, mesh=\"spherical\")\n", + "U = parcels.Field(\"U\", ds[\"U\"], grid, interp_method=parcels.interpolators.XLinear)\n", + "V = parcels.Field(\"V\", ds[\"V\"], grid, interp_method=parcels.interpolators.XLinear)\n", + "UV = parcels.VectorField(\"UV\", U, V)\n", + "temperature = parcels.Field(\n", + " \"temperature\", ds[\"temperature\"], grid, interp_method=parcels.interpolators.XLinear\n", + ")\n", + "fieldset = parcels.FieldSet([U, V, UV, temperature])\n", + "\n", + "plt.pcolormesh(\n", + " fieldset.U.grid.lon,\n", + " fieldset.U.grid.lat,\n", + " fieldset.U.data[0, 0, :, :],\n", + " vmin=0,\n", + " vmax=1,\n", + " shading=\"nearest\",\n", + ")\n", "plt.ylabel(\"Latitude\")\n", "plt.xlabel(\"Longitude\")\n", "plt.colorbar()\n", @@ -276,21 +109,14 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(array([1.17477258e-05]), array([8.99928006e-06]))\n", - "[20.]\n" - ] - } - ], + "outputs": [], "source": [ "print(fieldset.UV[np.array([0]), np.array([0]), np.array([40]), np.array([-5])])\n", - "print(fieldset.temperature[np.array([0]), np.array([0]), np.array([40]), np.array([-5])])" + "print(\n", + " fieldset.temperature[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]\n", + ")" ] }, { @@ -305,19 +131,9 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "U: \n", - "V: \n", - "temperature: \n" - ] - } - ], + "outputs": [], "source": [ "for fld in [fieldset.U, fieldset.V, fieldset.temperature]:\n", " print(f\"{fld.name}: {fld.units}\")" @@ -335,17 +151,9 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1.]\n" - ] - } - ], + "outputs": [], "source": [ "u, v = fieldset.UV[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]\n", "print(v * 1852 * 60)" @@ -361,19 +169,19 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(array([1.]), array([1.]))\n" - ] - } - ], + "outputs": [], "source": [ - "print(fieldset.UV.eval(np.array([0]), np.array([0]), np.array([40]), np.array([-5]), applyConversion=False))" + "print(\n", + " fieldset.UV.eval(\n", + " np.array([0]),\n", + " np.array([0]),\n", + " np.array([40]),\n", + " np.array([-5]),\n", + " applyConversion=False,\n", + " )\n", + ")" ] }, { @@ -389,45 +197,29 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If longitudes and latitudes are given in meters, rather than degrees, simply add `mesh='flat'` when creating the FieldSet object.\n" + "If longitudes and latitudes are given in meters, rather than degrees, simply add `mesh='flat'` when creating the XGrid object.\n" ] }, { "cell_type": "code", - "execution_count": 49, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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MTExiomJ0ahRo2o83O5MDofDZ3v88cc9Y/r371/j/XPduK0xYh87dmyNuHr37u01pime95MnT+r//u//1L17d7Vo0UKJiYkaPXq0Dh065DXOivNuxWOjN2zYoMsvv1xOp1OXX365Nm3a5FeMgYh948aNGjRokC688EJFR0crLS1Nr732mteYVatW+fzunzhxwra4t27d6jOmjz/+2GtcUzznvv4+OhwOXXHFFZ4xjXXOt2/frmHDhikxMVEOh0MvvvjiObdpSt/1kOT39TNB5te//rVZtGiRycjI8PlwOF/mz59vWrVqZTZs2GB2795tRowYYRISEkxFRYVnzMSJE81FF11kcnJyzHvvvWcGDBhgrrrqKnPq1KmAxT5kyBDTrVs3k5uba3Jzc023bt3MLbfcctZtiouLvdqKFSuMw+EwX3zxhWdMv379zIQJE7zGHT16NGBxNzT2MWPGmCFDhnjFdfjwYa8xTfG8Hz161Nx0001m/fr15uOPPzY7d+40vXr1MqmpqV7jAn3e161bZ8LDw80f//hHs2fPHvPAAw+YFi1amP379/scv2/fPnPBBReYBx54wOzZs8f88Y9/NOHh4ebPf/6zZ0xubq4JCwsz8+bNM3v37jXz5s0zzZs3N++8806D4wxE7A888IB57LHHzD//+U/z6aefmszMTBMeHm7ee+89z5iVK1ea6OjoGn8H7Iz7zTffNJLMJ5984hXTd7+vTfWcHz161CvmoqIi07ZtW68HfjXGOTfGmM2bN5tZs2aZDRs2GElm06ZNZx3flL7roSrkko1qtT2J9kxut9vEx8eb+fPne/pOnDhhYmJizLJly4wxp/8ShoeHm3Xr1nnGHDx40DRr1sy8+uqrAYl3z549RpLXF3/nzp1Gkvn444/rvJ9bb73VDBw40KuvX79+5oEHHghInL40NPYxY8aYW2+9tdb3g+m8//Of/zSSvP5HHujz/v3vf99MnDjRq++yyy4zDz30kM/xDz74oLnsssu8+u677z7Tu3dvz+u77rrLDBkyxGvM4MGDzd133x2gqE+rb+y+XH755WbOnDme13X9O+6P+sZdnWwcOXKk1n0GyznftGmTcTgc5ssvv/T0NcY5P1Ndko2m9F0PVSE3jVJfBQUFKikpUXp6uqfP6XSqX79+ys3NlSTl5eXp5MmTXmMSExPVrVs3zxh/7dy5UzExMerVq5enr3fv3oqJianzMb766iu98sorGjduXI331qxZo9jYWF1xxRWaOXOmjh07FpC4/Y1969atat++vbp06aIJEyZ4nosjBc95l6Ty8nI5HI4aU3eBOu+VlZXKy8vzOhfS6bv01hbnzp07a4wfPHiw3n33XZ08efKsYwJ1fhsa+5ncbreOHTumtm3bevUfP35cycnJuvjii3XLLbcoPz+/ScR9zTXXKCEhQTfeeKPefPNNr/eC5ZwvX75cN910k5KTk736rTznDdVUvuuhzNZHzAeD6ofP+Hq87v79+z1jIiIi1KZNmxpjantkb0PiaN++fY3+9u3b1/kYzz33nFq1aqU77rjDq/+ee+5RSkqK4uPj9eGHHyozM1P/+te/lJOTY2vsQ4cO1Z133qnk5GQVFBToV7/6lQYOHKi8vDw5nc6gOe8nTpzQQw89pJEjR3o9bTKQ592qx0bXNiZQ57ehsZ9p4cKF+uabb3TXXXd5+i677DKtWrVK3bt3V0VFhX7/+9/ruuuu07/+9S9dcskltsSdkJCgZ555RqmpqXK5XPp//+//6cYbb9TWrVt1ww03SKr9v0tTOufFxcX629/+prVr13r1W33OG6qpfNdD2XmRbAT6sfe+1PdxvHUdU9fYfcVQ12NUW7Fihe65554ajzSeMGGC58/dunXTJZdcop49e+q9995Tjx49bIt9xIgRXnH17NlTycnJeuWVV2okTPXZb2PEXu3kyZO6++675Xa7tWTJEq/3Gnrez8aKx0Y35LvfEA09zvPPP6+srCz95S9/8UoMe/fu7bWg+LrrrlOPHj30xBNP6A9/+IMtcV966aW69NJLPa/T0tJUVFSkBQsWeJKN+u7THw09zqpVq9S6dWvddtttXv2Ndc4boil910PReZFsBPKx92eKj4+XdDozrn4yreT9ON74+HhVVlbqyJEjXv/KLi0tVZ8+fQIS+wcffKCvvvqqxnv//ve/a30s8Hft2LFDn3zyidavX3/OsT169FB4eLg+++yzs/7oNVbs1RISEpScnKzPPvtMUtM/7ydPntRdd92lgoICvfHGG15VDV/qet59seqx0bWNqc9/Nytir7Z+/XqNGzdOL7zwgm666aazjm3WrJmuvfZaz/fHX/7E/V29e/fWn/70J8/rpn7OjTFasWKFRo0a5XkSd20Cfc4bqql810Na4y8TaRrqu0D0scce8/S5XC6fC0TXr1/vGXPo0CFLFir+4x//8PS98847dV6oOGbMmBpXQ9Rm9+7dRpLXg3n84W/s1crKyozT6TTPPfecMaZpn/fKykpz2223mSuuuMKUlpbW6Vj+nvfvf//75uc//7lXX9euXc+6QLRr165efRMnTqyxaG7o0KFeY4YMGWLJYsX6xG6MMWvXrjWRkZHnXBxYze12m549e5p7773Xn1C9NCTuM/3whz80AwYM8LxuyufcmP8tct29e/c5j2HFOT+T6rhAtKl810NVyCUb+/fvN/n5+WbOnDmmZcuWJj8/3+Tn55tjx455xlx66aVm48aNntfz5883MTExZuPGjWb37t3mxz/+sc9LXy+++GLz+uuvm/fee88MHDjQkkswr7zySrNz506zc+dO07179xqXYJ4ZuzHGlJeXmwsuuMDnU/o+//xzM2fOHLNr1y5TUFBgXnnlFXPZZZeZa665xtbYjx07ZmbMmGFyc3NNQUGBefPNN01aWpq56KKLmvx5P3nypBk+fLi5+OKLzfvvv+91CaDL5TLGWHPeqy9lXL58udmzZ4+ZNm2aadGihedqgYceesiMGjXKM776csDp06ebPXv2mOXLl9e4HPDtt982YWFhZv78+Wbv3r1m/vz5ll6GWdfY165da5o3b26eeuqpWi8dzsrKMq+++qr54osvTH5+vrn33ntN8+bNvRLHxo77d7/7ndm0aZP59NNPzYcffmgeeughI8ls2LDBM6apnvNqP/nJT0yvXr187rMxzrkxp///UP3/bklm0aJFJj8/33O1V1P+roeqkEs26vLYe0lm5cqVntdut9vMnj3bxMfHG6fTaW644YYaWf23335rpkyZYtq2bWuioqLMLbfcYgoLCwMa++HDh80999xjWrVqZVq1amXuueeeGpfQnRm7McY8/fTTJioqyuc9HAoLC80NN9xg2rZtayIiIkznzp3N1KlTa9zPorFj/89//mPS09PNhRdeaMLDw02HDh3MmDFjapzTpnjeCwoKfH7Hvvs9s+q8W/HY6BdeeMFceumlJjw83Fx22WVeP4yBVJ/Y+/Xr5/P8jhkzxjNm2rRppkOHDiYiIsJceOGFJj093eTm5toa92OPPWY6d+5sIiMjTZs2bcz1119vXnnllRr7bIrn3JjT1cSoqCjzzDPP+NxfY53z6upKbf/9m/p3PRTxiHkAAGAp7rMBAAAsRbIBAAAsRbIBAAAsRbIBAAAsRbIBAAAsRbIBAAAsRbIBAAAsRbIBAAgq27dv17Bhw5SYmCiHw6EXX3yx3vswxmjBggXq0qWLnE6nkpKSNG/evMAHC0nnyYPYAACh45tvvtFVV12le++9Vz/84Q8btI8HHnhAW7Zs0YIFC9S9e3eVl5errKwswJGiGncQBQAELYfDoU2bNnk97r6yslIPP/yw1qxZo6NHj6pbt2567LHH1L9/f0nS3r17deWVV+rDDz/UpZdeak/gIYZpFADAeeXee+/V22+/rXXr1umDDz7QnXfeqSFDhngedf/yyy+rU6dO+utf/6qUlBR17NhR48eP19dff21z5Ocvkg0AwHnjiy++0PPPP68XXnhBffv2VefOnTVz5kxdf/31WrlypSRp37592r9/v1544QWtXr1aq1atUl5enn70ox/ZHP35izUbAIDzxnvvvSdjjLp06eLV73K51K5dO0mS2+2Wy+XS6tWrPeOWL1+u1NRUffLJJ0ytWIBkAwBw3nC73QoLC1NeXp7CwsK83mvZsqUkKSEhQc2bN/dKSLp27SpJKiwsJNmwAMkGAOC8cc0116iqqkqlpaXq27evzzHXXXedTp06pS+++EKdO3eWJH366aeSpOTk5EaLNZRwNQoAIKgcP35cn3/+uaTTycWiRYs0YMAAtW3bVh06dNBPfvITvf3221q4cKGuueYalZWV6Y033lD37t31gx/8QG63W9dee61atmypxYsXy+12a/LkyYqOjtaWLVts/nTnJ5INAEBQ2bp1qwYMGFCjf8yYMVq1apVOnjypRx55RKtXr9bBgwfVrl07paWlac6cOerevbsk6dChQ7r//vu1ZcsWtWjRQkOHDtXChQvVtm3bxv44IYFkAwAAWIpLXwEAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKVINgAAgKX+P5wZg0+tCUrrAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Velocities: (array([1.]), array([1.]))\n", - "U: \n", - "V: \n", - "temperature: \n" - ] - } - ], + "outputs": [], "source": [ - "ds = simple_UV_dataset(dims=(1, 1, nlat, nlon), mesh=\"flat\").isel(time=0, depth=0)\n", - "ds[\"temperature\"] = ds[\"U\"] + 20 # add temperature field of 20 deg\n", - "ds[\"U\"].data[:] = 1.0 # set U to 1 m/s\n", - "ds[\"V\"].data[:] = 1.0 # set V to 1 m/s\n", - "grid = parcels.XGrid.from_dataset(ds, mesh=\"flat\")\n", - "U = parcels.Field(\"U\", ds[\"U\"], grid, interp_method=parcels.interpolators.XLinear)\n", - "V = parcels.Field(\"V\", ds[\"V\"], grid, interp_method=parcels.interpolators.XLinear)\n", + "ds_flat = simple_UV_dataset(dims=(1, 1, nlat, nlon), mesh=\"flat\").isel(time=0, depth=0)\n", + "ds_flat[\"temperature\"] = ds_flat[\"U\"] + 20 # add temperature field of 20 deg\n", + "ds_flat[\"U\"].data[:] = 1.0 # set U to 1 m/s\n", + "ds_flat[\"V\"].data[:] = 1.0 # set V to 1 m/s\n", + "grid = parcels.XGrid.from_dataset(ds_flat, mesh=\"flat\")\n", + "U = parcels.Field(\"U\", ds_flat[\"U\"], grid, interp_method=parcels.interpolators.XLinear)\n", + "V = parcels.Field(\"V\", ds_flat[\"V\"], grid, interp_method=parcels.interpolators.XLinear)\n", "UV = parcels.VectorField(\"UV\", U, V)\n", - "temperature = parcels.Field(\"temperature\", ds[\"temperature\"], grid, interp_method=parcels.interpolators.XLinear)\n", + "temperature = parcels.Field(\n", + " \"temperature\",\n", + " ds_flat[\"temperature\"],\n", + " grid,\n", + " interp_method=parcels.interpolators.XLinear,\n", + ")\n", "fieldset_flat = parcels.FieldSet([U, V, UV, temperature])\n", "\n", "plt.pcolormesh(\n", @@ -436,12 +228,15 @@ " fieldset_flat.U.data[0, 0, :, :],\n", " vmin=0,\n", " vmax=1,\n", - " shading=\"nearest\"\n", + " shading=\"nearest\",\n", ")\n", "plt.colorbar()\n", "plt.show()\n", "\n", - "print(\"Velocities:\", fieldset_flat.UV[np.array([0]), np.array([0]), np.array([40]), np.array([-5])])\n", + "print(\n", + " \"Velocities:\",\n", + " fieldset_flat.UV[np.array([0]), np.array([0]), np.array([40]), np.array([-5])],\n", + ")\n", "for fld in [fieldset_flat.U, fieldset_flat.V, fieldset_flat.temperature]:\n", " print(f\"{fld.name}: {fld.units}\")" ] @@ -472,45 +267,42 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "Expected `data` to be a uxarray.UxDataArray or xarray.DataArray object, got .", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[52]\u001b[39m\u001b[32m, line 5\u001b[39m\n\u001b[32m 1\u001b[39m kh_zonal = \u001b[32m100\u001b[39m \u001b[38;5;66;03m# in m^2/s\u001b[39;00m\n\u001b[32m 2\u001b[39m kh_meridional = \u001b[32m100\u001b[39m \u001b[38;5;66;03m# in m^2/s\u001b[39;00m\n\u001b[32m 4\u001b[39m fieldset.add_field(\n\u001b[32m----> \u001b[39m\u001b[32m5\u001b[39m \u001b[43mparcels\u001b[49m\u001b[43m.\u001b[49m\u001b[43mField\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 6\u001b[39m \u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mKh_zonal\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 7\u001b[39m \u001b[43m \u001b[49m\u001b[43mkh_zonal\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[43m.\u001b[49m\u001b[43mones\u001b[49m\u001b[43m(\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnlat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnlon\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdtype\u001b[49m\u001b[43m=\u001b[49m\u001b[43mnp\u001b[49m\u001b[43m.\u001b[49m\u001b[43mfloat32\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 8\u001b[39m \u001b[43m \u001b[49m\u001b[43mgrid\u001b[49m\u001b[43m=\u001b[49m\u001b[43mfieldset\u001b[49m\u001b[43m.\u001b[49m\u001b[43mU\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgrid\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9\u001b[39m \u001b[43m \u001b[49m\u001b[43minterp_method\u001b[49m\u001b[43m=\u001b[49m\u001b[43mparcels\u001b[49m\u001b[43m.\u001b[49m\u001b[43minterpolators\u001b[49m\u001b[43m.\u001b[49m\u001b[43mXLinear\u001b[49m\n\u001b[32m 10\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 11\u001b[39m )\n\u001b[32m 12\u001b[39m fieldset.add_field(\n\u001b[32m 13\u001b[39m parcels.Field(\n\u001b[32m 14\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mKh_meridional\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 18\u001b[39m )\n\u001b[32m 19\u001b[39m )\n\u001b[32m 21\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m fld \u001b[38;5;129;01min\u001b[39;00m [fieldset.Kh_zonal, fieldset.Kh_meridional]:\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Documents/UU/parcels/Parcels/src/parcels/_core/field.py:94\u001b[39m, in \u001b[36mField.__init__\u001b[39m\u001b[34m(self, name, data, grid, interp_method)\u001b[39m\n\u001b[32m 86\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__init__\u001b[39m(\n\u001b[32m 87\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m 88\u001b[39m name: \u001b[38;5;28mstr\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 91\u001b[39m interp_method: Callable,\n\u001b[32m 92\u001b[39m ):\n\u001b[32m 93\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(data, (ux.UxDataArray, xr.DataArray)):\n\u001b[32m---> \u001b[39m\u001b[32m94\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m 95\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mExpected `data` to be a uxarray.UxDataArray or xarray.DataArray object, got \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mtype\u001b[39m(data)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 96\u001b[39m )\n\u001b[32m 98\u001b[39m _assert_str_and_python_varname(name)\n\u001b[32m 100\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(grid, (UxGrid, XGrid)):\n", - "\u001b[31mValueError\u001b[39m: Expected `data` to be a uxarray.UxDataArray or xarray.DataArray object, got ." - ] - } - ], + "outputs": [], "source": [ "kh_zonal = 100 # in m^2/s\n", "kh_meridional = 100 # in m^2/s\n", "\n", - "fieldset.add_field(\n", - " parcels.Field(\n", - " \"Kh_zonal\",\n", - " kh_zonal * np.ones((nlat, nlon), dtype=np.float32),\n", - " grid=fieldset.U.grid,\n", - " interp_method=parcels.interpolators.XLinear\n", - " )\n", + "ds[\"Kh_zonal\"] = xr.DataArray(\n", + " data=kh_zonal * np.ones((nlat, nlon), dtype=np.float32), dims=[\"YG\", \"XG\"]\n", ")\n", - "fieldset.add_field(\n", - " parcels.Field(\n", - " \"Kh_meridional\",\n", - " kh_meridional * np.ones((nlat, nlon), dtype=np.float32),\n", - " grid=fieldset.U.grid,\n", - " interp_method=parcels.interpolators.XLinear\n", - " )\n", + "\n", + "kh_zonal_field = parcels.Field(\n", + " \"Kh_zonal\",\n", + " ds[\"Kh_zonal\"],\n", + " grid=fieldset.U.grid,\n", + " interp_method=parcels.interpolators.XLinear,\n", + ")\n", + "\n", + "fieldset.add_field(kh_zonal_field)\n", + "\n", + "ds[\"Kh_meridional\"] = xr.DataArray(\n", + " data=kh_meridional * np.ones((nlat, nlon), dtype=np.float32), dims=[\"YG\", \"XG\"]\n", ")\n", "\n", + "kh_meridional_field = parcels.Field(\n", + " \"Kh_meridional\",\n", + " ds[\"Kh_meridional\"],\n", + " grid=grid,\n", + " interp_method=parcels.interpolators.XLinear,\n", + ")\n", + "\n", + "fieldset.add_field(kh_meridional_field)\n", + "\n", "for fld in [fieldset.Kh_zonal, fieldset.Kh_meridional]:\n", - " print(f\"{fld.name}: {fld[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]:e} {fld.units}\")" + " val = fld[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]\n", + " print(f\"{fld.name}: {val} {fld.units}\")" ] }, { @@ -530,7 +322,10 @@ "outputs": [], "source": [ "deg_to_m = 1852 * 60\n", - "print(fieldset.Kh_meridional[0, 0, 40, -5] * deg_to_m**2)" + "print(\n", + " fieldset.Kh_meridional[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]\n", + " * deg_to_m**2\n", + ")" ] }, { @@ -567,12 +362,19 @@ "metadata": {}, "outputs": [], "source": [ + "ds[\"Ustokes\"] = xr.DataArray(\n", + " data=np.ones((nlat, nlon), dtype=np.float32), dims=[\"YG\", \"XG\"]\n", + ")\n", + "\n", "fieldset.add_field(\n", " parcels.Field(\n", - " \"Ustokes\", np.ones((ydim, xdim), dtype=np.float32), grid=fieldset.U.grid\n", + " \"Ustokes\",\n", + " ds[\"Ustokes\"],\n", + " grid=fieldset.U.grid,\n", + " interp_method=parcels.interpolators.XLinear,\n", " )\n", ")\n", - "print(fieldset.Ustokes[0, 0, 40, -5])" + "print(fieldset.Ustokes[np.array([0]), np.array([0]), np.array([40]), np.array([-5])])" ] }, { @@ -589,11 +391,14 @@ "metadata": {}, "outputs": [], "source": [ - "from parcels.tools.converters import GeographicPolar\n", - "\n", - "fieldset.Ustokes.units = GeographicPolar()\n", - "print(fieldset.Ustokes[0, 0, 40, -5])\n", - "print(fieldset.Ustokes[0, 0, 40, -5] * 1852 * 60 * np.cos(40 * np.pi / 180))" + "fieldset.Ustokes.units = parcels.GeographicPolar()\n", + "print(fieldset.Ustokes[np.array([0]), np.array([0]), np.array([40]), np.array([-5])])\n", + "print(\n", + " fieldset.Ustokes[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]\n", + " * 1852\n", + " * 60\n", + " * np.cos(40 * np.pi / 180)\n", + ")" ] } ], From 7419f14d8de54b8baa1fad240e40398075fe6fb5 Mon Sep 17 00:00:00 2001 From: reint-fischer Date: Wed, 26 Nov 2025 10:53:53 +0100 Subject: [PATCH 3/7] add conversion guide to index --- .../{examples_v3 => examples}/tutorial_unitconverters.ipynb | 0 docs/user_guide/index.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename docs/user_guide/{examples_v3 => examples}/tutorial_unitconverters.ipynb (100%) diff --git a/docs/user_guide/examples_v3/tutorial_unitconverters.ipynb b/docs/user_guide/examples/tutorial_unitconverters.ipynb similarity index 100% rename from docs/user_guide/examples_v3/tutorial_unitconverters.ipynb rename to docs/user_guide/examples/tutorial_unitconverters.ipynb diff --git a/docs/user_guide/index.md b/docs/user_guide/index.md index eebe94cc1e..1c456403cb 100644 --- a/docs/user_guide/index.md +++ b/docs/user_guide/index.md @@ -24,6 +24,7 @@ The tutorials written for Parcels v3 are currently being updated for Parcels v4. :titlesonly: examples/tutorial_nemo_curvilinear.ipynb +examples/tutorial_unitconverters.ipynb ``` @@ -32,7 +33,6 @@ examples/tutorial_nemo_curvilinear.ipynb - ```{toctree} :caption: Create ParticleSets From 93cd2fc6df6347e9be3149b0bdf48e10b80662e3 Mon Sep 17 00:00:00 2001 From: reint-fischer Date: Wed, 26 Nov 2025 11:53:34 +0100 Subject: [PATCH 4/7] add math support for myst-nb --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 88584cc8dc..7fd13ba1f4 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -521,7 +521,7 @@ def linkcode_resolve(domain, info): # -- Options for MyST parser ---------------------------------------------- myst_heading_anchors = 3 -myst_enable_extensions = ["substitution"] +myst_enable_extensions = ["substitution", "amsmath", "dollarmath"] # -- Options for MyST-nb -------------------------------------------------- nb_execution_mode = "cache" From d367e4f33e2093ab3e3c08a5649245f5ea9084e8 Mon Sep 17 00:00:00 2001 From: reint-fischer Date: Thu, 27 Nov 2025 08:50:17 +0100 Subject: [PATCH 5/7] add suggestions from review --- .../examples/tutorial_unitconverters.ipynb | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/user_guide/examples/tutorial_unitconverters.ipynb b/docs/user_guide/examples/tutorial_unitconverters.ipynb index e28d0b694a..d8cc68f785 100644 --- a/docs/user_guide/examples/tutorial_unitconverters.ipynb +++ b/docs/user_guide/examples/tutorial_unitconverters.ipynb @@ -5,7 +5,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Spherical grids and UnitConversion\n" + "# Spherical grids and unit conversion\n" ] }, { @@ -15,7 +15,7 @@ "source": [ "In most applications, Parcels works with `spherical` meshes, where longitude and latitude are given in degrees, while depth is given in meters. But it is also possible to use `flat` meshes, where longitude and latitude are given in meters (note that the dimensions are then still called `longitude` and `latitude` for consistency reasons).\n", "\n", - "In all cases, velocities are given in m/s. Parcels seamlessly converts between meters and degrees, under the hood. For transparency, this tutorial explains how this works.\n" + "In all cases, velocities are given in m/s. Parcels seamlessly converts between meters and degrees, under the hood. For transparency, this guide explains how this works.\n" ] }, { @@ -64,7 +64,7 @@ "To create a `parcels.FieldSet` object, we define the `parcels.Field`s and the structured grid (`parcels.XGrid`) the fields are defined on. We add the argument `mesh='spherical'` to the `parcels.XGrid` to signal that all longitudes and latitudes are in degrees.\n", "\n", "```{note}\n", - "When using a `FieldSet` method for a specific dataset, such as `from_copernicusmarine()`, the grid information is known and parsed by **Parcels**, so we do not have to add the `mesh` argument.\n", + "When using a `FieldSet` method for a specific dataset, such as `from_copernicusmarine()`, the grid information is known and parsed by Parcels, so we do not have to add the `mesh` argument.\n", "```\n", "\n", "Plotting the `U` field indeed shows a uniform 1m/s eastward flow.\n" @@ -91,7 +91,7 @@ " fieldset.U.data[0, 0, :, :],\n", " vmin=0,\n", " vmax=1,\n", - " shading=\"nearest\",\n", + " shading=\"gouraud\",\n", ")\n", "plt.ylabel(\"Latitude\")\n", "plt.xlabel(\"Longitude\")\n", @@ -228,7 +228,7 @@ " fieldset_flat.U.data[0, 0, :, :],\n", " vmin=0,\n", " vmax=1,\n", - " shading=\"nearest\",\n", + " shading=\"gouraud\",\n", ")\n", "plt.colorbar()\n", "plt.show()\n", @@ -345,11 +345,11 @@ "\n", "| Field name | Converter object | Conversion for `mesh='spherical'` | Conversion for `mesh='flat'` |\n", "| ---------------- | ----------------------- | --------------------------------------------------------- | ---------------------------- |\n", - "| 'U' | `GeographicPolar` | $1852 \\cdot 60 \\cdot \\cos(lat \\cdot \\frac{\\pi}{180})$ | 1 |\n", - "| 'V' | `Geographic` | $1852 \\cdot 60$ | 1 |\n", - "| 'Kh_zonal' | `GeographicPolarSquare` | $(1852 \\cdot 60 \\cdot \\cos(lat \\cdot \\frac{\\pi}{180}))^2$ | 1 |\n", - "| 'Kh_meridional' | `GeographicSquare` | $(1852 \\cdot 60)^2$ | 1 |\n", - "| All other fields | `UnitConverter` | 1 | 1 |\n", + "| `\"U\"` | `GeographicPolar` | $1852 \\cdot 60 \\cdot \\cos(lat \\cdot \\frac{\\pi}{180})$ | 1 |\n", + "| `\"V\"` | `Geographic` | $1852 \\cdot 60$ | 1 |\n", + "| `\"Kh_zonal\"` | `GeographicPolarSquare` | $(1852 \\cdot 60 \\cdot \\cos(lat \\cdot \\frac{\\pi}{180}))^2$ | 1 |\n", + "| `\"Kh_meridional\"` | `GeographicSquare` | $(1852 \\cdot 60)^2$ | 1 |\n", + "| All other fields | `UnitConverter` | 1 | 1 |\n", "\n", "Only four Field names are recognised and assigned an automatic UnitConverter object. This means that things might go very wrong when e.g. a velocity field is not called `U` or `V`.\n", "\n", From 34905ee868d4010d0f0ee0bff1d8bdd299e5abd0 Mon Sep 17 00:00:00 2001 From: reint-fischer Date: Thu, 27 Nov 2025 10:34:18 +0100 Subject: [PATCH 6/7] fix wording --- .../examples/tutorial_unitconverters.ipynb | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/docs/user_guide/examples/tutorial_unitconverters.ipynb b/docs/user_guide/examples/tutorial_unitconverters.ipynb index d8cc68f785..8e26db5f97 100644 --- a/docs/user_guide/examples/tutorial_unitconverters.ipynb +++ b/docs/user_guide/examples/tutorial_unitconverters.ipynb @@ -5,7 +5,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Spherical grids and unit conversion\n" + "# Spherical grids and unit converters\n" ] }, { @@ -67,7 +67,7 @@ "When using a `FieldSet` method for a specific dataset, such as `from_copernicusmarine()`, the grid information is known and parsed by Parcels, so we do not have to add the `mesh` argument.\n", "```\n", "\n", - "Plotting the `U` field indeed shows a uniform 1m/s eastward flow.\n" + "Plotting the `U` field indeed shows a uniform 1 m/s eastward flow.\n" ] }, { @@ -113,9 +113,13 @@ "metadata": {}, "outputs": [], "source": [ - "print(fieldset.UV[np.array([0]), np.array([0]), np.array([40]), np.array([-5])])\n", + "time = np.array([0])\n", + "z = np.array([0])\n", + "lat = np.array([40])\n", + "lon = np.array([-5])\n", + "print(fieldset.UV[time, z, lat, lon])\n", "print(\n", - " fieldset.temperature[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]\n", + " fieldset.temperature[time, z, lat, lon]\n", ")" ] }, @@ -126,7 +130,7 @@ "source": [ "While the temperature field indeed is 20C, as we defined, these printed velocities are much smaller.\n", "\n", - "This is because Parcels converts under the hood from m/s to degrees/s. This conversion is done with a `UnitConverter` object, which is stored in the `.units` attribute of each Field. Below, we print these\n" + "This is because Parcels converts under the hood from m/s to degrees/s. This conversion is done with a `parcels.converters` object, which is stored in the `.units` attribute of each Field. Below, we print these\n" ] }, { @@ -144,7 +148,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "So the U field has a `GeographicPolar` UnitConverter object, the V field has a `Geographic` Unitconverter and the `temp` field has a `UnitConverter` object.\n", + "So the U field has a `GeographicPolar` UnitConverter object, the V field has a `Geographic` UnitConverter and the `temp` field has a `UnitConverter` object.\n", "\n", "Indeed, if we multiply the value of the V field with 1852 \\* 60 (the number of meters in 1 degree of latitude), we get the expected 1 m/s.\n" ] @@ -155,7 +159,7 @@ "metadata": {}, "outputs": [], "source": [ - "u, v = fieldset.UV[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]\n", + "u, v = fieldset.UV[time, z, lat, lon]\n", "print(v * 1852 * 60)" ] }, @@ -175,10 +179,10 @@ "source": [ "print(\n", " fieldset.UV.eval(\n", - " np.array([0]),\n", - " np.array([0]),\n", - " np.array([40]),\n", - " np.array([-5]),\n", + " time,\n", + " z,\n", + " lat,\n", + " lon,\n", " applyConversion=False,\n", " )\n", ")" @@ -235,7 +239,7 @@ "\n", "print(\n", " \"Velocities:\",\n", - " fieldset_flat.UV[np.array([0]), np.array([0]), np.array([40]), np.array([-5])],\n", + " fieldset_flat.UV[time, z, lat, lon],\n", ")\n", "for fld in [fieldset_flat.U, fieldset_flat.V, fieldset_flat.temperature]:\n", " print(f\"{fld.name}: {fld.units}\")" @@ -301,7 +305,7 @@ "fieldset.add_field(kh_meridional_field)\n", "\n", "for fld in [fieldset.Kh_zonal, fieldset.Kh_meridional]:\n", - " val = fld[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]\n", + " val = fld[time, z, lat, lon]\n", " print(f\"{fld.name}: {val} {fld.units}\")" ] }, @@ -323,7 +327,7 @@ "source": [ "deg_to_m = 1852 * 60\n", "print(\n", - " fieldset.Kh_meridional[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]\n", + " fieldset.Kh_meridional[time, z, lat, lon]\n", " * deg_to_m**2\n", ")" ] @@ -374,7 +378,7 @@ " interp_method=parcels.interpolators.XLinear,\n", " )\n", ")\n", - "print(fieldset.Ustokes[np.array([0]), np.array([0]), np.array([40]), np.array([-5])])" + "print(fieldset.Ustokes[time, z, lat, lon])" ] }, { @@ -392,9 +396,9 @@ "outputs": [], "source": [ "fieldset.Ustokes.units = parcels.GeographicPolar()\n", - "print(fieldset.Ustokes[np.array([0]), np.array([0]), np.array([40]), np.array([-5])])\n", + "print(fieldset.Ustokes[time, z, lat, lon])\n", "print(\n", - " fieldset.Ustokes[np.array([0]), np.array([0]), np.array([40]), np.array([-5])]\n", + " fieldset.Ustokes[time, z, lat, lon]\n", " * 1852\n", " * 60\n", " * np.cos(40 * np.pi / 180)\n", From 7fc30af247d12f26013b8f8e068a84a2dca5ec00 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 09:34:43 +0000 Subject: [PATCH 7/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../examples/tutorial_unitconverters.ipynb | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/docs/user_guide/examples/tutorial_unitconverters.ipynb b/docs/user_guide/examples/tutorial_unitconverters.ipynb index 8e26db5f97..e9847781fa 100644 --- a/docs/user_guide/examples/tutorial_unitconverters.ipynb +++ b/docs/user_guide/examples/tutorial_unitconverters.ipynb @@ -118,9 +118,7 @@ "lat = np.array([40])\n", "lon = np.array([-5])\n", "print(fieldset.UV[time, z, lat, lon])\n", - "print(\n", - " fieldset.temperature[time, z, lat, lon]\n", - ")" + "print(fieldset.temperature[time, z, lat, lon])" ] }, { @@ -326,10 +324,7 @@ "outputs": [], "source": [ "deg_to_m = 1852 * 60\n", - "print(\n", - " fieldset.Kh_meridional[time, z, lat, lon]\n", - " * deg_to_m**2\n", - ")" + "print(fieldset.Kh_meridional[time, z, lat, lon] * deg_to_m**2)" ] }, { @@ -397,12 +392,7 @@ "source": [ "fieldset.Ustokes.units = parcels.GeographicPolar()\n", "print(fieldset.Ustokes[time, z, lat, lon])\n", - "print(\n", - " fieldset.Ustokes[time, z, lat, lon]\n", - " * 1852\n", - " * 60\n", - " * np.cos(40 * np.pi / 180)\n", - ")" + "print(fieldset.Ustokes[time, z, lat, lon] * 1852 * 60 * np.cos(40 * np.pi / 180))" ] } ],