diff --git a/examples/model_architecture.ipynb b/examples/model_architecture.ipynb index c554f76..98e4002 100644 --- a/examples/model_architecture.ipynb +++ b/examples/model_architecture.ipynb @@ -49,532 +49,8 @@ "outputs": [ { "data": { - "application/javascript": [ - "(function(root) {\n", - " function now() {\n", - " return new Date();\n", - " }\n", - "\n", - " var force = true;\n", - " var py_version = '3.3.1'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", - " var is_dev = py_version.indexOf(\"+\") !== -1 || py_version.indexOf(\"-\") !== -1;\n", - " var reloading = false;\n", - " var Bokeh = root.Bokeh;\n", - " var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n", - "\n", - " if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n", - " root._bokeh_timeout = Date.now() + 5000;\n", - " root._bokeh_failed_load = false;\n", - " }\n", - "\n", - " function run_callbacks() {\n", - " try {\n", - " root._bokeh_onload_callbacks.forEach(function(callback) {\n", - " if (callback != null)\n", - " callback();\n", - " });\n", - " } finally {\n", - " delete root._bokeh_onload_callbacks;\n", - " }\n", - " console.debug(\"Bokeh: all callbacks have finished\");\n", - " }\n", - "\n", - " function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n", - " if (css_urls == null) css_urls = [];\n", - " if (js_urls == null) js_urls = [];\n", - " if (js_modules == null) js_modules = [];\n", - " if (js_exports == null) js_exports = {};\n", - "\n", - " root._bokeh_onload_callbacks.push(callback);\n", - "\n", - " if (root._bokeh_is_loading > 0) {\n", - " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", - " return null;\n", - " }\n", - " if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n", - " run_callbacks();\n", - " return null;\n", - " }\n", - " if (!reloading) {\n", - " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", - " }\n", - "\n", - " function on_load() {\n", - " root._bokeh_is_loading--;\n", - " if (root._bokeh_is_loading === 0) {\n", - " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", - " run_callbacks()\n", - " }\n", - " }\n", - " window._bokeh_on_load = on_load\n", - "\n", - " function on_error() {\n", - " console.error(\"failed to load \" + url);\n", - " }\n", - "\n", - " var skip = [];\n", - " if (window.requirejs) {\n", - " window.requirejs.config({'packages': {}, 'paths': {'jspanel': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel', 'jspanel-modal': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal', 'jspanel-tooltip': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip', 'jspanel-hint': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint', 'jspanel-layout': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout', 'jspanel-contextmenu': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu', 'jspanel-dock': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock', 'gridstack': 'https://cdn.jsdelivr.net/npm/gridstack@7.2.3/dist/gridstack-all', 'notyf': 'https://cdn.jsdelivr.net/npm/notyf@3/notyf.min'}, 'shim': {'jspanel': {'exports': 'jsPanel'}, 'gridstack': {'exports': 'GridStack'}}});\n", - " require([\"jspanel\"], function(jsPanel) {\n", - "\twindow.jsPanel = jsPanel\n", - "\ton_load()\n", - " })\n", - " require([\"jspanel-modal\"], function() {\n", - "\ton_load()\n", - " })\n", - " require([\"jspanel-tooltip\"], function() {\n", - "\ton_load()\n", - " })\n", - " require([\"jspanel-hint\"], function() {\n", - "\ton_load()\n", - " })\n", - " require([\"jspanel-layout\"], function() {\n", - "\ton_load()\n", - " })\n", - " require([\"jspanel-contextmenu\"], function() {\n", - "\ton_load()\n", - " })\n", - " require([\"jspanel-dock\"], function() {\n", - "\ton_load()\n", - " })\n", - " require([\"gridstack\"], function(GridStack) {\n", - "\twindow.GridStack = GridStack\n", - "\ton_load()\n", - " })\n", - " require([\"notyf\"], function() {\n", - "\ton_load()\n", - " })\n", - " root._bokeh_is_loading = css_urls.length + 9;\n", - " } else {\n", - " root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n", - " }\n", - "\n", - " var existing_stylesheets = []\n", - " var links = document.getElementsByTagName('link')\n", - " for (var i = 0; i < links.length; i++) {\n", - " var link = links[i]\n", - " if (link.href != null) {\n", - "\texisting_stylesheets.push(link.href)\n", - " }\n", - " }\n", - " for (var i = 0; i < css_urls.length; i++) {\n", - " var url = css_urls[i];\n", - " if (existing_stylesheets.indexOf(url) !== -1) {\n", - "\ton_load()\n", - "\tcontinue;\n", - " }\n", - " const element = document.createElement(\"link\");\n", - " element.onload = on_load;\n", - " element.onerror = on_error;\n", - " element.rel = \"stylesheet\";\n", - " element.type = \"text/css\";\n", - " element.href = url;\n", - " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", - " document.body.appendChild(element);\n", - " } if (((window['jsPanel'] !== undefined) && (!(window['jsPanel'] instanceof HTMLElement))) || window.requirejs) {\n", - " var urls = ['https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/jspanel.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock.js'];\n", - " for (var i = 0; i < urls.length; i++) {\n", - " skip.push(urls[i])\n", - " }\n", - " } if (((window['GridStack'] !== undefined) && (!(window['GridStack'] instanceof HTMLElement))) || window.requirejs) {\n", - " var urls = ['https://cdn.holoviz.org/panel/1.3.1/dist/bundled/gridstack/gridstack@7.2.3/dist/gridstack-all.js'];\n", - " for (var i = 0; i < urls.length; i++) {\n", - " skip.push(urls[i])\n", - " }\n", - " } if (((window['Notyf'] !== undefined) && (!(window['Notyf'] instanceof HTMLElement))) || window.requirejs) {\n", - " var urls = ['https://cdn.holoviz.org/panel/1.3.1/dist/bundled/notificationarea/notyf@3/notyf.min.js'];\n", - " for (var i = 0; i < urls.length; i++) {\n", - " skip.push(urls[i])\n", - " }\n", - " } var existing_scripts = []\n", - " var scripts = document.getElementsByTagName('script')\n", - " for (var i = 0; i < scripts.length; i++) {\n", - " var script = scripts[i]\n", - " if (script.src != null) {\n", - "\texisting_scripts.push(script.src)\n", - " }\n", - " }\n", - " for (var i = 0; i < js_urls.length; i++) {\n", - " var url = js_urls[i];\n", - " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", - "\tif (!window.requirejs) {\n", - "\t on_load();\n", - "\t}\n", - "\tcontinue;\n", - " }\n", - " var element = document.createElement('script');\n", - " element.onload = on_load;\n", - " element.onerror = on_error;\n", - " element.async = false;\n", - " element.src = url;\n", - " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", - " document.head.appendChild(element);\n", - " }\n", - " for (var i = 0; i < js_modules.length; i++) {\n", - " var url = js_modules[i];\n", - " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", - "\tif (!window.requirejs) {\n", - "\t on_load();\n", - "\t}\n", - "\tcontinue;\n", - " }\n", - " var element = document.createElement('script');\n", - " element.onload = on_load;\n", - " element.onerror = on_error;\n", - " element.async = false;\n", - " element.src = url;\n", - " element.type = \"module\";\n", - " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", - " document.head.appendChild(element);\n", - " }\n", - " for (const name in js_exports) {\n", - " var url = js_exports[name];\n", - " if (skip.indexOf(url) >= 0 || root[name] != null) {\n", - "\tif (!window.requirejs) {\n", - "\t on_load();\n", - "\t}\n", - "\tcontinue;\n", - " }\n", - " var element = document.createElement('script');\n", - " element.onerror = on_error;\n", - " element.async = false;\n", - " element.type = \"module\";\n", - " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", - " element.textContent = `\n", - " import ${name} from \"${url}\"\n", - " window.${name} = ${name}\n", - " window._bokeh_on_load()\n", - " `\n", - " document.head.appendChild(element);\n", - " }\n", - " if (!js_urls.length && !js_modules.length) {\n", - " on_load()\n", - " }\n", - " };\n", - "\n", - " function inject_raw_css(css) {\n", - " const element = document.createElement(\"style\");\n", - " element.appendChild(document.createTextNode(css));\n", - " document.body.appendChild(element);\n", - " }\n", - "\n", - " var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.3.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.3.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.3.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.3.1.min.js\", \"https://cdn.holoviz.org/panel/1.3.1/dist/panel.min.js\"];\n", - " var js_modules = [];\n", - " var js_exports = {};\n", - " var css_urls = [];\n", - " var inline_js = [ function(Bokeh) {\n", - " Bokeh.set_log_level(\"info\");\n", - " },\n", - "function(Bokeh) {} // ensure no trailing comma for IE\n", - " ];\n", - "\n", - " function run_inline_js() {\n", - " if ((root.Bokeh !== undefined) || (force === true)) {\n", - " for (var i = 0; i < inline_js.length; i++) {\n", - " inline_js[i].call(root, root.Bokeh);\n", - " }\n", - " // Cache old bokeh versions\n", - " if (Bokeh != undefined && !reloading) {\n", - "\tvar NewBokeh = root.Bokeh;\n", - "\tif (Bokeh.versions === undefined) {\n", - "\t Bokeh.versions = new Map();\n", - "\t}\n", - "\tif (NewBokeh.version !== Bokeh.version) {\n", - "\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n", - "\t}\n", - "\troot.Bokeh = Bokeh;\n", - " }} else if (Date.now() < root._bokeh_timeout) {\n", - " setTimeout(run_inline_js, 100);\n", - " } else if (!root._bokeh_failed_load) {\n", - " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", - " root._bokeh_failed_load = true;\n", - " }\n", - " root._bokeh_is_initializing = false\n", - " }\n", - "\n", - " function load_or_wait() {\n", - " // Implement a backoff loop that tries to ensure we do not load multiple\n", - " // versions of Bokeh and its dependencies at the same time.\n", - " // In recent versions we use the root._bokeh_is_initializing flag\n", - " // to determine whether there is an ongoing attempt to initialize\n", - " // bokeh, however for backward compatibility we also try to ensure\n", - " // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n", - " // before older versions are fully initialized.\n", - " if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n", - " root._bokeh_is_initializing = false;\n", - " root._bokeh_onload_callbacks = undefined;\n", - " console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n", - " load_or_wait();\n", - " } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n", - " setTimeout(load_or_wait, 100);\n", - " } else {\n", - " Bokeh = root.Bokeh;\n", - " bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n", - " root._bokeh_is_initializing = true\n", - " root._bokeh_onload_callbacks = []\n", - " if (!reloading && (!bokeh_loaded || is_dev)) {\n", - "\troot.Bokeh = undefined;\n", - " }\n", - " load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n", - "\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", - "\trun_inline_js();\n", - " });\n", - " }\n", - " }\n", - " // Give older versions of the autoload script a head-start to ensure\n", - " // they initialize before we start loading newer version.\n", - " setTimeout(load_or_wait, 100)\n", - "}(window));" - ], - "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n var py_version = '3.3.1'.replace('rc', '-rc.').replace('.dev', '-dev.');\n var is_dev = py_version.indexOf(\"+\") !== -1 || py_version.indexOf(\"-\") !== -1;\n var reloading = false;\n var Bokeh = root.Bokeh;\n var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n\n if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n run_callbacks();\n return null;\n }\n if (!reloading) {\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n var skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {'jspanel': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel', 'jspanel-modal': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal', 'jspanel-tooltip': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip', 'jspanel-hint': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint', 'jspanel-layout': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout', 'jspanel-contextmenu': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu', 'jspanel-dock': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock', 'gridstack': 'https://cdn.jsdelivr.net/npm/gridstack@7.2.3/dist/gridstack-all', 'notyf': 'https://cdn.jsdelivr.net/npm/notyf@3/notyf.min'}, 'shim': {'jspanel': {'exports': 'jsPanel'}, 'gridstack': {'exports': 'GridStack'}}});\n require([\"jspanel\"], function(jsPanel) {\n\twindow.jsPanel = jsPanel\n\ton_load()\n })\n require([\"jspanel-modal\"], function() {\n\ton_load()\n })\n require([\"jspanel-tooltip\"], function() {\n\ton_load()\n })\n require([\"jspanel-hint\"], function() {\n\ton_load()\n })\n require([\"jspanel-layout\"], function() {\n\ton_load()\n })\n require([\"jspanel-contextmenu\"], function() {\n\ton_load()\n })\n require([\"jspanel-dock\"], function() {\n\ton_load()\n })\n require([\"gridstack\"], function(GridStack) {\n\twindow.GridStack = GridStack\n\ton_load()\n })\n require([\"notyf\"], function() {\n\ton_load()\n })\n root._bokeh_is_loading = css_urls.length + 9;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n var existing_stylesheets = []\n var links = document.getElementsByTagName('link')\n for (var i = 0; i < links.length; i++) {\n var link = links[i]\n if (link.href != null) {\n\texisting_stylesheets.push(link.href)\n }\n }\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n if (existing_stylesheets.indexOf(url) !== -1) {\n\ton_load()\n\tcontinue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } if (((window['jsPanel'] !== undefined) && (!(window['jsPanel'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/jspanel.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu.js', 'https://cdn.holoviz.org/panel/1.3.1/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } if (((window['GridStack'] !== undefined) && (!(window['GridStack'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.3.1/dist/bundled/gridstack/gridstack@7.2.3/dist/gridstack-all.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } if (((window['Notyf'] !== undefined) && (!(window['Notyf'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.3.1/dist/bundled/notificationarea/notyf@3/notyf.min.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } var existing_scripts = []\n var scripts = document.getElementsByTagName('script')\n for (var i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n\texisting_scripts.push(script.src)\n }\n }\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (var i = 0; i < js_modules.length; i++) {\n var url = js_modules[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n var url = js_exports[name];\n if (skip.indexOf(url) >= 0 || root[name] != null) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.3.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.3.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.3.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.3.1.min.js\", \"https://cdn.holoviz.org/panel/1.3.1/dist/panel.min.js\"];\n var js_modules = [];\n var js_exports = {};\n var css_urls = [];\n var inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n\tvar NewBokeh = root.Bokeh;\n\tif (Bokeh.versions === undefined) {\n\t Bokeh.versions = new Map();\n\t}\n\tif (NewBokeh.version !== Bokeh.version) {\n\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n\t}\n\troot.Bokeh = Bokeh;\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n Bokeh = root.Bokeh;\n bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n if (!reloading && (!bokeh_loaded || is_dev)) {\n\troot.Bokeh = undefined;\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n\trun_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": [ - "\n", - "if ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n", - " window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n", - "}\n", - "\n", - "\n", - " function JupyterCommManager() {\n", - " }\n", - "\n", - " JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n", - " if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", - " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", - " comm_manager.register_target(comm_id, function(comm) {\n", - " comm.on_msg(msg_handler);\n", - " });\n", - " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", - " window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n", - " comm.onMsg = msg_handler;\n", - " });\n", - " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", - " google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n", - " var messages = comm.messages[Symbol.asyncIterator]();\n", - " function processIteratorResult(result) {\n", - " var message = result.value;\n", - " console.log(message)\n", - " var content = {data: message.data, comm_id};\n", - " var buffers = []\n", - " for (var buffer of message.buffers || []) {\n", - " buffers.push(new DataView(buffer))\n", - " }\n", - " var metadata = message.metadata || {};\n", - " var msg = {content, buffers, metadata}\n", - " msg_handler(msg);\n", - " return messages.next().then(processIteratorResult);\n", - " }\n", - " return messages.next().then(processIteratorResult);\n", - " })\n", - " }\n", - " }\n", - "\n", - " JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n", - " if (comm_id in window.PyViz.comms) {\n", - " return window.PyViz.comms[comm_id];\n", - " } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", - " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", - " var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n", - " if (msg_handler) {\n", - " comm.on_msg(msg_handler);\n", - " }\n", - " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", - " var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n", - " comm.open();\n", - " if (msg_handler) {\n", - " comm.onMsg = msg_handler;\n", - " }\n", - " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", - " var comm_promise = google.colab.kernel.comms.open(comm_id)\n", - " comm_promise.then((comm) => {\n", - " window.PyViz.comms[comm_id] = comm;\n", - " if (msg_handler) {\n", - " var messages = comm.messages[Symbol.asyncIterator]();\n", - " function processIteratorResult(result) {\n", - " var message = result.value;\n", - " var content = {data: message.data};\n", - " var metadata = message.metadata || {comm_id};\n", - " var msg = {content, metadata}\n", - " msg_handler(msg);\n", - " return messages.next().then(processIteratorResult);\n", - " }\n", - " return messages.next().then(processIteratorResult);\n", - " }\n", - " }) \n", - " var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n", - " return comm_promise.then((comm) => {\n", - " comm.send(data, metadata, buffers, disposeOnDone);\n", - " });\n", - " };\n", - " var comm = {\n", - " send: sendClosure\n", - " };\n", - " }\n", - " window.PyViz.comms[comm_id] = comm;\n", - " return comm;\n", - " }\n", - " window.PyViz.comm_manager = new JupyterCommManager();\n", - " \n", - "\n", - "\n", - "var JS_MIME_TYPE = 'application/javascript';\n", - "var HTML_MIME_TYPE = 'text/html';\n", - "var EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\n", - "var CLASS_NAME = 'output';\n", - "\n", - "/**\n", - " * Render data to the DOM node\n", - " */\n", - "function render(props, node) {\n", - " var div = document.createElement(\"div\");\n", - " var script = document.createElement(\"script\");\n", - " node.appendChild(div);\n", - " node.appendChild(script);\n", - "}\n", - "\n", - "/**\n", - " * Handle when a new output is added\n", - " */\n", - "function handle_add_output(event, handle) {\n", - " var output_area = handle.output_area;\n", - " var output = handle.output;\n", - " if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", - " return\n", - " }\n", - " var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", - " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", - " if (id !== undefined) {\n", - " var nchildren = toinsert.length;\n", - " var html_node = toinsert[nchildren-1].children[0];\n", - " html_node.innerHTML = output.data[HTML_MIME_TYPE];\n", - " var scripts = [];\n", - " var nodelist = html_node.querySelectorAll(\"script\");\n", - " for (var i in nodelist) {\n", - " if (nodelist.hasOwnProperty(i)) {\n", - " scripts.push(nodelist[i])\n", - " }\n", - " }\n", - "\n", - " scripts.forEach( function (oldScript) {\n", - " var newScript = document.createElement(\"script\");\n", - " var attrs = [];\n", - " var nodemap = oldScript.attributes;\n", - " for (var j in nodemap) {\n", - " if (nodemap.hasOwnProperty(j)) {\n", - " attrs.push(nodemap[j])\n", - " }\n", - " }\n", - " attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n", - " newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n", - " oldScript.parentNode.replaceChild(newScript, oldScript);\n", - " });\n", - " if (JS_MIME_TYPE in output.data) {\n", - " toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n", - " }\n", - " output_area._hv_plot_id = id;\n", - " if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n", - " window.PyViz.plot_index[id] = Bokeh.index[id];\n", - " } else {\n", - " window.PyViz.plot_index[id] = null;\n", - " }\n", - " } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", - " var bk_div = document.createElement(\"div\");\n", - " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", - " var script_attrs = bk_div.children[0].attributes;\n", - " for (var i = 0; i < script_attrs.length; i++) {\n", - " toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n", - " }\n", - " // store reference to server id on output_area\n", - " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", - " }\n", - "}\n", - "\n", - "/**\n", - " * Handle when an output is cleared or removed\n", - " */\n", - "function handle_clear_output(event, handle) {\n", - " var id = handle.cell.output_area._hv_plot_id;\n", - " var server_id = handle.cell.output_area._bokeh_server_id;\n", - " if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n", - " var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n", - " if (server_id !== null) {\n", - " comm.send({event_type: 'server_delete', 'id': server_id});\n", - " return;\n", - " } else if (comm !== null) {\n", - " comm.send({event_type: 'delete', 'id': id});\n", - " }\n", - " delete PyViz.plot_index[id];\n", - " if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n", - " var doc = window.Bokeh.index[id].model.document\n", - " doc.clear();\n", - " const i = window.Bokeh.documents.indexOf(doc);\n", - " if (i > -1) {\n", - " window.Bokeh.documents.splice(i, 1);\n", - " }\n", - " }\n", - "}\n", - "\n", - "/**\n", - " * Handle kernel restart event\n", - " */\n", - "function handle_kernel_cleanup(event, handle) {\n", - " delete PyViz.comms[\"hv-extension-comm\"];\n", - " window.PyViz.plot_index = {}\n", - "}\n", - "\n", - "/**\n", - " * Handle update_display_data messages\n", - " */\n", - "function handle_update_output(event, handle) {\n", - " handle_clear_output(event, {cell: {output_area: handle.output_area}})\n", - " handle_add_output(event, handle)\n", - "}\n", - "\n", - "function register_renderer(events, OutputArea) {\n", - " function append_mime(data, metadata, element) {\n", - " // create a DOM node to render to\n", - " var toinsert = this.create_output_subarea(\n", - " metadata,\n", - " CLASS_NAME,\n", - " EXEC_MIME_TYPE\n", - " );\n", - " this.keyboard_manager.register_events(toinsert);\n", - " // Render to node\n", - " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", - " render(props, toinsert[0]);\n", - " element.append(toinsert);\n", - " return toinsert\n", - " }\n", - "\n", - " events.on('output_added.OutputArea', handle_add_output);\n", - " events.on('output_updated.OutputArea', handle_update_output);\n", - " events.on('clear_output.CodeCell', handle_clear_output);\n", - " events.on('delete.Cell', handle_clear_output);\n", - " events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n", - "\n", - " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", - " safe: true,\n", - " index: 0\n", - " });\n", - "}\n", - "\n", - "if (window.Jupyter !== undefined) {\n", - " try {\n", - " var events = require('base/js/events');\n", - " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", - " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", - " register_renderer(events, OutputArea);\n", - " }\n", - " } catch(err) {\n", - " }\n", - "}\n" - ], - "application/vnd.holoviews_load.v0+json": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" + "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 console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n", + "application/vnd.holoviews_load.v0+json": "" }, "metadata": {}, "output_type": "display_data" @@ -602,85 +78,6 @@ }, "metadata": {}, "output_type": "display_data" - }, - { - "data": { - "application/vnd.holoviews_exec.v0+json": "", - "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ] - }, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "p1002" - } - }, - "output_type": "display_data" } ], "source": [ @@ -778,87 +175,1175 @@ "metadata": { "tags": [] }, - "outputs": [], - "source": [ - "class CarbonSequestration(cwm.base.Model):\n", - " _variables: list[cwm.base.Variable] = []\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "id": "9b3b4c00-7a20-4446-bad9-e67577cddf87", - "metadata": {}, - "source": [ - "## Next, use the `register_variable` decorator to add a few variables\n", - "\n", - "To do this, make a sub-class of `base.Variable` but with the decorator pointed at the model(s) you want to add the variables too. Note that the `models` argument of the decorator must be either a single sub-class of `base.Model`, or a list of them.\n", - "\n", - "Next, just write instances of the new `base.Variable` sub-class. Each variable's `use` attribute must be set to `static`, `dynamic`, and `state`. Read below about what this means / how you should split up your variables.\n", - "\n", - "Note that anything that needs to be calculated or input into a model should be encapsulated by a variable!" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "0b00f982-d1b8-46bd-8060-fe95799eab30", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "@cwm.base.register_variable(models=CarbonSequestration)\n", - "class Variable(cwm.base.Variable):\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "id": "da67c500-7980-4241-acda-cbcc746608ab", - "metadata": {}, - "source": [ - "### Add our static variables\n", - "\n", - "**Working Definition:** Static variables are any variables that will not change across the course of a simulation, regardless of how many time-steps are run.\n", - "\n", - "Note that one can update static variables if they really want to by re-initializing the model class and providing new static variable inputs.\n", - "\n", - "Here we will use the following static variables:\n", - "1. **Net Primary Productivity (NPP)**: The average annual NPP of the forest ecosystem (g/m²/year).\n", - "2. **Carbon Content**: The fraction of NPP that is composed of carbon (usually around 50%, but it can vary)." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "0775943a-06fc-4fbe-b3f0-f8605cab58f7", - "metadata": { - "tags": [] - }, "outputs": [ { "data": { - "text/plain": [ - "['npp', 'carbon_content']" - ] + "application/javascript": "(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n var py_version = '3.2.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n var is_dev = py_version.indexOf(\"+\") !== -1 || py_version.indexOf(\"-\") !== -1;\n var reloading = false;\n var Bokeh = root.Bokeh;\n var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n\n if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n run_callbacks();\n return null;\n }\n if (!reloading) {\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n var skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {'jspanel': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel', 'jspanel-modal': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal', 'jspanel-tooltip': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip', 'jspanel-hint': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint', 'jspanel-layout': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout', 'jspanel-contextmenu': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu', 'jspanel-dock': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock', 'gridstack': 'https://cdn.jsdelivr.net/npm/gridstack@7.2.3/dist/gridstack-all', 'notyf': 'https://cdn.jsdelivr.net/npm/notyf@3/notyf.min'}, 'shim': {'jspanel': {'exports': 'jsPanel'}, 'gridstack': {'exports': 'GridStack'}}});\n require([\"jspanel\"], function(jsPanel) {\n\twindow.jsPanel = jsPanel\n\ton_load()\n })\n require([\"jspanel-modal\"], function() {\n\ton_load()\n })\n require([\"jspanel-tooltip\"], function() {\n\ton_load()\n })\n require([\"jspanel-hint\"], function() {\n\ton_load()\n })\n require([\"jspanel-layout\"], function() {\n\ton_load()\n })\n require([\"jspanel-contextmenu\"], function() {\n\ton_load()\n })\n require([\"jspanel-dock\"], function() {\n\ton_load()\n })\n require([\"gridstack\"], function(GridStack) {\n\twindow.GridStack = GridStack\n\ton_load()\n })\n require([\"notyf\"], function() {\n\ton_load()\n })\n root._bokeh_is_loading = css_urls.length + 9;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n var existing_stylesheets = []\n var links = document.getElementsByTagName('link')\n for (var i = 0; i < links.length; i++) {\n var link = links[i]\n if (link.href != null) {\n\texisting_stylesheets.push(link.href)\n }\n }\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n if (existing_stylesheets.indexOf(url) !== -1) {\n\ton_load()\n\tcontinue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } if (((window['jsPanel'] !== undefined) && (!(window['jsPanel'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/jspanel.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } if (((window['GridStack'] !== undefined) && (!(window['GridStack'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/gridstack/gridstack@7.2.3/dist/gridstack-all.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } if (((window['Notyf'] !== undefined) && (!(window['Notyf'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/notificationarea/notyf@3/notyf.min.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } var existing_scripts = []\n var scripts = document.getElementsByTagName('script')\n for (var i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n\texisting_scripts.push(script.src)\n }\n }\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (var i = 0; i < js_modules.length; i++) {\n var url = js_modules[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n var url = js_exports[name];\n if (skip.indexOf(url) >= 0 || root[name] != null) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.2.min.js\", \"https://cdn.holoviz.org/panel/1.2.3/dist/panel.min.js\"];\n var js_modules = [];\n var js_exports = {};\n var css_urls = [];\n var inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n\tvar NewBokeh = root.Bokeh;\n\tif (Bokeh.versions === undefined) {\n\t Bokeh.versions = new Map();\n\t}\n\tif (NewBokeh.version !== Bokeh.version) {\n\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n\t}\n\troot.Bokeh = Bokeh;\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n Bokeh = root.Bokeh;\n bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n if (!reloading && (!bokeh_loaded || is_dev)) {\n\troot.Bokeh = undefined;\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n\trun_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));", + "application/vnd.holoviews_load.v0+json": "" }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "Variable(\n", - " name='npp',\n", - " long_name='Net Primary Productivity (NPP)',\n", - " units='g/m^2/year',\n", - " description='The annual average NPP of the forest ecosystem.',\n", - " use='static',\n", - ")\n", - "Variable(\n", - " name='carbon_content',\n", - " long_name='Carbon Content ratio',\n", - " units='ratio',\n", + }, + { + "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 console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n", + "application/vnd.holoviews_load.v0+json": "" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initializing from dicts...\n", + "Model initialized from input dicts successfully!.\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:            (year: 2, x: 1, y: 1)\n",
+       "Coordinates:\n",
+       "  * year               (year) int32 0 1\n",
+       "  * x                  (x) float64 1.0\n",
+       "  * y                  (y) float64 1.0\n",
+       "Data variables: (12/52)\n",
+       "    water_temp_c       (year, x, y) float64 20.0 20.0\n",
+       "    surface_area       (year, x, y) int32 1 1\n",
+       "    volume             (year, x, y) int32 1 1\n",
+       "    use_sed_temp       (x, y) bool True\n",
+       "    stefan_boltzmann   (x, y) float64 5.67e-08\n",
+       "    cp_air             (x, y) int32 1005\n",
+       "    ...                 ...\n",
+       "    q_sediment         (year, x, y) float64 nan -401.5\n",
+       "    dTdt_sediment_c    (year, x, y) float64 nan 129.6\n",
+       "    q_longwave_down    (year, x, y) float64 nan 337.8\n",
+       "    q_longwave_up      (year, x, y) float64 nan 406.2\n",
+       "    q_net              (year, x, y) float64 nan -151.1\n",
+       "    dTdt_water_c       (year, x, y) float64 nan -3.619e-05
" + ], + "text/plain": [ + "\n", + "Dimensions: (year: 2, x: 1, y: 1)\n", + "Coordinates:\n", + " * year (year) int32 0 1\n", + " * x (x) float64 1.0\n", + " * y (y) float64 1.0\n", + "Data variables: (12/52)\n", + " water_temp_c (year, x, y) float64 20.0 20.0\n", + " surface_area (year, x, y) int32 1 1\n", + " volume (year, x, y) int32 1 1\n", + " use_sed_temp (x, y) bool True\n", + " stefan_boltzmann (x, y) float64 5.67e-08\n", + " cp_air (x, y) int32 1005\n", + " ... ...\n", + " q_sediment (year, x, y) float64 nan -401.5\n", + " dTdt_sediment_c (year, x, y) float64 nan 129.6\n", + " q_longwave_down (year, x, y) float64 nan 337.8\n", + " q_longwave_up (year, x, y) float64 nan 406.2\n", + " q_net (year, x, y) float64 nan -151.1\n", + " dTdt_water_c (year, x, y) float64 nan -3.619e-05" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import clearwater_modules as cwm\n", + "import clearwater_modules.sorter as sorter\n", + "import numba\n", + "import random\n", + "import hvplot.xarray\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "initial_state_values = {'water_temp_c': 20, 'surface_area': 1, 'volume': 1}\n", + "meteo_parameters = {\n", + " 'air_temp_c': 20,\n", + " 'q_solar': 400,\n", + " 'sed_temp_c': 5,\n", + " 'eair_mb': 1,\n", + " 'pressure_mb': 1013,\n", + " 'cloudiness': .1,\n", + " 'wind_speed': 3,\n", + " 'wind_a': .3,\n", + " 'wind_b': 1.5,\n", + " 'wind_c': 1,\n", + " 'wind_kh_kw': 1\n", + "}\n", + "\n", + "temp_parameters = {\n", + " 'use_sed_temp': False,\n", + " 'stefan_boltzmann': 0.0000000567,\n", + " 'cp_air': 1005,\n", + " 'emissivity_water': 0.97,\n", + " 'gravity': 9.806,\n", + " 'a0': 6984.505294,\n", + " 'a1': -188.903931,\n", + " 'a2': 2.133357675,\n", + " 'a3': -0.01288581,\n", + " 'a4': 0.0000439359,\n", + " 'a5': -.0000000802392,\n", + " 'a6': .0000000000613682,\n", + " 'pb': 1600,\n", + " 'cps': 1673,\n", + " 'h2': 0.1,\n", + " 'alphas': 0.0432,\n", + " 'richardson_option': True\n", + "}\n", + "\n", + "tsm_model = cwm.tsm.EnergyBudget(\n", + " initial_state_values=initial_state_values, # mandatory\n", + " temp_parameters=temp_parameters,\n", + " meteo_parameters=meteo_parameters,\n", + " track_dynamic_variables=True, # default is true\n", + " hotstart_dataset=None, # default is None\n", + " time_dim='year', # default is \"timestep\"\n", + ")\n", + "\n", + "tsm_model.increment_timestep()\n", + "tsm_model.dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "664a7267", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Variable(name='water_temp_c', long_name='Water temperature', units='degC', description='TSM state variable for water temperature', use='state', process=CPUDispatcher()),\n", + " Variable(name='surface_area', long_name='Surface area', units='m^2', description='Surface area', use='state', process=),\n", + " Variable(name='volume', long_name='Volume', units='m^3', description='Volume', use='state', process=)]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tsm_model.get_state_variables()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "76906763", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Variable | Inputs\n", + "------------------\n", + "air_temp_k | ['air_temp_c']\n", + "water_temp_k | ['water_temp_c']\n", + "mixing_ratio_air | ['eair_mb', 'pressure_mb']\n", + "density_air | ['pressure_mb', 'air_temp_k', 'mixing_ratio_air']\n", + "density_water | ['water_temp_c']\n", + "esat_mb | ['water_temp_k', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6']\n", + "density_air_sat | ['water_temp_k', 'esat_mb', 'pressure_mb']\n", + "ri_number | ['gravity', 'density_air', 'density_air_sat', 'wind_speed']\n", + "ri_function | ['ri_number']\n", + "lv | ['water_temp_k']\n", + "cp_water | ['water_temp_c']\n", + "emissivity_air | ['air_temp_k']\n", + "wind_function | ['ri_function', 'wind_a', 'wind_b', 'wind_c', 'wind_speed']\n", + "q_latent | ['pressure_mb', 'density_water', 'lv', 'wind_function', 'esat_mb', 'eair_mb']\n", + "q_sensible | ['wind_kh_kw', 'cp_air', 'density_water', 'wind_function', 'air_temp_k', 'water_temp_k']\n", + "q_sediment | ['use_sed_temp', 'pb', 'cps', 'alphas', 'h2', 'sed_temp_c', 'water_temp_c']\n", + "dTdt_sediment_c | ['use_sed_temp', 'alphas', 'h2', 'water_temp_c', 'sed_temp_c']\n", + "q_longwave_down | ['air_temp_k', 'emissivity_air', 'cloudiness', 'stefan_boltzmann']\n", + "q_longwave_up | ['water_temp_k', 'emissivity_water', 'stefan_boltzmann']\n", + "surface_area | ['surface_area']\n", + "volume | ['volume']\n", + "q_net | ['q_sensible', 'q_latent', 'q_longwave_up', 'q_longwave_down', 'q_solar', 'q_sediment']\n", + "dTdt_water_c | ['q_net', 'surface_area', 'volume', 'density_water', 'cp_water']\n", + "water_temp_c | ['water_temp_c', 'dTdt_water_c']\n" + ] + } + ], + "source": [ + "print('Variable | Inputs\\n------------------')\n", + "for i in tsm_model.computation_order:\n", + " print(f'{i.name} | {sorter.get_process_args(i.process)}')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "af03305c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:            (year: 2, x: 1, y: 1)\n",
+       "Coordinates:\n",
+       "  * year               (year) int32 0 1\n",
+       "  * x                  (x) float64 1.0\n",
+       "  * y                  (y) float64 1.0\n",
+       "Data variables: (12/52)\n",
+       "    water_temp_c       (year, x, y) float64 20.0 -7.427e+10\n",
+       "    surface_area       (year, x, y) int32 1 1\n",
+       "    volume             (year, x, y) int32 1 1\n",
+       "    use_sed_temp       (x, y) bool True\n",
+       "    stefan_boltzmann   (x, y) int32 10\n",
+       "    cp_air             (x, y) int32 1\n",
+       "    ...                 ...\n",
+       "    q_sediment         (year, x, y) float64 nan -2.572e+03\n",
+       "    dTdt_sediment_c    (year, x, y) float64 nan 0.006001\n",
+       "    q_longwave_down    (year, x, y) float64 nan 5.958e+10\n",
+       "    q_longwave_up      (year, x, y) float64 nan 7.386e+10\n",
+       "    q_net              (year, x, y) float64 nan -3.1e+17\n",
+       "    dTdt_water_c       (year, x, y) float64 nan -7.427e+10
" + ], + "text/plain": [ + "\n", + "Dimensions: (year: 2, x: 1, y: 1)\n", + "Coordinates:\n", + " * year (year) int32 0 1\n", + " * x (x) float64 1.0\n", + " * y (y) float64 1.0\n", + "Data variables: (12/52)\n", + " water_temp_c (year, x, y) float64 20.0 -7.427e+10\n", + " surface_area (year, x, y) int32 1 1\n", + " volume (year, x, y) int32 1 1\n", + " use_sed_temp (x, y) bool True\n", + " stefan_boltzmann (x, y) int32 10\n", + " cp_air (x, y) int32 1\n", + " ... ...\n", + " q_sediment (year, x, y) float64 nan -2.572e+03\n", + " dTdt_sediment_c (year, x, y) float64 nan 0.006001\n", + " q_longwave_down (year, x, y) float64 nan 5.958e+10\n", + " q_longwave_up (year, x, y) float64 nan 7.386e+10\n", + " q_net (year, x, y) float64 nan -3.1e+17\n", + " dTdt_water_c (year, x, y) float64 nan -7.427e+10" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tsm_model.increment_timestep()\n", + "tsm_model.dataset" + ] + }, + { + "cell_type": "markdown", + "id": "9b3b4c00-7a20-4446-bad9-e67577cddf87", + "metadata": {}, + "source": [ + "## Next, use the `register_variable` decorator to add a few variables\n", + "\n", + "To do this, make a sub-class of `base.Variable` but with the decorator pointed at the model(s) you want to add the variables too. Note that the `models` argument of the decorator must be either a single sub-class of `base.Model`, or a list of them.\n", + "\n", + "Next, just write instances of the new `base.Variable` sub-class. Each variable's `use` attribute must be set to `static`, `dynamic`, and `state`. Read below about what this means / how you should split up your variables.\n", + "\n", + "Note that anything that needs to be calculated or input into a model should be encapsulated by a variable!" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "0b00f982-d1b8-46bd-8060-fe95799eab30", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "@cwm.base.register_variable(models=CarbonSequestration)\n", + "class Variable(cwm.base.Variable):\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "0775943a-06fc-4fbe-b3f0-f8605cab58f7", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['npp', 'carbon_content']" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "Variable(\n", + " name='npp',\n", + " long_name='Net Primary Productivity (NPP)',\n", + " units='g/m^2/year',\n", + " description='The annual average NPP of the forest ecosystem.',\n", + " use='static',\n", + ")\n", + "Variable(\n", + " name='carbon_content',\n", + " long_name='Carbon Content ratio',\n", + " units='ratio',\n", " description='The fraction of NPP that is composed of carbon (usually around 50%, but it can vary).',\n", " use='static',\n", ")\n", @@ -867,6 +1352,22 @@ "display(CarbonSequestration.get_variable_names())" ] }, + { + "cell_type": "markdown", + "id": "da67c500-7980-4241-acda-cbcc746608ab", + "metadata": {}, + "source": [ + "### Add our static variables\n", + "\n", + "**Working Definition:** Static variables are any variables that will not change across the course of a simulation, regardless of how many time-steps are run.\n", + "\n", + "Note that one can update static variables if they really want to by re-initializing the model class and providing new static variable inputs.\n", + "\n", + "Here we will use the following static variables:\n", + "1. **Net Primary Productivity (NPP)**: The average annual NPP of the forest ecosystem (g/m²/year).\n", + "2. **Carbon Content**: The fraction of NPP that is composed of carbon (usually around 50%, but it can vary)." + ] + }, { "cell_type": "markdown", "id": "619d0f25-5048-4b50-a3f6-d36b468abb77", diff --git a/examples/model_architecture_nsm.ipynb b/examples/model_architecture_nsm.ipynb new file mode 100644 index 0000000..8ca83c1 --- /dev/null +++ b/examples/model_architecture_nsm.ipynb @@ -0,0 +1,1449 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "25bd6ed5-3bd1-4592-a813-5958221d5a21", + "metadata": { + "tags": [] + }, + "source": [ + "# Clearwater Modules Architecture - NSM\n", + "\n", + "**Author:** Xavier Nogueira" + ] + }, + { + "cell_type": "markdown", + "id": "95a96e20", + "metadata": {}, + "source": [ + "# Installation and Setup" + ] + }, + { + "cell_type": "markdown", + "id": "80133cfc", + "metadata": {}, + "source": [ + "## Install\n", + "\n", + "Carefully follow our **[Installation Instructions](README.md#getting-started)**, especially including:\n", + "- Creating a virtual environment for this repository (step 3)" + ] + }, + { + "cell_type": "markdown", + "id": "f73cfdf5", + "metadata": {}, + "source": [ + "## Import Python Dependancies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "663a1e5a-4311-42e7-babc-6e254a5ff9cf", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import clearwater_modules as cwm\n", + "import clearwater_modules.sorter as sorter\n", + "import numba\n", + "import random\n", + "import hvplot.xarray\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01c71150", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Confirm that sub-modules are imported\n", + "dir(cwm)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "3bce5192", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initializing from dicts...\n", + "Model initialized from input dicts successfully!.\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:  (year: 1, x: 1, y: 1)\n",
+       "Coordinates:\n",
+       "  * year     (year) int32 0\n",
+       "  * x        (x) float64 1.0\n",
+       "  * y        (y) float64 1.0\n",
+       "Data variables:\n",
+       "    *empty*
" + ], + "text/plain": [ + "\n", + "Dimensions: (year: 1, x: 1, y: 1)\n", + "Coordinates:\n", + " * year (year) int32 0\n", + " * x (x) float64 1.0\n", + " * y (y) float64 1.0\n", + "Data variables:\n", + " *empty*" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import clearwater_modules as cwm\n", + "import clearwater_modules.sorter as sorter\n", + "import numba\n", + "import random\n", + "import hvplot.xarray\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "from clearwater_modules.nsm1.model import NutrientBudget\n", + "import clearwater_modules.nsm1.model as test\n", + "\n", + "\n", + "\n", + "initial_state_values = {'Ap': 1,\n", + " 'Ab': 1,\n", + " 'NH4': 1,\n", + " 'NO3': 1,\n", + " 'OrgN': 1,\n", + " 'N2': 1,\n", + " 'TIP': 1,\n", + " 'OrgP': 1,\n", + " 'POC': 1,\n", + " 'DOC': 1,\n", + " 'DIC': 1,\n", + " 'POM': 1,\n", + " 'CBOD': 1,\n", + " 'DOX': 1,\n", + " 'PX': 1,\n", + " 'Alk': 1}\n", + "\n", + "algae_parameters = {\n", + " 'AWd': 100,\n", + " 'AWc': 40,\n", + " 'AWn': 7.2,\n", + " 'AWp': 1,\n", + " 'AWa': 1000,\n", + " \n", + " 'KL': 10,\n", + " 'KsN': 0.04,\n", + " 'KsP': 0.0012,\n", + " 'mu_max_20': 1,\n", + " 'kdp_20': 0.15,\n", + " 'krp_20': 0.2,\n", + " 'vsap': 0.15,\n", + " 'growth_rate_option': 1,\n", + " 'light_limitation_option': 1\n", + "}\n", + "\n", + "balgae_parameters = {\n", + " 'BWd': 100,\n", + " 'BWc': 40,\n", + " 'Bwn': 7.2,\n", + " 'BWp': 1,\n", + " 'BWa': 3500,\n", + "\n", + " 'KLb': 10,\n", + " 'KsNb': 0.25,\n", + " 'KsPb': 0.125,\n", + " 'Ksb': 10,\n", + " 'mub_max_20': 0.4,\n", + " 'krb_20': 0.2,\n", + " 'kdb_20': 0.3,\n", + " 'b_growth_rate_option': 1,\n", + " 'b_light_limitation_option': 1,\n", + " 'Fw': 0.9,\n", + " 'Fb': 0.9\n", + "}\n", + "\n", + "nitrogen_parameters = {\n", + " 'KNR': 0.6,\n", + " 'knit_20': 0.1,\n", + " 'kon_20': 0.1,\n", + " 'kdnit_20': 0.002,\n", + " 'rnh4_20': 0,\n", + " 'vno3_20': 0,\n", + " 'KsOxdn': 0.1,\n", + " 'PN': 0.5,\n", + " 'PNb': 0.5\n", + "}\n", + "\n", + "phosphorus_parameters = {\n", + " 'kop_20': 0.1,\n", + " 'rpo4_20': 0\n", + "}\n", + "\n", + "POM_parameters = {\n", + " 'kpom_20': 0.1\n", + "}\n", + "\n", + "CBOD_parameters = {\n", + " 'kbod_20': 0.12,\n", + " 'ksbod_20': 0,\n", + " 'ksOxbod': 0.5\n", + "}\n", + "\n", + "carbon_parameters = {\n", + " 'F_pocp': 0.9,\n", + " 'kdoc_20': 0.01,\n", + " 'F_pocb': 0.9,\n", + " 'kpoc_20': 0.005,\n", + " 'K_sOxmc': 1,\n", + " 'pCO2': 383,\n", + " 'FCO2': 0.2\n", + "}\n", + "\n", + "pathogen_parameters = {\n", + " 'kdx': 0.8,\n", + " 'apx': 1,\n", + " 'vx': 1\n", + "}\n", + "\n", + "alkalinity_parameters = {\n", + " 'r_alkaa': 1,\n", + " 'r_alkan': 1,\n", + " 'r_alkn': 1,\n", + " 'r_alkden': 1,\n", + " 'r_alkba': 1,\n", + " 'r_alkbn': 1 \n", + "}\n", + "\n", + "global_parameters = {\n", + " 'use_NH4': True,\n", + " 'use_NO3': True, \n", + " 'use_OrgN': True,\n", + " 'use_TIP': True, \n", + " 'use_SedFlux': True,\n", + " 'use_DOX': True,\n", + " 'use_Algae': True,\n", + " 'use_Balgae': True,\n", + " 'use_OrgP': True,\n", + " 'use_POC': True,\n", + " 'use_DOC': True,\n", + " 'use_DIC': True,\n", + " 'use_N2': True,\n", + " 'use_Pathogen': True,\n", + " 'use_Alk': True,\n", + " 'use_POM': True \n", + "}\n", + "\n", + "\n", + "global_vars = {\n", + " 'vson': 0.01,\n", + " 'vsoc': 0.01,\n", + " 'vsop': 999,\n", + " 'vs': 999,\n", + " 'SOD_20': 999,\n", + " 'SOD_theta': 999,\n", + " 'vb': 0.01,\n", + " 'fcom': 0.4,\n", + " 'kaw_20_user': 999,\n", + " 'kah_20_user': 999,\n", + " 'hydraulic_reaeration_option': 2,\n", + " 'wind_reaeration_option': 2,\n", + " 'Fr_PAR': .5,\n", + " 'lambda0': .5,\n", + " 'lambda1': .5,\n", + " 'lambda2': .5,\n", + " 'lambdas': .5,\n", + " 'lambdam': .5, \n", + " 'timestep': 86400,\n", + " 'velocity': 1,\n", + " 'flow': 2,\n", + " 'topwidth': 1,\n", + " 'slope': 2,\n", + " 'shear_velocity': 4,\n", + " 'pressure_atm': 2,\n", + " 'wind_speed': 4,\n", + " 'q_solar': 4,\n", + " 'Solid': 1\n", + "}\n", + "\n", + "DOX_parameters = {}\n", + "N2_parameters = {}\n", + "\n", + "\n", + "nsm_model = NutrientBudget(\n", + " initial_state_values=initial_state_values, # mandatory\n", + " algae_parameters=algae_parameters,\n", + " alkalinity_parameters=alkalinity_parameters,\n", + " balgae_parameters=balgae_parameters,\n", + " carbon_parameters=carbon_parameters,\n", + " CBOD_parameters=CBOD_parameters,\n", + " DOX_parameters=DOX_parameters,\n", + " nitrogen_parameters=nitrogen_parameters,\n", + " POM_parameters=POM_parameters,\n", + " N2_parameters=N2_parameters,\n", + " phosphorus_parameters=phosphorus_parameters,\n", + " pathogen_parameters=pathogen_parameters,\n", + " global_parameters=global_parameters,\n", + " global_vars=global_vars, \n", + " track_dynamic_variables=True, # default is true\n", + " hotstart_dataset=None, # default is None\n", + " time_dim='year', # default is \"timestep\"\n", + ")\n", + "\n", + "#print(tsm_model.get_state_variables())\n", + "nsm_model.dataset" + ] + }, + { + "cell_type": "markdown", + "id": "234fa8ec", + "metadata": {}, + "source": [ + "### If you get `ModuleNotFoundError`:\n", + "\n", + "If you get this error:\n", + "```python\n", + "ModuleNotFoundError: No module named 'clearwater_modules'\n", + "```\n", + "Then:\n", + "1. Run the following terminal command with your local absolute path to this repo.\n", + " - NOTE: Here we use Jupyter `!` magic command to run from the terminal via this notebook. \n", + "2. Restart the kernel.\n", + "3. Rerun the import statements above.\n", + "\n", + "See [4. Add your `ClearWater-modules-python` Path to Miniconda/Anaconda sites-packages](..ReadMe.md#4-add-your-clearwater-modules-python-path-to-minicondaanaconda-sites-packages)." + ] + }, + { + "cell_type": "markdown", + "id": "de2f2cd1-a5dc-4536-8b1c-5f4fbf866601", + "metadata": {}, + "source": [ + "# Writing/using a simple `Model` sub-class example\n", + "\n", + "In this example we will be writing a `base.Model` sub-class that calculates the annual carbon sequestration in a forest for a given year timestep.\n", + "\n", + "**Note:** Do not take the calculation too literally! I got it off ChatGPT in order to find a good, simple example for the code." + ] + }, + { + "cell_type": "markdown", + "id": "a52584bf-13af-4527-87ff-dbfa1010ddc1", + "metadata": {}, + "source": [ + "## Start by inheriting `base.Model` -> `CarbonSequestration(cwm.base.Model)`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a5d34d2d-03a6-41e1-92fc-9e6fba72d6f5", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "class NSM_model(cwm.base.Model):\n", + " _variables: list[cwm.base.Variable] = []\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "id": "9b3b4c00-7a20-4446-bad9-e67577cddf87", + "metadata": {}, + "source": [ + "## Next, use the `register_variable` decorator to add a few variables\n", + "\n", + "To do this, make a sub-class of `base.Variable` but with the decorator pointed at the model(s) you want to add the variables too. Note that the `models` argument of the decorator must be either a single sub-class of `base.Model`, or a list of them.\n", + "\n", + "Next, just write instances of the new `base.Variable` sub-class. Each variable's `use` attribute must be set to `static`, `dynamic`, and `state`. Read below about what this means / how you should split up your variables.\n", + "\n", + "Note that anything that needs to be calculated or input into a model should be encapsulated by a variable!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b00f982-d1b8-46bd-8060-fe95799eab30", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "@cwm.base.register_variable(models=CarbonSequestration)\n", + "class Variable(cwm.base.Variable):\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "id": "da67c500-7980-4241-acda-cbcc746608ab", + "metadata": {}, + "source": [ + "### Add our static variables\n", + "\n", + "**Working Definition:** Static variables are any variables that will not change across the course of a simulation, regardless of how many time-steps are run.\n", + "\n", + "Note that one can update static variables if they really want to by re-initializing the model class and providing new static variable inputs.\n", + "\n", + "Here we will use the following static variables:\n", + "1. **Net Primary Productivity (NPP)**: The average annual NPP of the forest ecosystem (g/m²/year).\n", + "2. **Carbon Content**: The fraction of NPP that is composed of carbon (usually around 50%, but it can vary)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0775943a-06fc-4fbe-b3f0-f8605cab58f7", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "Variable(\n", + " name='npp',\n", + " long_name='Net Primary Productivity (NPP)',\n", + " units='g/m^2/year',\n", + " description='The annual average NPP of the forest ecosystem.',\n", + " use='static',\n", + ")\n", + "Variable(\n", + " name='carbon_content',\n", + " long_name='Carbon Content ratio',\n", + " units='ratio',\n", + " description='The fraction of NPP that is composed of carbon (usually around 50%, but it can vary).',\n", + " use='static',\n", + ")\n", + "\n", + "# display the variables we have registered so far\n", + "display(CarbonSequestration.get_variable_names())" + ] + }, + { + "cell_type": "markdown", + "id": "619d0f25-5048-4b50-a3f6-d36b468abb77", + "metadata": {}, + "source": [ + "### Add our dynamic variables\n", + "\n", + "**Working Definition:** Dynamic variables are any intermediate variable calculation that don't need to be passed to the next timestep. All dynamic variables need to be associated with a function via the optional `Variable.process` attribute. This \"process\" function is used to calculate them. **Importantly, the arguments of said function should match the variable names that will be passed in!**\n", + "\n", + "In this simple example we will have only one dynamic variable:\n", + "1. **Annual carbon sequestration** (delta_C_annual):\n", + "\n", + " `delta_C_annual = npp * carbon_content * forest_area`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f622cae1-4e0b-493e-8458-d441f5d125a2", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "@numba.njit\n", + "def delta_C_annual(\n", + " npp: float,\n", + " carbon_content: float,\n", + " forest_area: float,\n", + ") -> float:\n", + " return npp * carbon_content * forest_area" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "774efcc3-3a9a-4f74-a1a9-3663daf21dd5", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "Variable(\n", + " name='delta_C_annual',\n", + " long_name='Annual Carbon Delta',\n", + " units='g',\n", + " description='Annual change in forest carbon content',\n", + " use='dynamic',\n", + " process=delta_C_annual,\n", + ")\n", + "\n", + "# display the variables we have registered so far\n", + "display(CarbonSequestration.get_variable_names())" + ] + }, + { + "cell_type": "markdown", + "id": "5af6cb2f-50c4-4cd9-91a9-a8d159595df9", + "metadata": {}, + "source": [ + "### Add our state variable\n", + "\n", + "**Working Definition:** A state variable is the main input/output to each timestep. Notably, it can be updated between timesteps to allow interaction with other models. Our model needs to be initialized with state variable values, and no matter what settings are used in initialization, the state variable is stored in our main dataset (keep reading to see this).\n", + "\n", + "Our state variable is the total carbon stock of the forest, which is updated each year:\n", + "1. **Total carbon stock** (C_total):\n", + "\n", + " `C_total = C_total + delta_C_annual`\n", + " \n", + "State variables also require a process function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6f26ee-56d2-493f-9478-01407020b34c", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "@numba.njit\n", + "def C_total(\n", + " C_total: float,\n", + " delta_C_annual: float,\n", + ") -> float:\n", + " return C_total + delta_C_annual" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8b9ba47-3c9a-4b8f-92b0-7a44182d7813", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "Variable(\n", + " name='C_total',\n", + " long_name='Carbon total',\n", + " units='g',\n", + " description='Total forest carbon content',\n", + " use='state',\n", + " process=C_total,\n", + ")\n", + "\n", + "Variable(\n", + " name='forest_area',\n", + " long_name='Area of the forest',\n", + " units='m^2',\n", + " description='Area of the forest, may change year by year with deforestation.',\n", + " use='state',\n", + " process=forest_area,\n", + ")\n", + "\n", + "# display the variables we have registered so far\n", + "display(CarbonSequestration.get_variable_names())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "98b447fb-65fa-46bf-a52c-4a4b1390ba08", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# for state variables we can see them before initialization\n", + "display(CarbonSequestration.get_state_variables())" + ] + }, + { + "cell_type": "markdown", + "id": "95d04864-2b19-4081-a688-f6729b37a8fd", + "metadata": {}, + "source": [ + "## Now let's instantiate our new model\n", + "\n", + "To instantiate a model we need to pass in a dictionary with our initial state variable values, any non-default changes to our static variables, and any other optional config settings." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f489f488-42e6-4e1d-b403-01ebf5738582", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "initial_state_values = {'C_total': 1000, 'forest_area': 1000}\n", + "static_variable_values = {\n", + " 'carbon_content': 0.5,\n", + " 'npp': 10,\n", + "}\n", + "\n", + "carbon_model = CarbonSequestration(\n", + " initial_state_values=initial_state_values, # mandatory\n", + " static_variable_values=static_variable_values, # mandatory/optional depending on defaults\n", + " track_dynamic_variables=True, # default is true\n", + " hotstart_dataset=None, # default is None\n", + " time_dim='year', # default is \"timestep\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "f058c597-d808-4c1f-8c57-249ee0ba859f", + "metadata": { + "tags": [] + }, + "source": [ + "### All instantiated models have static, dynamic, and state variable properties" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b9316ecd-5372-4eab-8d0c-87c80c20ce90", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "display(carbon_model.state_variables)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c131bccf-eb39-4abf-8e35-cfb6027c8df6", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "display(carbon_model.static_variables)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37e64e74-9ec2-4d43-83ff-0f2bfdac7445", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "display(carbon_model.dynamic_variables)" + ] + }, + { + "cell_type": "markdown", + "id": "97639e68-ffcc-4d53-b315-4c79ce873139", + "metadata": { + "tags": [] + }, + "source": [ + "### One can access their \"computation order\" which is calculated using a \"dependency tree\" approach in `sorter.py`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "188344ee-ff6a-4ee0-8cde-cf0ab7817735", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "carbon_model.computation_order" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2bbcfe5-5c23-4224-a505-f1fb79c2e661", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "print('Variable | Inputs\\n------------------')\n", + "for i in carbon_model.computation_order:\n", + " print(f'{i.name} | {sorter.get_process_args(i.process)}')" + ] + }, + { + "cell_type": "markdown", + "id": "5be835a7-8a2b-4200-a962-e24147aa2e35", + "metadata": {}, + "source": [ + "### Data is stored in `self.dataset`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2897c1a4-520f-4a31-928e-038756da36af", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "carbon_model.dataset" + ] + }, + { + "cell_type": "markdown", + "id": "a7f8dabd-1f2b-4610-8280-29be9f18b016", + "metadata": {}, + "source": [ + "## Running a timestep\n", + "All timesteps can be run independently. Optionally, one can update the state values with a float or a `xarray.DataArray`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bb7808-dbca-4508-ba86-b0cb23b8f5e8", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "carbon_model.increment_timestep()\n", + "carbon_model.dataset" + ] + }, + { + "cell_type": "markdown", + "id": "b824cec0-7385-4b21-937e-64af96bb10b7", + "metadata": {}, + "source": [ + "## Running a loop of timesteps\n", + "\n", + "Here we run 100 years of our model with the following hypothetical:\n", + "* For the first 50 years deforestation reduces forest area incrementally.\n", + "* 50 years in, a program begins that ends deforestation, and the forest grows back incrementally.\n", + "\n", + "**This demonstrates how we can update state variables to interact with other models!**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aec534de-846d-40d8-9d25-e4cf2e24c1b1", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "%%time\n", + "for i in range(100):\n", + " forest_area_change = random.uniform(0.0, 25)\n", + " if i < 50:\n", + " forest_area_change = -forest_area_change\n", + " new_forest_area = (carbon_model.dataset.forest_area + forest_area_change).isel(year=-1)\n", + " carbon_model.increment_timestep(update_state_values={'forest_area': new_forest_area})\n", + "carbon_model.dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b35b04a-9dfa-4af1-a7d3-f13e84019c3b", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "carbon_model.dataset.hvplot(x='year', y='delta_C_annual', title='delta_C_annual')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49e9cb85-9d90-49c1-a308-dcb31a6f6092", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "carbon_model.dataset.hvplot(x='year', y='C_total', title='C_total')" + ] + }, + { + "cell_type": "markdown", + "id": "be48713d-1170-4195-b95d-35ee1c99dd3d", + "metadata": { + "tags": [] + }, + "source": [ + "# TSM `EnergyBudget` Example\n", + "\n", + "Now that we understand how the code architecture works, we can explore a real example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d02e421d-e478-4c14-b08b-fbd8ffc82a5d", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from clearwater_modules.tsm.model import EnergyBudget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a963a7f9", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Confirm that sub-modules are imported\n", + "dir(cwm.tsm)" + ] + }, + { + "cell_type": "markdown", + "id": "28e4266d-424e-47dc-bc26-5c2e66819b0a", + "metadata": { + "tags": [] + }, + "source": [ + "## Start by instantiating a `EnergyBudget`\n", + "\n", + "Initial state variable values are always required. To see the names/info of a model's state variables, we can use `Model.get_state_variables()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6942a19-df9c-48c7-afc3-37e98d44c0d0", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "EnergyBudget.get_state_variables()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be877b70-073a-4877-af10-c1ad3c386165", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "initial_state_values = {\n", + " 'water_temp_c': 1.0,\n", + " 'volume': 1.0,\n", + " 'surface_area': 1.0,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "299d062c-b981-45e6-a437-156c4a3d8388", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "my_model = EnergyBudget(\n", + " initial_state_values,\n", + " time_dim='my_time_step',\n", + ")\n", + "my_model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ee305a3-5990-47aa-885e-2771dda9fa5f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "[i for i in dir(my_model) if i[0] != '_']" + ] + }, + { + "cell_type": "markdown", + "id": "825e288f-b51a-4590-91f0-dd706c118725", + "metadata": {}, + "source": [ + "## TSM can be initialized with alternative met/temp parameter\n", + "**This is an example of a model specific `__init__`**. As of now we are using the defaults." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac36bc9f-aa8f-4e35-946e-1c69f32a3d27", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "my_model.met_parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7411afa5-83ac-4ff7-9e95-a73a4d3a2aa8", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "my_model.temp_parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8da8894-a6ad-42dc-9618-dc899a1525ea", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "my_model.time_dim" + ] + }, + { + "cell_type": "markdown", + "id": "f74682c6-096c-4f41-b929-09027a6e335d", + "metadata": {}, + "source": [ + "## All models have static, dynamic, and state variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2d0c2856-9012-4ebb-bb23-457f209c4e0f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "display(my_model.static_variables)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f082754-055e-4c58-af74-2e2c73a023fb", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "display(my_model.dynamic_variables)" + ] + }, + { + "cell_type": "markdown", + "id": "7e672a66-3649-4d49-9616-232959fc3d40", + "metadata": {}, + "source": [ + "## One can access their \"computation order\" which is calculated using a \"dependency tree\" approach in `sorter.py`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "301e91eb-fb88-468d-9a5c-5baba31cd21f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "my_model.computation_order" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aca8eace-0ee4-4a3f-bb82-e3794098dbf5", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "for i in my_model.computation_order:\n", + " print(f'{i.name} | {sorter.get_process_args(i.process)}')" + ] + }, + { + "cell_type": "markdown", + "id": "f5b2234a-38b5-4ffc-a180-cf126be22544", + "metadata": {}, + "source": [ + "## Run 5 timesteps" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa7f69c8-d45d-4119-a353-7655327dcdb6", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "TIME_STEPS = 5" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb7f2291-3df9-4464-b256-45abc8b225d0", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "@numba.jit(forceobj=True)\n", + "def run_n_timesteps(time_steps: int, model: EnergyBudget):\n", + " for i in range(time_steps):\n", + " model.increment_timestep()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b4ed28c4-301f-4fd2-9e70-e443b4ef909a", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "%%time\n", + "run_n_timesteps(TIME_STEPS, my_model)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df2bd00b-4dc0-4f1f-b527-c537c72a454a", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "my_model.dataset" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.6" + }, + "toc-autonumbering": true + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/clearwater_modules/__init__.py b/src/clearwater_modules/__init__.py index f26f3f6..2e22135 100644 --- a/src/clearwater_modules/__init__.py +++ b/src/clearwater_modules/__init__.py @@ -1,3 +1,4 @@ __version__ = "0.2.0" from clearwater_modules import shared from clearwater_modules import tsm +from clearwater_modules import nsm1 diff --git a/src/clearwater_modules/nsm1/CBOD/dynamic_variables.py b/src/clearwater_modules/nsm1/CBOD/dynamic_variables.py index 834dded..7c81a2a 100644 --- a/src/clearwater_modules/nsm1/CBOD/dynamic_variables.py +++ b/src/clearwater_modules/nsm1/CBOD/dynamic_variables.py @@ -1,32 +1,34 @@ -# TODO: figure out imports... +""" +File contains dynamic variables related to the CBOD module +""" import clearwater_modules.shared.processes as shared_processes from clearwater_modules import base -from clearwater_modules.tsm.model import EnergyBudget -from clearwater_modules.nsm1.CBOD import processes +from clearwater_modules.nsm1.model import NutrientBudget +import clearwater_modules.nsm1.CBOD.processes as processes -@base.register_variable(models=EnergyBudget) +@base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... Variable( - name='kbod_T', + name='kbod_tc', long_name='Temperature adjusted oxidation rate', units='1/d', description='Temperature adjusted oxidation rate', use='dynamic', - process=processes.kbod_T + process=processes.kbod_tc ) Variable( - name='ksbod_T', + name='ksbod_tc', long_name='Temperature adjusted sedimentation rate', units='m/d', description='Temperature adjusted sedimentation rate', use='dynamic', - process=processes.ksbod_T + process=processes.ksbod_tc ) Variable( diff --git a/src/clearwater_modules/nsm1/CBOD/processes.py b/src/clearwater_modules/nsm1/CBOD/processes.py index fa5916b..0ea2729 100644 --- a/src/clearwater_modules/nsm1/CBOD/processes.py +++ b/src/clearwater_modules/nsm1/CBOD/processes.py @@ -1,52 +1,49 @@ -import numpy as np +""" +File contains process to calculate new CBOD concentration and associated dependent variables +""" + import numba import xarray as xr -from clearwater_modules.shared.processes import ( - arrhenius_correction, -) - +from clearwater_modules.shared.processes import arrhenius_correction +import math @numba.njit -def kbod_T( - water_temp_c: xr.DataArray, +def kbod_tc( + TwaterC: xr.DataArray, kbod_20: xr.DataArray, - theta: xr.DataArray ) -> xr.DataArray: """Calculate the temperature adjusted CBOD oxidation rate (1/d) Args: - water_temp_c: water temperature in Celsius + TwaterC: water temperature in Celsius kbod_20: CBOD oxidation rate at 20 degrees Celsius (1/d) - theta: Arrhenius coefficient """ - kbod_T = arrhenius_correction(water_temp_c, kbod_20, theta) - return kbod_T + kbod_tc = arrhenius_correction(TwaterC, kbod_20, 1.047) + return kbod_tc @numba.njit -def ksbod_T( - water_temp_c: xr.DataArray, +def ksbod_tc( + TwaterC: xr.DataArray, ksbod_20: xr.DataArray, - theta: xr.DataArray ) -> xr.DataArray: """Calculate the temperature adjusted CBOD sedimentation rate (m/d) Args: - water_temp_c: water temperature in Celsius + TwaterC: water temperature in Celsius ksbod_20: CBOD sedimentation rate at 20 degrees Celsius (m/d) - theta: Arrhenius coefficient """ - ksbod_T = arrhenius_correction(water_temp_c, ksbod_20, theta) - return ksbod_T + ksbod_tc = arrhenius_correction(TwaterC, ksbod_20, 1.024) + return ksbod_tc def CBOD_oxidation( DOX: xr.DataArray, CBOD: xr.DataArray, - kbod_T: xr.DataArray, + kbod_tc: xr.DataArray, KsOxbod: xr.DataArray, use_DOX: xr.DataArray ) -> xr.DataArray: @@ -55,11 +52,11 @@ def CBOD_oxidation( Args: DOX: Dissolved oxygen concentration (mg-O2/L) CBOD: Carbonaceous biochemical oxygen demand (mg-O2/L) - kbod_T: Temperature adjusted CBOD oxidation rate (1/d) + kbod_tc: Temperature adjusted CBOD oxidation rate (1/d) KsOxbod: Half-saturation oxygen attenuation for CBOD oxidation (mg-O2/L) use_DOX: Option to consider DOX concentration in calculation of CBOD oxidation """ - da: xr.DataArray = xr.where(use_DOX == True, (DOX / (KsOxbod + DOX)) * kbod_T * CBOD, kbod_T * CBOD) + da: xr.DataArray = xr.where(use_DOX == True, (DOX / (KsOxbod + DOX)) * kbod_tc * CBOD, kbod_tc * CBOD) return da @@ -67,16 +64,16 @@ def CBOD_oxidation( @numba.njit def CBOD_sedimentation( CBOD: xr.DataArray, - ksbod_T: xr.DataArray + ksbod_tc: xr.DataArray ) -> xr.DataArray: """Calculates CBOD sedimentation for each group Args: CBOD: CBOD concentration (mg-O2/L) - ksbod_T: Temperature adjusted sedimentation rate (m/d) + ksbod_tc: Temperature adjusted sedimentation rate (m/d) """ - CBOD_sedimentation = CBOD * ksbod_T + CBOD_sedimentation = CBOD * ksbod_tc return CBOD_sedimentation @@ -95,7 +92,7 @@ def dCBODdt( @numba.njit -def CBOD_new( +def CBOD( CBOD: xr.DataArray, dCBODdt: xr.DataArray, timestep: xr.DataArray diff --git a/src/clearwater_modules/nsm1/CBOD/static_variables.py b/src/clearwater_modules/nsm1/CBOD/static_variables.py index 05c0129..dfeebd5 100644 --- a/src/clearwater_modules/nsm1/CBOD/static_variables.py +++ b/src/clearwater_modules/nsm1/CBOD/static_variables.py @@ -1,14 +1,16 @@ -# TODO: figure out what 'model' to import +""" +File contains static variables related to the CBOD module +""" + import clearwater_modules.base as base -from clearwater_modules.tsm.model import EnergyBudget +from clearwater_modules.nsm1.model import NutrientBudget -@base.register_variable(models=EnergyBudget) +@base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... -# CBOD variables for each CBOD group - array Variable( name='kbod_20', long_name='CBOD oxidation rate at 20C', @@ -26,9 +28,10 @@ class Variable(base.Variable): ) Variable( - name='ksOxbod', + name='KsOxbod', long_name='Half saturation oxygen attenuation constant for CBOD oxidation', units='mg-O2/L', description='Half saturation oxygen attenuation constant for CBOD oxidation', use='static' ) + diff --git a/src/clearwater_modules/nsm1/DOX/dynamic_variables.py b/src/clearwater_modules/nsm1/DOX/dynamic_variables.py index 2b5b8a9..87ae512 100644 --- a/src/clearwater_modules/nsm1/DOX/dynamic_variables.py +++ b/src/clearwater_modules/nsm1/DOX/dynamic_variables.py @@ -2,11 +2,11 @@ import clearwater_modules.shared.processes as shared_processes from clearwater_modules import base -from clearwater_modules.tsm.model import EnergyBudget -from clearwater_modules.nsm1.DOX import processes +from clearwater_modules.nsm1.model import NutrientBudget +import clearwater_modules.nsm1.DOX.processes as processes -@base.register_variable(models=EnergyBudget) +@base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... @@ -38,51 +38,6 @@ class Variable(base.Variable): process=processes.DOs_atm_alpha ) -Variable( - name='kah_20', - long_name='Hydraulic oxygen reaeration rate adjusted for hydraulics', - units='1/d', - description='Hydraulic oxygen reaeration rate adjusted for hydraulic parameters according to XX lit', - use='dynamic', - process=processes.kah_20 -) - -Variable( - name='kah_T', - long_name='Hydraulic oxygen reaeration rate adjusted for temperature', - units='1/d', - description='Hydraulic oxygen reaeration rate adjusted for temperature', - use='dynamic', - process=processes.kah_T -) - -Variable( - name='kaw_20', - long_name='Wind oxygen reaeration velocity adjusted for hydraulics', - units='m/d', - description='Wind oxygen reaeration velocity adjusted for hydraulic parameters according to XX lit', - use='dynamic', - process=processes.kaw_20 -) - -Variable( - name='kaw_T', - long_name='Wind oxygen reaeration velocity adjusted for temperature', - units='m/d', - description='Wind oxygen reaeration velocity adjusted for temperature', - use='dynamic', - process=processes.kaw_T -) - -Variable( - name='ka_T', - long_name='Oxygen reaeration rate', - units='1/d', - description='Oxygen reaeration rate', - use='dynamic', - process=processes.ka_T -) - Variable( name='Atm_O2_reaeration', long_name='Atmospheric oxygen reaeration', @@ -157,14 +112,6 @@ class Variable(base.Variable): process=processes.DOX_AbRespiration ) -Variable( - name='SOD_tc', - long_name='Sediment oxygen demand corrected by temperature and dissolved oxygen concentration', - units='g/m^3/d', - description='Sediment oxygen demand corrected by temperature and dissolved oxygen concentration', - use='dynamic', - process=processes.SOD_tc -) Variable( name='DOX_SOD', diff --git a/src/clearwater_modules/nsm1/DOX/processes.py b/src/clearwater_modules/nsm1/DOX/processes.py index 52d752e..0dd2f9e 100644 --- a/src/clearwater_modules/nsm1/DOX/processes.py +++ b/src/clearwater_modules/nsm1/DOX/processes.py @@ -1,197 +1,83 @@ -import numpy as np +""" +File contains dynamic variables related to the DOX module +""" + import numba import xarray as xr -from clearwater_modules.shared.processes import ( - arrhenius_correction, -) +from clearwater_modules.shared.processes import arrhenius_correction +import math #TODO: make sure np.exp will work here... @numba.njit def pwv( - t_water_k: xr.DataArray + TwaterK: xr.DataArray ) -> xr.DataArray: """Calculate partial pressure of water vapor Args: - t_water_k: Water temperature kelvin + TwaterK: Water temperature kelvin """ - return np.exp(11.8571 - 3840.70 / t_water_k - 216961 / t_water_k ** 2) + return np.exp(11.8571 - 3840.70 / TwaterK - 216961 / TwaterK ** 2) @numba.njit def DOs_atm_alpha( - t_water_k: xr.DataArray + TwaterK: xr.DataArray ) -> xr.DataArray: """Calculate DO saturation atmospheric correction coefficient Args: - t_water_k: Water temperature kelvin + TwaterK: Water temperature kelvin """ - return .000975 - 1.426 * 10 ** -5 * t_water_k + 6.436 * 10 ** -8 * t_water_k ** 2 + return .000975 - 1.426 * 10 ** -5 * TwaterK + 6.436 * 10 ** -8 * TwaterK ** 2 @numba.njit def DOX_sat( - t_water_k: xr.DataArray, - patm: xr.DataArray, + TwaterK: xr.DataArray, + pressure_atm: xr.DataArray, pwv: xr.DataArray, DOs_atm_alpha: xr.DataArray ) -> xr.DataArray: """Calculate DO saturation value Args: - t_water_k: Water temperature kelvin - patm: Atmospheric pressure (atm) + TwaterK: Water temperature kelvin + pressure_atm: Atmospheric pressure (atm) pwv: Patrial pressure of water vapor (atm) DOs_atm_alpha: DO saturation atmospheric correction coefficient """ - DOX_sat_uncorrected = np.exp(-139.34410 + 1.575701 * 10 ** 5 / t_water_k - 6.642308 * 10 ** 7 / t_water_k ** 2 - + 1.243800 * 10 ** 10 / t_water_k - 8.621949 * 10 ** 11 / t_water_k) + DOX_sat_uncorrected = np.exp(-139.34410 + 1.575701 * 10 ** 5 / TwaterK - 6.642308 * 10 ** 7 / TwaterK ** 2 + + 1.243800 * 10 ** 10 / TwaterK - 8.621949 * 10 ** 11 / TwaterK) - DOX_sat_corrected = DOX_sat_uncorrected * patm * \ - (1 - pwv / patm) * (1 - DOs_atm_alpha * patm) / \ + DOX_sat_corrected = DOX_sat_uncorrected * pressure_atm * \ + (1 - pwv / pressure_atm) * (1 - DOs_atm_alpha * pressure_atm) / \ ((1 - pwv) * (1 - DOs_atm_alpha)) return DOX_sat_corrected -def kah_20( - kah_20_user: xr.DataArray, - hydraulic_reaeration_option: xr.DataArray, - velocity: xr.DataArray, - depth: xr.DataArray, - flow: xr.DataArray, - topwidth: xr.DataArray, - slope: xr.DataArray, - shear_velocity: xr.DataArray -) -> xr.DataArray: - """Calculate hydraulic oxygen reaeration rate based on flow parameters in different cells - - Args: - kah_20_user: User defined O2 reaeration rate at 20 degrees (1/d) - hydraulic_reaeration_option: Integer value which selects method for computing O2 reaeration rate - velocity: Average water velocity in cell (m/s) - depth: Average water depth in cell (m) - flow: Average flow rate in cell (m3/s) - topwidth: Average topwidth of cell (m) - slope: Average slope of bottom surface - shear_velocity: Average shear velocity on bottom surface (m/s) - """ - - da: xr.DataArray = xr.where(hydraulic_reaeration_option == 1, kah_20_user, - xr.where(hydraulic_reaeration_option == 2, (3.93 * velocity**0.5) / (depth**1.5), - xr.where(hydraulic_reaeration_option == 3, (5.32 * velocity**0.67) / (depth**1.85), - xr.where(hydraulic_reaeration_option == 4, (5.026 * velocity) / (depth**1.67), - xr.where(hydraulic_reaeration_option == 5, xr.where(depth < 0.61, (5.32 * velocity**0.67) / (depth**1.85), xr.where(depth > 0.61, (3.93 * velocity**0.5) / (depth**1.5), (5.026 * velocity) / (depth**1.67))), - xr.where(hydraulic_reaeration_option == 6, xr.where(flow < 0.556, 517 * (velocity * slope)**0.524 * flow**-0.242, 596 * (velocity * slope)**0.528 * flow**-0.136), - xr.where(hydraulic_reaeration_option == 7, xr.where(flow < 0.556, 88 * (velocity * slope)**0.313 * depth**-0.353, 142 * (velocity * slope)**0.333 * depth**-0.66 * topwidth**-0.243), - xr.where(hydraulic_reaeration_option == 8, xr.where(flow < 0.425, 31183 * velocity * slope, 15308 * velocity * slope), - xr.where(hydraulic_reaeration_option == 9, 2.16 * (1 + 9 * (velocity / (9.81 * depth)**0.5)**0.25) * shear_velocity / depth, -9999 - ))))))))) - return da - - -@numba.njit -def kah_T( - water_temp_c: xr.DataArray, - kah_20: xr.DataArray, - theta: xr.DataArray -) -> xr.DataArray: - """Calculate the temperature adjusted hydraulic oxygen reaeration rate (/d) - - Args: - water_temp_c: Water temperature in Celsius - kah_20: Hydraulic oxygen reaeration rate at 20 degrees Celsius - theta: Arrhenius coefficient - """ - return arrhenius_correction(water_temp_c, kah_20, theta) - - -def kaw_20( - kaw_20_user: xr.DataArray, - wind_speed: xr.DataArray, - wind_reaeration_option: xr.DataArray -) -> xr.DataArray: - """Calculate the wind oxygen reaeration velocity (m/d) based on wind speed, r stands for regional - - Args: - kaw_20_user: User defined wind oxygen reaeration velocity at 20 degrees C (m/d) - wind_speed: Wind speed at 10 meters above the water surface (m/s) - wind_reaeration_option: Integer value which selects method for computing wind oxygen reaeration velocity - """ - Uw10 = wind_speed * (10 / 2)**0.143 - - da: xr.DataArray = xr.where(wind_reaeration_option == 1, kaw_20_user, - xr.where(wind_reaeration_option == 2, 0.864 * Uw10, - xr.where(wind_reaeration_option == 3, xr.where(Uw10 <= 3.5, 0.2 * Uw10, 0.057 * Uw10**2), - xr.where(wind_reaeration_option == 4, 0.728 * Uw10**0.5 - 0.317 * Uw10 + 0.0372 * Uw10**2, - xr.where(wind_reaeration_option == 5, 0.0986 * Uw10**1.64, - xr.where(wind_reaeration_option == 6, 0.5 + 0.05 * Uw10**2, - xr.where(wind_reaeration_option == 7, xr.where(Uw10 <= 5.5, 0.362 * Uw10**0.5, 0.0277 * Uw10**2), - xr.where(wind_reaeration_option == 8, 0.64 + 0.128 * Uw10**2, - xr.where(wind_reaeration_option == 9, xr.where(Uw10 <= 4.1, 0.156 * Uw10**0.63, 0.0269 * Uw10**1.9), - xr.where(wind_reaeration_option == 10, 0.0276 * Uw10**2, - xr.where(wind_reaeration_option == 11, 0.0432 * Uw10**2, - xr.where(wind_reaeration_option == 12, 0.319 * Uw10, - xr.where(wind_reaeration_option == 13, xr.where(Uw10 < 1.6, 0.398, 0.155 * Uw10**2), -9999 - ))))))))))))) - - return da - - -@numba.njit -def kaw_T( - water_temp_c: xr.DataArray, - kaw_20: xr.DataArray, - theta: xr.DataArray -) -> xr.DataArray: - """Calculate the temperature adjusted wind oxygen reaeration velocity (m/d) - - Args: - water_temp_c: Water temperature in Celsius - kaw_20: Wind oxygen reaeration velocity at 20 degrees Celsius - theta: Arrhenius coefficient - """ - return arrhenius_correction(water_temp_c, kaw_20, theta) - - -@numba.njit -def ka_T( - kah_T: xr.DataArray, - kaw_T: xr.DataArray, - depth: xr.DataArray -) -> xr.DataArray: - """Compute the oxygen reaeration rate, adjusted for temperature (1/d) - - Args: - kah_T: Oxygen reaeration rate adjusted for temperature (1/d) - kaw_T: Wind oxygen reaeration velocity adjusted for temperature (m/d) - depth: Average water depth in cell (m) - """ - return kaw_T / depth + kah_T - - @numba.njit def Atm_O2_reaeration( - ka_T: xr.DataArray, + ka_tc: xr.DataArray, DOX_sat: xr.DataArray, DOX: xr.DataArray ) -> xr.DataArray: """Compute the atmospheric O2 reaeration flux Args: - ka_T: Oxygen reaeration rate adjusted for temperature (1/d) + ka_tc: Oxygen reaeration rate adjusted for temperature (1/d) DOX_sat: Dissolved oxygen saturation concentration (mg/L) DOX: Dissolved oxygen concentration (mg/L) """ - return ka_T * (DOX_sat - DOX) + return ka_tc * (DOX_sat - DOX) def DOX_ApGrowth( ApGrowth: xr.DataArray, rca: xr.DataArray, roc: xr.DataArray, - F1: xr.DataArray, + ApUptakeFr_NH4: xr.DataArray, use_Algae: xr.DataArray ) -> xr.DataArray: """Compute DOX flux due to algal photosynthesis @@ -200,9 +86,9 @@ def DOX_ApGrowth( ApGrowth: Algae photosynthesis, calculated in the algae module (ug-Chla/L/d) rca: Ratio of algal carbon to chlorophyll-a (mg-C/ug-Chla) roc: Ratio of oxygen to carbon for carbon oxidation (mg-O2/mg-C) - F1: Algae preference for ammonia + ApUptakeFr_NH4: Fraction of actual algal uptake that is from the ammonia pool, calculated in nitrogen module """ - da: xr.DataArray = xr.where(use_Algae == True, ApGrowth * rca * roc * (138 / 106 - 32 * F1 / 106), 0) + da: xr.DataArray = xr.where(use_Algae == True, ApGrowth * rca * roc * (138 / 106 - 32 * ApUptakeFr_NH4 / 106), 0) return da @@ -289,7 +175,7 @@ def DOX_AbGrowth( """Compute dissolved oxygen flux due to benthic algae growth Args: - AbUptakeFr_NH4: Fraction of actual benthic algal uptake that is form the ammonia pool, calculated in nitrogen module + AbUptakeFr_NH4: Fraction of actual benthic algal uptake that is from the ammonia pool, calculated in nitrogen module roc: Ratio of oxygen to carbon for carbon oxidation (mg-O2/mg-C) rcb: Benthic algae carbon to dry weight ratio (mg-C/mg-D) AbGrowth: Benthic algae photosynthesis, calculated in benthic algae module (mg/L/d) @@ -326,29 +212,6 @@ def DOX_AbRespiration( return da -def SOD_tc( - SOD_20: xr.DataArray, - t_water_C: xr.DataArray, - theta: xr.DataArray, - DOX: xr.DataArray, - KsSOD: xr.DataArray, - use_DOX: xr.DataArray -) -> xr.DataArray: - """Compute the sediment oxygen demand corrected by temperature and dissolved oxygen concentration - - Args: - SOD_20: Sediment oxygen demand at 20 degrees celsius (mg-O2/m2) - t_water_C: Water temperature in degrees C - theta: Arrhenius coefficient - use_DOX: Option to consider DOX concentration in water in calculation of sediment oxygen demand - """ - SOD_tc = arrhenius_correction(t_water_C, SOD_20, theta) - - da: xr.DataArray = xr.where(use_DOX == True, SOD_tc * DOX / (DOX + KsSOD), SOD_tc) - - return da - - def DOX_SOD( SOD_Bed: xr.DataArray, depth: xr.DataArray, @@ -397,7 +260,7 @@ def dDOXdt( @numba.njit -def DOX_new( +def DOX( DOX: xr.DataArray, dDOXdt: xr.DataArray, timestep: xr.DataArray diff --git a/src/clearwater_modules/nsm1/DOX/static_variables.py b/src/clearwater_modules/nsm1/DOX/static_variables.py index a416189..2a3397c 100644 --- a/src/clearwater_modules/nsm1/DOX/static_variables.py +++ b/src/clearwater_modules/nsm1/DOX/static_variables.py @@ -1,52 +1,24 @@ -# TODO: figure out what 'model' to import +""" +File contains static variables related to the DOX module +""" + import clearwater_modules.base as base -from clearwater_modules.tsm.model import EnergyBudget +from clearwater_modules.nsm1.model import NutrientBudget -@base.register_variable(models=EnergyBudget) +@base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... Variable( - name='kaw_20_user', - long_name='Wind oxygen reaeration velocity at 20C', - units='m/d', - description='Wind oxygen reaeration velocity at 20C', - use='static' -) - -Variable( - name='kah_20_user', - long_name='Hydraulic oxygen reaeration rate at 20C', - units='1/d', - description='Hydraulic oxygen reaeration rate at 20C', - use='static' -) - -Variable( - name='hydraulic_reaeration_option', - long_name='Option for chosing the method by which O2 reaeration rate is calculated', - units='unitless', - description='Selects method for computing O2 reaeration rate', - use='static' -) - -Variable( - name='wind_reaeration_option', - long_name='Option for chosing the method by which wind reaeration is calculated', - units='unitless', - description='Selects method for computing O2 reaeration due to wind', + name='ron', + long_name='O2:N ratio for nitrification', + units='mg-O2/mg-N', + description='2*32/14', use='static' ) -Variable( - name='patm', - long_name='Atmospheric pressure', - units='atm', - description='Atmospheric pressure', - use='static' -) Variable( name='KsSOD', diff --git a/src/clearwater_modules/nsm1/POM/dynamic_variables.py b/src/clearwater_modules/nsm1/POM/dynamic_variables.py index 7f7a820..7df4dbf 100644 --- a/src/clearwater_modules/nsm1/POM/dynamic_variables.py +++ b/src/clearwater_modules/nsm1/POM/dynamic_variables.py @@ -1,39 +1,20 @@ -import clearwater_modules.shared.processes as shared_processes from clearwater_modules import base -from clearwater_modules.nsm1.carbon.model import CarbonBudget -from clearwater_modules.nsm1.POM import processes +from clearwater_modules.nsm1.model import NutrientBudget +import clearwater_modules.nsm1.POM.processes as processes +import clearwater_modules.shared.processes as shared_processes -@base.register_variable(models=CarbonBudget) +@base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... - -Variable( - name='kdb_T', - long_name='Benthic algal mortality rate adjusted for temperature', - units='1/d', - description='Benthic algal mortality rate adjusted for temperature', - use='dynamic', - process=processes.kdb_T -) - Variable( - name='kpom_T', + name='kpom_tc', long_name='POM dissolution rate adjusted for temperature', units='1/d', description='POM dissolution rate adjusted for temperature', use='dynamic', - process=processes.kpom_T -) - -Variable( - name='depth', - long_name='Water depth in computation cell', - units='m', - description='Water depth in computation cell', - use='dynamic', - process=shared_processes.compute_depth + process=processes.kpom_tc ) Variable( diff --git a/src/clearwater_modules/nsm1/POM/processes.py b/src/clearwater_modules/nsm1/POM/processes.py index 9e63194..eed5b1a 100644 --- a/src/clearwater_modules/nsm1/POM/processes.py +++ b/src/clearwater_modules/nsm1/POM/processes.py @@ -1,46 +1,23 @@ +""" +File contains process to calculate new POM concentration and associated dependent variables +""" import numba -import math -from clearwater_modules.shared.processes import ( - arrhenius_correction -) import xarray as xr -from clearwater_modules.nsm1.POM import dynamic_variables -from clearwater_modules.nsm1.POM import static_variables -from clearwater_modules.nsm1 import static_variables_global -from clearwater_modules.nsm1 import dynamic_variables_global -from clearwater_modules.nsm1 import state_variables - - -@numba.njit -def kdb_T( - water_temp_c: float, - kdb_20: float, - theta: float -) -> float: - """Calculate the temperature adjusted POC hydrolysis rate (/d) - - Args: - water_temp_c: Water temperature in Celsius - kdb_20: Benthic algae mortality rate at 20 degrees Celsius (1/d) - theta: Arrhenius coefficient for kdb - """ - return arrhenius_correction(water_temp_c, kdb_20, theta) - +from clearwater_modules.shared.processes import arrhenius_correction +import math @numba.njit -def kpom_T( - water_temp_c: float, +def kpom_tc( + TwaterC: float, kpom_20: float, - theta: float ) -> float: - """Calculate the temperature adjusted POC hydrolysis rate (/d) + """Calculate the temperature adjusted POM dissolution rate (1/d) Args: - water_temp_c: Water temperature in Celsius + TwaterC: Water temperature in Celsius kpom_20: POM dissolution rate at 20 degrees Celsius (1/d) - theta: Arrhenius coefficient for kpom """ - return arrhenius_correction(water_temp_c, kpom_20, theta) + return arrhenius_correction(TwaterC, kpom_20, 1.047) def POM_algal_settling( @@ -67,23 +44,23 @@ def POM_algal_settling( @numba.njit def POM_dissolution( POM: xr.DataArray, - kpom_T: xr.DataArray + kpom_tc: xr.DataArray ) -> xr.DataArray: """Calculates the particulate organic matter concentration change due to POM dissolution Args: POM: Concentration of particulate organic matter (mg/L) - kpom_T: POM dissolution rate corrected for temperature (1/d) + kpom_tc: POM dissolution rate corrected for temperature (1/d) """ - return POM * kpom_T + return POM * kpom_tc def POM_POC_settling( POC: xr.DataArray, vsoc: xr.DataArray, depth: xr.DataArray, - fcom: xr.DataArray, + focm: xr.DataArray, use_POC: xr.DataArray ) -> xr.DataArray: """Calculates particulate organic matter concentration change due to POM settling @@ -95,14 +72,14 @@ def POM_POC_settling( fcom: Fraction of carbon in organic matter (mg-C/mg-D) use_POC: Option to consider particulate organic carbon """ - da: xr.DataArray = xr.where(use_POC == True, vsoc * POC / depth / fcom, 0) + da: xr.DataArray = xr.where(use_POC == True, vsoc * POC / depth / focm, 0) return da def POM_benthic_algae_mortality( Ab: xr.DataArray, - kdb_T: xr.DataArray, + kdb_tc: xr.DataArray, Fb: xr.DataArray, Fw: xr.DataArray, depth: xr.DataArray, @@ -112,13 +89,13 @@ def POM_benthic_algae_mortality( Args: Ab: Benthic algae concentration (mg/L) - kdb_T: Benthic algae death rate (1/d) + kdb_tc: Benthic algae death rate (1/d) Fb: Fraction of bottom area available for benthic algae growth Fw: Fraction of benthic algae mortality into water column depth: Depth of water in computation cell (m) use_Balgae: Option for considering benthic algae in DOC budget (boolean) """ - da: xr.DataArray = xr.where(use_Balgae == True, Ab * kdb_T * Fb * (1 - Fw) / depth, 0) + da: xr.DataArray = xr.where(use_Balgae == True, Ab * kdb_tc * Fb * (1 - Fw) / depth, 0) return da @@ -160,7 +137,7 @@ def dPOMdt( @numba.njit -def POM_new( +def POM( dPOMdt: xr.DataArray, POM: xr.DataArray, timestep: xr.DataArray diff --git a/src/clearwater_modules/nsm1/POM/static_variables.py b/src/clearwater_modules/nsm1/POM/static_variables.py index 9eb0c9a..7645f64 100644 --- a/src/clearwater_modules/nsm1/POM/static_variables.py +++ b/src/clearwater_modules/nsm1/POM/static_variables.py @@ -1,20 +1,16 @@ +""" +File contains static variables related to the POM module +""" + import clearwater_modules.base as base -from clearwater_modules.tsm.model import EnergyBudget +from clearwater_modules.nsm1.model import NutrientBudget -@base.register_variable(models=EnergyBudget) +@base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... -Variable( - name='kdb_20', - long_name='Benthic algal mortality rate at 20C', - units='1/d', - description='Benthic algal mortality rate at 20C', - use='static', -) - Variable( name='kpom_20', long_name='POM dissolution rate at 20C', @@ -23,58 +19,3 @@ class Variable(base.Variable): use='static' ) -Variable( - name='rda', - long_name='Algal D:Chla Ratio', - units='mg-D/ug Chla', - description='Algal D:Chla Ratio', - use='static' -) - -Variable( - name='vsoc', - long_name='POC settling velocity', - units='m/d', - description='POC settling velocity', - use='static' -) - -Variable( - name='vsap', - long_name='Algal Setting Velocity', - units='m/d', - description='Algal Setting Velocity', - use='static', -) - -Variable( - name='Fw', - long_name='Fraction of benthic algae mortality into water column', - units='unitless', - description='Fraction of benthic algae mortality into water column', - use='static', -) - -Variable( - name='Fb', - long_name='Fraction of bottom area available for benthic algae', - units='unitless', - description='Fraction of bottom area available for benthic algae', - use='static', -) - -Variable( - name='fcom', - long_name='Fraction of carbon in organic matter', - units='mg-C/mg-D', - description='Fraction of carbon in organic matter', - use='static' -) - -Variable( - name='vb', - long_name='Burial velocity', - units='m/d', - description='Rate at which constituents are buried on the bottom', - use='static' -) diff --git a/src/clearwater_modules/nsm1/__init__.py b/src/clearwater_modules/nsm1/__init__.py index 1fa481c..a807441 100644 --- a/src/clearwater_modules/nsm1/__init__.py +++ b/src/clearwater_modules/nsm1/__init__.py @@ -21,6 +21,16 @@ Initial Version: June 5, 2021 """ +from clearwater_modules.nsm1 import algae +from clearwater_modules.nsm1 import alkalinity +from clearwater_modules.nsm1 import balgae +from clearwater_modules.nsm1 import carbon +from clearwater_modules.nsm1 import CBOD +from clearwater_modules.nsm1 import DOX +from clearwater_modules.nsm1 import nitrogen +from clearwater_modules.nsm1 import POM + + class NSM1: def __init__(self): pass diff --git a/src/clearwater_modules/nsm1/algae/TO_DELETE_2.py b/src/clearwater_modules/nsm1/algae/TO_DELETE_2.py deleted file mode 100644 index b1a05ce..0000000 --- a/src/clearwater_modules/nsm1/algae/TO_DELETE_2.py +++ /dev/null @@ -1,24 +0,0 @@ - -import numpy as np - -def KL01(var:int) -> int: - return var + 5 - - -def RUN_SCRIPT() -> np.array: - """Calculate krp_tc (1/d). - - Args: - TwaterC: Water temperature (C) - krp: Algal respiration rate at 20 degree (1/d) - """ - to_return=0 - for i in range(1,10) : - - to_return = KL01(to_return) - print(to_return) - return to_return - - -if __name__ == "__main__": - RUN_SCRIPT() \ No newline at end of file diff --git a/src/clearwater_modules/nsm1/algae/dynamic_variables_algae.py b/src/clearwater_modules/nsm1/algae/dynamic_variables.py similarity index 78% rename from src/clearwater_modules/nsm1/algae/dynamic_variables_algae.py rename to src/clearwater_modules/nsm1/algae/dynamic_variables.py index 992c5b6..029d534 100644 --- a/src/clearwater_modules/nsm1/algae/dynamic_variables_algae.py +++ b/src/clearwater_modules/nsm1/algae/dynamic_variables.py @@ -1,25 +1,24 @@ """ -File includes dynamic variables computed in Algae module. Dynamic variables may be accessed by other modules. +File contains dynamic variables related to the Algae module """ import clearwater_modules.shared.processes as shared_processes from clearwater_modules import base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.algae.algae_processes as algae_processes +import clearwater_modules.nsm1.algae.processes as processes @base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... - Variable( name='rna', long_name='Algal N:Chla Ratio', units='mg-N/ug Chla', description='Algal N:Chla Ratio', use='dynamic', - process=algae_processes.rna + process=processes.rna ) Variable( @@ -28,7 +27,7 @@ class Variable(base.Variable): units='mg-P/ug Chla', description='Algal P:Chla Ratio', use='dynamic', - process=algae_processes.rpa + process=processes.rpa ) Variable( @@ -37,7 +36,7 @@ class Variable(base.Variable): units='mg-C/ug Chla', description='Algal C:Chla Ratio', use='dynamic', - process=algae_processes.rca + process=processes.rca ) Variable( @@ -46,7 +45,7 @@ class Variable(base.Variable): units='mg-D/ug Chla', description='Algal D:Chla Ratio', use='dynamic', - process=algae_processes.rda + process=processes.rda ) Variable( @@ -55,7 +54,7 @@ class Variable(base.Variable): units='1/d', description='Max Algae Growth with Temperature Correction', use='dynamic', - process=algae_processes.mu_max_tc, + process=processes.mu_max_tc, ) Variable( @@ -64,7 +63,7 @@ class Variable(base.Variable): units='1/d', description='Algal Respiration Rate with Temperature Correction', use='dynamic', - process=algae_processes.krp_tc, + process=processes.krp_tc, ) Variable( @@ -73,7 +72,7 @@ class Variable(base.Variable): units='1/d', description='Algal Mortality Rate with Temperature Correction', use='dynamic', - process=algae_processes.kdp_tc, + process=processes.kdp_tc, ) Variable( @@ -82,7 +81,7 @@ class Variable(base.Variable): units='unitless', description='Algal Light Limitation', use='dynamic', - process=algae_processes.FL, + process=processes.FL, ) Variable( @@ -91,7 +90,7 @@ class Variable(base.Variable): units='unitless', description='Algal Nitrogen Limitation', use='dynamic', - process=algae_processes.FN, + process=processes.FN, ) Variable( @@ -100,7 +99,7 @@ class Variable(base.Variable): units='unitless', description='Algal Phosphorus Limitation', use='dynamic', - process=algae_processes.FP, + process=processes.FP, ) Variable( @@ -109,7 +108,7 @@ class Variable(base.Variable): units='1/d', description='Algal Growth Rate', use='dynamic', - process=algae_processes.mu, + process=processes.mu, ) Variable( @@ -118,7 +117,7 @@ class Variable(base.Variable): units='ug-Chala/L/d', description='Algal Growth', use='dynamic', - process=algae_processes.ApGrowth, + process=processes.ApGrowth, ) Variable( @@ -127,7 +126,7 @@ class Variable(base.Variable): units='ug-Chala/L/d', description='Algal Respiration', use='dynamic', - process=algae_processes.ApRespiration, + process=processes.ApRespiration, ) Variable( @@ -136,7 +135,7 @@ class Variable(base.Variable): units='ug-Chala/L/d', description='Algal Death', use='dynamic', - process=algae_processes.ApDeath, + process=processes.ApDeath, ) Variable( @@ -145,7 +144,7 @@ class Variable(base.Variable): units='ug-Chala/L/d', description='Algal Settling', use='dynamic', - process=algae_processes.ApSettling, + process=processes.ApSettling, ) Variable( @@ -154,14 +153,14 @@ class Variable(base.Variable): units='ug-Chala/L/d', description='Algal Biomass Concentration Change', use='dynamic', - process=algae_processes.dApdt, + process=processes.dApdt, ) Variable( - name='Ap_new', + name='Ap', long_name='New algal biomass concentration', units='ug-Chala/L/d', description='New algal biomass concentration', use='dynamic', - process=algae_processes.Ap_new, + process=processes.Ap, ) diff --git a/src/clearwater_modules/nsm1/algae/algae_processes.py b/src/clearwater_modules/nsm1/algae/processes.py similarity index 74% rename from src/clearwater_modules/nsm1/algae/algae_processes.py rename to src/clearwater_modules/nsm1/algae/processes.py index 65adcdf..e232666 100644 --- a/src/clearwater_modules/nsm1/algae/algae_processes.py +++ b/src/clearwater_modules/nsm1/algae/processes.py @@ -1,12 +1,10 @@ """ File contains process to calculate new algae biomass concentration and associated dependent variables """ - -# TODO calculate lambda? -import math -from clearwater_modules.shared.processes import arrhenius_correction import numba import xarray as xr +from clearwater_modules.shared.processes import arrhenius_correction +import math @numba.njit @@ -132,36 +130,17 @@ def FL( """ KEXT = L * depth - sqrt1 = 0.0 - sqrt2 = 0.0 - - FL0 = xr.where(Ap <= 0.0 | KEXT <= 0.0 | PAR <= 0.0, 0, -1) # After sunset or if there is no algae present - FL1= xr.where(FL0<0 | light_limitation_option>0 | light_limitation_option <2, (1.0 / KEXT) * math.log((KL + PAR) /(KL + PAR * math.exp(-KEXT))),-1) # Half-saturation formulation - FL2= xr.where(FL0<0 | light_limitation_option>1 | light_limitation_option <3, (1.0 / KEXT) * math.log((KL + PAR) /(KL + PAR * math.exp(-KEXT)))) # Half-saturation formulation - - elif light_limitation_option == 2: - # Smith's model - if abs(KL) < 1.0E-10: - FL = 1.0 - else: - sqrt1 = (1.0 + (PAR / KL)**2.0)**0.5 - sqrt2 = (1.0 + (PAR * math.exp(-KEXT) / KL)**2.0)**0.5 - FL = (1.0 / KEXT) * math.log((PAR / KL + sqrt1) / - (PAR * math.exp(-KEXT) / KL + sqrt2)) - elif light_limitation_option == 3: - # Steele's model - if abs(KL) < 1.0E-10: - FL = 0.0 - else: - FL = (2.718/KEXT) * (math.exp(-PAR/KL * - math.exp(-KEXT)) - math.exp(-PAR/KL)) - - # Limit factor to between 0.0 and 1.0. - # This should never happen, but it would be a mess if it did. - if FL > 1.0: - FL = 1.0 - if FL < 0.0: - FL = 0.0 + + FL = xr.where(Ap <= 0.0 or KEXT <= 0.0 or PAR <= 0.0, 0, + xr.where(light_limitation_option==1, (1.0 / KEXT) * math.log((KL + PAR) /(KL + PAR * math.exp(-KEXT))), + xr.where(light_limitation_option==2, + xr.where(abs(KL)<0.0000000001, 1, (1.0 / KEXT) * math.log( (PAR / KL + ((1.0 + (PAR / KL)**2.0)**0.5)) / (PAR * math.exp(-KEXT) / KL + ((1.0 + (PAR * math.exp(-KEXT) / KL)**2.0)**0.5)))), + xr.where(light_limitation_option==3, + xr.where(abs(KL)<0.0000000001,0,(2.718/KEXT) * (math.exp(-PAR/KL * math.exp(-KEXT)) - math.exp(-PAR/KL))), "NaN")))) + + + FL= xr.where(FL > 1.0, 1.0, + xr.where(FL<0.0, 0.0, FL)) return FL @@ -185,14 +164,9 @@ def FN( KsN: Michaelis-Menton half-saturation constant relating inorganic N to algal growth (mg-N/L) """ - if use_NH4 or use_NO3: - FN = (NH4 + NO3) / (KsN + NH4 + NO3) - if math.isnan(FN): - FN = 0.0 - if FN > 1.0: - FN = 1.0 - else: - FN = 1.0 + FN = xr.where(use_NH4 or use_NO3, (NH4 + NO3) / (KsN + NH4 + NO3), 1) + FN = xr.where(math.isnan(FN), 0, + xr.where(FN>1.0,1.0,FN)) return FN @@ -213,15 +187,9 @@ def FP( fdp: Fraction P dissolved (unitless) """ - if use_TIP: - - FP = fdp * TIP / (KsP + fdp * TIP) - if math.isnan(FP): - FP = 0.0 - if FP > 1.0: - FP = 1.0 - else: - FP = 1.0 + FP = xr.where(use_TIP, fdp * TIP / (KsP + fdp * TIP), 1.0) + FP = xr.where(math.isnan(FP), 0, + xr.where(FP>1.0, 1, FP)) return FP @@ -245,20 +213,10 @@ def mu( FN: Algae nitrogen limitation factor (unitless) """ - if growth_rate_option == 1: - # (1) Multiplicative (day-1) - mu = mu_max_tc * FL * FP * FN - elif growth_rate_option == 2: - # (2) Limiting nutrient (day-1) - mu = mu_max_tc * FL * min(FP, FN) - elif growth_rate_option == 3: - # (3) Harmonic Mean Option (day-1) - if FN == 0.0 or FP == 0.0: - mu = 0.0 - else: - mu = mu_max_tc * FL * 2.0 / (1.0 / FN + 1.0 / FP) - - return mu + return xr.where(growth_rate_option == 1, mu_max_tc * FL * FP * FN, + xr.where(growth_rate_option == 2, mu_max_tc * FL * min(FP, FN), + xr.where(growth_rate_option == 3, + xr.where(FN==0.0 or FP==0.0, 0.0, mu_max_tc * FL * 2.0 / (1.0 / FN + 1.0 / FP)), "NaN"))) @numba.njit @@ -341,14 +299,16 @@ def dApdt( @numba.njit -def Ap_new( +def Ap( Ap: xr.DataArray, dApdt: xr.DataArray, + timestep: xr.DataArray ) -> xr.DataArray: """Calculate new algae concentration (ug-Chla/L) Args: Ap: Initial algae biomass concentration (ug-Chla/L) dApdt: Change in algae biomass concentration (ug-Chla/L/d) + timestep: current iteration timestep (d) """ - return Ap + dApdt + return Ap + dApdt*timestep diff --git a/src/clearwater_modules/nsm1/algae/static_variables_algae.py b/src/clearwater_modules/nsm1/algae/static_variables.py similarity index 74% rename from src/clearwater_modules/nsm1/algae/static_variables_algae.py rename to src/clearwater_modules/nsm1/algae/static_variables.py index efa1522..7b0c027 100644 --- a/src/clearwater_modules/nsm1/algae/static_variables_algae.py +++ b/src/clearwater_modules/nsm1/algae/static_variables.py @@ -1,22 +1,18 @@ """ -File includes static variables only used in Algae module +File contains static variables related to the Algae module """ import clearwater_modules.base as base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.algae.algae_processes as algae_processes @base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... -# TODO: verify all these values - -# Only Algae Variables Variable( - name='Awd', + name='AWd', long_name='Algal Dry Weight', units='mg', description='Algal Dry Weight', @@ -121,17 +117,58 @@ class Variable(base.Variable): ) Variable( - name='growth_rate_option', - long_name='Growth Rate Option', + name='light_limitation_option', + long_name='Light Limitation Option', units='1/d', - description='Algal growth rate option 1) multiplicative, 2) Limiting Nutrient, 3) Harmonic Mean Option', + description='Algal light limitation 1) half-saturation, 2) Smith model, 3) Steele model', + use='static', +) + + +Variable( + name='lambda0', + long_name='lambda0', + units='1/m', + description='background portion', use='static', ) Variable( - name='light_limitation_option', - long_name='Light Limitation Option', - units='1/d', - description='Algal light limitation 1) half-saturation, 2) Smith model, 3) Steele model', + name='lambda1', + long_name='lambda1', + units='1/m/(ug Chla/L)', + description='linear self shading', use='static', ) + +Variable( + name='lambda2', + long_name='lambda2', + units='unitless', + description='nonlinear', + use='static', +) + +Variable( + name='lambdas', + long_name='lambdas', + units='L/mg/m', + description='ISS portion', + use='static', +) + +Variable( + name='lambdam', + long_name='lambdam', + units='L/mg/m', + description='POM portion', + use='static', +) + +Variable( + name='Fr_PAR', + long_name='fraction PAR', + units='unitless', + description='fraction of solar radiation within the PAR of the spectrum', + use='static', +) \ No newline at end of file diff --git a/src/clearwater_modules/nsm1/alkalinity/dynamic_variables.py b/src/clearwater_modules/nsm1/alkalinity/dynamic_variables.py index 698693e..29da0ab 100644 --- a/src/clearwater_modules/nsm1/alkalinity/dynamic_variables.py +++ b/src/clearwater_modules/nsm1/alkalinity/dynamic_variables.py @@ -1,43 +1,17 @@ -# TODO: figure out imports +""" +File contains dynamic variables related to the alkalinity module +""" import clearwater_modules.shared.processes as shared_processes from clearwater_modules import base -from clearwater_modules.nsm1.carbon.model import CarbonBudget -from clearwater_modules.nsm1.alkalinity import processes +from clearwater_modules.nsm1.model import NutrientBudget +import clearwater_modules.nsm1.alkalinity.processes as processes -@base.register_variable(models=CarbonBudget) +@base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... - -Variable( - name='depth', - long_name='Average water depth in cell', - units='m', - description='Average water depth in cell computed by dividing volume by surface area', - use='dynamic', - process=shared_processes.compute_depth -) - -Variable( - name='knit_T', - long_name='Nitrification rate corrected for temperature', - units='1/d', - description='Nitrification rate corrected for temperature', - use='dynamic', - process=processes.knit_T -) - -Variable( - name='kdnit_T', - long_name='Denitrification rate corrected for temperature', - units='1/d', - description='Denitrification rate corrected for temperature', - use='dynamic', - process=processes.kdnit_T -) - Variable( name='Alk_denitrification', long_name='Alkalinity change due to denitrification', @@ -101,3 +75,5 @@ class Variable(base.Variable): process=processes.dAlkdt ) + + diff --git a/src/clearwater_modules/nsm1/alkalinity/processes.py b/src/clearwater_modules/nsm1/alkalinity/processes.py index bd8b77d..2da53bd 100644 --- a/src/clearwater_modules/nsm1/alkalinity/processes.py +++ b/src/clearwater_modules/nsm1/alkalinity/processes.py @@ -1,49 +1,16 @@ +""" +File contains process to calculate new alkalinity concentration and associated dependent variables +""" + import numba -import math -from clearwater_modules.shared.processes import ( - arrhenius_correction -) import xarray as xr -from clearwater_modules.nsm1.alkalinity import dynamic_variables -from clearwater_modules.nsm1.alkalinity import static_variables -from clearwater_modules.nsm1 import static_variables_global -from clearwater_modules.nsm1 import dynamic_variables_global -from clearwater_modules.nsm1 import state_variables - -@numba.njit -def kdnit_T( ##Theta variable?? - TwaterC: float, - kdnit_20: float -) -> float: - """Calculate kdnit_T: Denitrification rate temperature correction (1/d). #TODO only if use_NO3 = true - - Args: - TwaterC: Water temperature (C) - kdnit_20: Denitrification rate (1/d) - """ - - return arrhenius_correction(TwaterC, kdnit_20, 1.045) - - -@numba.njit -def knit_T( ##Theta variable?? - TwaterC: float, - knit_20: float -) -> float: - """Calculate knit_T: Denitrification rate temperature correction (1/d). #TODO only if use_NO3 = true - - Args: - TwaterC: Water temperature (C) - knit_20: Nitrification rate (1/d) - """ - - return arrhenius_correction(TwaterC, knit_20, 1.045) - +from clearwater_modules.shared.processes import arrhenius_correction +import math def Alk_denitrification( DOX: xr.DataArray, NO3: xr.DataArray, - kdnit_T: xr.DataArray, + kdnit_tc: xr.DataArray, KsOxdn: xr.DataArray, r_alkden: xr.DataArray, use_NO3: xr.DataArray, @@ -54,13 +21,13 @@ def Alk_denitrification( Args: DOX: Concentration of dissolved oxygen (mg/L) NO3: Concentration of nitrate (mg/L) - kdnit_T: Denitrification rate corrected for temperature (1/d) + kdnit_tc: Denitrification rate corrected for temperature (1/d) KsOxdn: Half-saturation oxygen inhibition constant for denitrification (mg-O2/L) ralkden: Ratio translating NO3 denitrification into Alk (eq/mg-N) use_NO3: Option to use nitrate use_DOX: Option to use dissolved oxygen """ - da: xr.DataArray = xr.where(use_NO3 == True, xr.where(use_DOX == True, r_alkden * (1.0 - (DOX / (DOX + KsOxdn))) * kdnit_T * NO3, r_alkden * kdnit_T * NO3), 0) + da: xr.DataArray = xr.where(use_NO3 == True, xr.where(use_DOX == True, r_alkden * (1.0 - (DOX / (DOX + KsOxdn))) * kdnit_tc * NO3, r_alkden * kdnit_tc * NO3), 0) return da @@ -68,7 +35,7 @@ def Alk_denitrification( def Alk_nitrification( DOX: xr.DataArray, NH4: xr.DataArray, - knit_T: xr.DataArray, + knit_tc: xr.DataArray, KNR: xr.DataArray, r_alkn: xr.DataArray, use_NH4: xr.DataArray, @@ -79,13 +46,13 @@ def Alk_nitrification( Args: DOX: Concentration of dissolved oxygen (mg/L) NH4: Concentration of ammonia/ammonium (mg/L) - knit_T: Nitrification rate corrected for temperature (1/d) + knit_tc: Nitrification rate corrected for temperature (1/d) KNR: Oxygen inhibition factor for nitrification (mg-O2/L) r_alkn: Ratio translating NH4 nitrification into Alk (eq/mg-N) use_NH4: Option to use ammonium use_DOX: Option to use dissolved oxygen """ - da: xr.DataArray = xr.where(use_NH4 == True, xr.where(use_DOX == True, r_alkn * (1 - math.exp(-KNR * DOX)) * knit_T * NH4, knit_T * NH4), 0) + da: xr.DataArray = xr.where(use_NH4 == True, xr.where(use_DOX == True, r_alkn * (1 - math.exp(-KNR * DOX)) * knit_tc * NH4, knit_tc * NH4), 0) return da @@ -94,7 +61,7 @@ def Alk_algal_growth( ApGrowth: xr.DataArray, r_alkaa: xr.DataArray, r_alkan: xr.DataArray, - F1: xr.DataArray, + ApUptakeFr_NH4 : xr.DataArray, use_Algae: xr.DataArray ) -> xr.DataArray: """Calculate the alkalinity concentration change due to algal growth @@ -103,10 +70,10 @@ def Alk_algal_growth( ApGrowth: Algal photosynthesis calculated in algae module (ug-Chla/L/d) r_alkaa: Ratio translating algal growth into Alk if NH4 is the N source (eq/ug-Chla) r_alkan: Ratio translating algal growth into Alk if NO3 is the N source (eq/ug-Chla) - F1: Preference fraction of algal N uptake from NH4 + ApUptakeFr_NH4 : Preference fraction of algal N uptake from NH4 use_Algae: Option to use algae """ - da: xr.DataArray = xr.where(use_Algae == True, (r_alkaa * F1 - r_alkan * (1 - F1)) * ApGrowth, 0) + da: xr.DataArray = xr.where(use_Algae == True, (r_alkaa * ApUptakeFr_NH4 - r_alkan * (1 - ApUptakeFr_NH4 )) * ApGrowth, 0) return da @@ -133,7 +100,7 @@ def Alk_benthic_algae_growth( depth: xr.DataArray, r_alkba: xr.DataArray, r_alkbn: xr.DataArray, - F2: xr.DataArray, + AbUptakeFr_NH4 : xr.DataArray, Fb: xr.DataArray, use_Balgae: xr.DataArray ) -> xr.DataArray: @@ -144,11 +111,11 @@ def Alk_benthic_algae_growth( depth: Depth of water (m) r_alkaa: Ratio translating algal growth into Alk if NH4 is the N source (eq/ug-Chla) r_alkan: Ratio translating algal growth into Alk if NO3 is the N source (eq/ug-Chla) - F2: Preference fraction of benthic algae N uptake from NH4 + AbUptakeFr_NH4 : Preference fraction of benthic algae N uptake from NH4 Fb: Fraction of bottom area available for benthic algae growth use_Balgae: Option to use benthic algae """ - da: xr.DataArray = xr.where(use_Balgae == True, (1 / depth) *(r_alkba * F2 - r_alkbn * (1 - F2)) * AbGrowth * Fb, 0) + da: xr.DataArray = xr.where(use_Balgae == True, (1 / depth) *(r_alkba * AbUptakeFr_NH4 - r_alkbn * (1 - AbUptakeFr_NH4 )) * AbGrowth * Fb, 0) return da @@ -196,7 +163,7 @@ def dAlkdt( @numba.njit -def Alk_new( +def Alk( Alk: xr.DataArray, dAlkdt: xr.DataArray, timestep: xr.DataArray, diff --git a/src/clearwater_modules/nsm1/alkalinity/static_variables.py b/src/clearwater_modules/nsm1/alkalinity/static_variables.py index e587f93..2cce845 100644 --- a/src/clearwater_modules/nsm1/alkalinity/static_variables.py +++ b/src/clearwater_modules/nsm1/alkalinity/static_variables.py @@ -1,43 +1,16 @@ +""" +File contains static variables related to the Alkalinity module +""" + import clearwater_modules.base as base -from clearwater_modules.tsm.model import EnergyBudget +from clearwater_modules.nsm1.model import NutrientBudget -@base.register_variable(models=EnergyBudget) +@base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... -Variable( - name='KsOxdn', - long_name='Half-saturation oxygen inhibition constant for denitrification', - units='mg-O2/L', - description='Half-saturation oxygen inhibition constant for denitrification', - use='static', -) - -Variable( - name='kdnit_20', - long_name='Denitrification rate at 20C', - units='1/d', - description='Denitrification rate at 20C', - use='static', -) - -Variable( - name='knit_20', - long_name='Nitrification Rate Ammonia decay at 20C', - units='1/d', - description='Nitrification Rate Ammonia NH4 -> NO3 decay at 20C', - use='static', -) - -Variable( - name='KNR', - long_name='Oxygen inhabitation factor for nitrification', - units='mg-O2/L', - description='Oxygen inhabitation factor for nitrification', - use='static', -) Variable( name='r_alkaa', @@ -86,28 +59,3 @@ class Variable(base.Variable): description='Ratio translating benthic algae growth into Alk if NO3 is the N source', use='static' ) - -Variable( - name='F1', - long_name='Preference fraction of algal N uptake from NH4', - units='unitless', - description='Preference fraction of algal N uptake from NH4', - use='static' -) - -Variable( - name='F2', - long_name='Preference fraction of benthic algae N uptake from NH4', - units='unitless', - description='Preference fraction of benthic algae N uptake from NH4', - use='static' -) - -Variable( - name='Fb', - long_name='Fraction of bottom area available for benthic algae growth', - units='unitless', - description='Fraction of bottom area available for benthic algae growth', - use='static' -) - diff --git a/src/clearwater_modules/nsm1/balgae/dynamic_variables_balgae.py b/src/clearwater_modules/nsm1/balgae/dynamic_variables.py similarity index 81% rename from src/clearwater_modules/nsm1/balgae/dynamic_variables_balgae.py rename to src/clearwater_modules/nsm1/balgae/dynamic_variables.py index ebbd25e..67b00db 100644 --- a/src/clearwater_modules/nsm1/balgae/dynamic_variables_balgae.py +++ b/src/clearwater_modules/nsm1/balgae/dynamic_variables.py @@ -1,11 +1,11 @@ """ -File includes dynamic variables computed in Balgae module. Dynamic variables may be accessed by other modules. +File contains dynamic variables related to the benthic algae module """ import clearwater_modules.shared.processes as shared_processes from clearwater_modules import base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.balgae.balgae_processes as balgae_processes +import clearwater_modules.nsm1.balgae.processes as processes @base.register_variable(models=NutrientBudget) @@ -19,7 +19,7 @@ class Variable(base.Variable): units='1/d', description='Maximum benthic algal growth rate with temperature correction', use='dynamic', - process=balgae_processes.mub_max_tc + process=processes.mub_max_tc ) Variable( @@ -28,7 +28,7 @@ class Variable(base.Variable): units='1/d', description='Benthic algae respiration rate with temperature correction', use='dynamic', - process=balgae_processes.krb_tc + process=processes.krb_tc ) Variable( @@ -37,7 +37,7 @@ class Variable(base.Variable): units='1/d', description='Benthic algae mortality rate with temperature correction', use='dynamic', - process=balgae_processes.kdb_tc + process=processes.kdb_tc ) Variable( @@ -46,7 +46,7 @@ class Variable(base.Variable): units='mg-N/mg-D', description='Ratio benthic algae nitrogen to dry weight', use='dynamic', - process=balgae_processes.rnb + process=processes.rnb ) Variable( @@ -55,7 +55,7 @@ class Variable(base.Variable): units='mg-P/mg-D', description='Ratio benthic algae phosphorus to dry weight', use='dynamic', - process=balgae_processes.rpb + process=processes.rpb ) Variable( @@ -64,7 +64,7 @@ class Variable(base.Variable): units='mg-C/mg-D', description='Ratio benthic algae carbon to dry weight', use='dynamic', - process=balgae_processes.rcb + process=processes.rcb ) Variable( @@ -73,7 +73,7 @@ class Variable(base.Variable): units='ug-Chala-a/mg-D', description='Ratio benthic algae chlorophyll-a to dry weight', use='dynamic', - process=balgae_processes.rab + process=processes.rab ) Variable( @@ -82,7 +82,7 @@ class Variable(base.Variable): units='unitless', description='Benthic algal light limitation factor', use='dynamic', - process=balgae_processes.FLb + process=processes.FLb ) Variable( @@ -91,7 +91,7 @@ class Variable(base.Variable): units='unitless', description='Benthic algal nitrogen limitation factor', use='dynamic', - process=balgae_processes.FNb + process=processes.FNb ) Variable( @@ -100,7 +100,7 @@ class Variable(base.Variable): units='unitless', description='Benthic algal phosphorous limitation factor', use='dynamic', - process=balgae_processes.FPb + process=processes.FPb ) Variable( @@ -109,7 +109,7 @@ class Variable(base.Variable): units='unitless', description='Benthic algal density attenuation', use='dynamic', - process=balgae_processes.FSb + process=processes.FSb ) Variable( @@ -118,7 +118,7 @@ class Variable(base.Variable): units='1/d', description='Benthic algae specific growth rate', use='dynamic', - process=balgae_processes.mub + process=processes.mub ) Variable( @@ -127,7 +127,7 @@ class Variable(base.Variable): units='g/m^2/d', description='Benthic algae growth rate', use='dynamic', - process=balgae_processes.AbGrowth + process=processes.AbGrowth ) Variable( @@ -136,7 +136,7 @@ class Variable(base.Variable): units='g/m^2/d', description='Benthic algae respiration rate', use='dynamic', - process=balgae_processes.AbRespiration + process=processes.AbRespiration ) Variable( @@ -145,7 +145,7 @@ class Variable(base.Variable): units='g/m^2/d', description='Benthic algae death rate', use='dynamic', - process=balgae_processes.AbDeath + process=processes.AbDeath ) Variable( @@ -154,7 +154,7 @@ class Variable(base.Variable): units='g/m^2/d', description='Change in benthic algae concentration', use='dynamic', - process=balgae_processes.dAbdt + process=processes.dAbdt ) Variable( @@ -163,5 +163,5 @@ class Variable(base.Variable): units='mg-Chla/m^2', description='Chlorophyll-a concentration', use='dynamic', - process=balgae_processes.Chlb + process=processes.Chlb ) diff --git a/src/clearwater_modules/nsm1/balgae/balgae_processes.py b/src/clearwater_modules/nsm1/balgae/processes.py similarity index 81% rename from src/clearwater_modules/nsm1/balgae/balgae_processes.py rename to src/clearwater_modules/nsm1/balgae/processes.py index 1dab95e..f582708 100644 --- a/src/clearwater_modules/nsm1/balgae/balgae_processes.py +++ b/src/clearwater_modules/nsm1/balgae/processes.py @@ -2,11 +2,10 @@ File contains process to calculate new benthic algae biomass concentration and associated dependent variables """ -import math -from clearwater_modules.shared.processes import arrhenius_correction import numba import xarray as xr - +from clearwater_modules.shared.processes import arrhenius_correction +import math @numba.njit def mub_max_tc( @@ -131,27 +130,14 @@ def FLb( # The equations are different, this expression is more convenient here. KEXT = math.exp(-L*depth) - if Ab <= 0.0 or KEXT <= 0.0 or PAR <= 0.0: - # After sunset, no growth - FLb = 0.0 - elif b_light_limitation_option == 1: - # Use half-saturation formulation - FLb = PAR * KEXT / (KLb + PAR * KEXT) - elif b_light_limitation_option == 2: - # Use Smith's equation - FLb = PAR * KEXT / ((KLb**2.0 + (PAR * KEXT)**2.0)**0.5) - elif b_light_limitation_option == 3: - # Use Steele's equation - if abs(KLb) < 1.0E-10: - FLb = 0.0 - else: - FLb = PAR * KEXT / KLb * math.exp(1.0 - PAR * KEXT / KLb) - - # Limit the benthic light limitation factor to between 0.0 and 1.0 - if FLb > 1.0: - FLb = 1.0 - if FLb < 0.0: - FLb = 0.0 + FLb = xr.where(Ab <= 0.0 or KEXT <= 0.0 or PAR <= 0.0, 0.0, + xr.where(b_light_limitation_option == 1, PAR * KEXT / (KLb + PAR * KEXT), + xr.where(b_light_limitation_option == 2, PAR * KEXT / ((KLb**2.0 + (PAR * KEXT)**2.0)**0.5), + xr.where(b_light_limitation_option == 3, + xr.where(abs(KLb) < 1.0E-10, 0.0, PAR * KEXT / KLb * math.exp(1.0 - PAR * KEXT / KLb)), "NaN" + )))) + FLb = xr.where(FLb > 1.0, 1.0, + xr.where(FLb < 0.0, 0.0, FLb)) return FLb @@ -174,15 +160,9 @@ def FNb( NO3: Nitrate concentration (mg-N/L) KsNb: Michaelis-Menton half-saturation constant relating inorganic N to benthic algal growth (mg-N/L) """ - - if use_NH4 or use_NO3: - FNb = (NH4 + NO3) / (KsNb + NH4 + NO3) - if math.isnan(FNb): - FNb = 0.0 - if FNb > 1.0: - FNb = 1.0 - else: - FNb = 1.0 + FNb = xr.where(use_NH4 or use_NO3, (NH4 + NO3) / (KsNb + NH4 + NO3),1) + FNb = xr.where(math.isnan(FNb),0.0, + xr.where(FNb < 1.0, 1, FNb)) return FNb @@ -203,14 +183,9 @@ def FPb( fdp: Fraction P dissolved (unitless) """ - if use_TIP: - FPb = fdp * TIP / (KsPb + fdp * TIP) - if math.isnan(FPb): - FPb = 0.0 - if FPb > 1.0: - FPb = 1.0 - else: - FPb = 1.0 + FPb = xr.where(use_TIP, fdp * TIP / (KsPb + fdp * TIP),1.0) + FPb = xr.where(math.isnan(FPb),0.0, + xr.where(FPb > 1.0, 1.0, FPb)) return FPb @@ -230,17 +205,14 @@ def FSb( """ FSb = 1.0 - (Ab / (Ab + Ksb)) - if math.isnan(FSb): - FSb = 1.0 - if FSb > 1.0: - FSb = 1.0 - + FSb = xr.where(math.isnan(FSb), 1.0, + xr.where(FSb > 1.0, 1.0, FSb)) + return FSb @numba.njit def mub( - Ab: xr.DataArray, mub_max_tc: xr.DataArray, b_growth_rate_option: int, FLb: xr.DataArray, @@ -252,7 +224,6 @@ def mub( """Calculate benthic algae specific growth rate (1/d) Args: - Ab: Benthic algae concentration (g/m^2) mub_max_tc: Maximum benthic algal growth rate with temperature correction (1/d) b_growth_rate_option: Benthic Algal growth rate with three options 1) Multiplicative, 2) Limiting Nutrient FLb: Benethic algal light limitation (unitless) @@ -262,14 +233,8 @@ def mub( """ # Benthic Local Specific Growth Rate - if b_growth_rate_option == 1: - # (a) Multiplicative (day-1) - mub = mub_max_tc * FLb * FPb * FNb * FSb - elif b_growth_rate_option == 2: - # (b) Limiting nutrient (day-1) - mub = mub_max_tc * FLb * FSb * min(FPb, FNb) + return xr.where(b_growth_rate_option == 1, mub_max_tc * FLb * FPb * FNb * FSb, mub_max_tc * FLb * FSb * min(FPb, FNb)) - return mub @numba.njit @@ -332,6 +297,37 @@ def dAbdt( """ return AbGrowth - AbRespiration - AbDeath +@numba.njit +def Ab( + Ab: xr.DataArray, + dAbdt: xr.DataArray + +) -> xr.DataArray: + """Calculate new benthic algae concentration (g/m^2/d) + + Args: + Ab: Benthic algae concentration (g/m^2) + dAbdt: Change in benthic algae concentration (g/m^2/d) + """ + return Ab + dAbdt + +@numba.njit +def Ab( + Ab: xr.DataArray, + dAbdt: xr.DataArray, + timestep: xr.DataArray, + +) -> xr.DataArray: + """Calculate Ab: New concentration benthic algae (mg-N/L) + + Args: + Ab: Concentration of benthic algae (mg-N/L) + dAbdt: Change in Ab (mg-N/L/d) + timestep: current iteration timestep (d) + + """ + + return Ab + dAbdt*timestep @numba.njit def Chlb( diff --git a/src/clearwater_modules/nsm1/balgae/static_variables_balgae.py b/src/clearwater_modules/nsm1/balgae/static_variables.py similarity index 86% rename from src/clearwater_modules/nsm1/balgae/static_variables_balgae.py rename to src/clearwater_modules/nsm1/balgae/static_variables.py index adbbcbb..91a7ff5 100644 --- a/src/clearwater_modules/nsm1/balgae/static_variables_balgae.py +++ b/src/clearwater_modules/nsm1/balgae/static_variables.py @@ -1,18 +1,13 @@ """ -File includes static variables only used in Algae module +File contains static variables related to the Benthic Algae module """ import clearwater_modules.base as base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.algae.algae_processes as algae_processes - @base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... -# TODO: verify all these values - -#Only Balgae Variables Variable( name='Fw', @@ -138,6 +133,22 @@ class Variable(base.Variable): name='b_light_limitation_option', long_name='Benthic Algal light limitation rate options', units='unitless', - description='Benthic Algal light limitation rate with three options: 1) Half-saturation formulation, 2) Smiths Model, 3) Steeles Model' + description='Benthic Algal light limitation rate with three options: 1) Half-saturation formulation, 2) Smiths Model, 3) Steeles Model', use='static', -) \ No newline at end of file +) + +Variable( + name='Fb', + long_name='Fraction of bottom area available for benthic algae growth', + units='unitless', + description='Fraction of bottom area available for benthic algae growth', + use='static' +) + +Variable( + name='Fw', + long_name='Fraction of benthic algae mortality into water column', + units='unitless', + description='Fraction of benthic algae mortality into water column', + use='static' +) diff --git a/src/clearwater_modules/nsm1/carbon/dynamic_variables.py b/src/clearwater_modules/nsm1/carbon/dynamic_variables.py index 20163fe..28a859e 100644 --- a/src/clearwater_modules/nsm1/carbon/dynamic_variables.py +++ b/src/clearwater_modules/nsm1/carbon/dynamic_variables.py @@ -1,32 +1,24 @@ -# TODO: figure out imports +""" +File contains dynamic variables related to the Carbon module +""" import clearwater_modules.shared.processes as shared_processes from clearwater_modules import base -from clearwater_modules.nsm1.carbon.model import CarbonBudget -from clearwater_modules.nsm1.carbon import processes +from clearwater_modules.nsm1.model import NutrientBudget +import clearwater_modules.nsm1.carbon.processes as processes -@base.register_variable(models=CarbonBudget) +@base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... - -Variable( - name='depth', - long_name='Average water depth in cell', - units='m', - description='Average water depth in cell computed by dividing volume by surface area', - use='dynamic', - process=shared_processes.compute_depth -) - Variable( - name='kpoc_T', + name='kpoc_tc', long_name='Temperature adjusted POC hydrolysis rate', units='1/d', description='Temperature adjusted POC hydrolysis rate', use='dynamic', - process=processes.kpoc_T, + process=processes.kpoc_tc, ) Variable( @@ -75,12 +67,12 @@ class Variable(base.Variable): ) Variable( - name='kdoc_T', + name='kdoc_tc', long_name='Dissolved organic carbon oxidation rate adjusted for temperature', units='1/d', description='Dissolved organic carbon oxidation rate adjusted for temperature', use='dynamic', - process=processes.kdoc_T + process=processes.kdoc_tc ) Variable( @@ -129,12 +121,12 @@ class Variable(base.Variable): ) Variable( - name='kac_T', + name='kac_tc', long_name='temperature dependent CO2 reaeration rate', units='1/d', description='temperature dependent CO2 reaeration rate', use='dynamic', - process=processes.kac_T + process=processes.kac_tc ) Variable( diff --git a/src/clearwater_modules/nsm1/carbon/model.py b/src/clearwater_modules/nsm1/carbon/model.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/clearwater_modules/nsm1/carbon/processes.py b/src/clearwater_modules/nsm1/carbon/processes.py index 168c6d0..0063797 100644 --- a/src/clearwater_modules/nsm1/carbon/processes.py +++ b/src/clearwater_modules/nsm1/carbon/processes.py @@ -1,44 +1,39 @@ -import numpy as np +""" +File contains process to calculate new carbon concentration and associated dependent variables +""" + import numba import xarray as xr -from clearwater_modules.shared.processes import ( - arrhenius_correction -) -from clearwater_modules.nsm1.carbon import dynamic_variables -from clearwater_modules.nsm1.carbon import static_variables -from clearwater_modules.nsm1 import static_variables_global -# from clearwater_modules.nsm1 import dynamic_variables_global -from clearwater_modules.nsm1 import state_variables +from clearwater_modules.shared.processes import arrhenius_correction +import math @numba.njit -def kpoc_T( - water_temp_c: xr.DataArray, +def kpoc_tc( + TwaterC: xr.DataArray, kpoc_20: xr.DataArray, - theta: xr.DataArray ) -> xr.DataArray: """Calculate the temperature adjusted POC hydrolysis rate (/d) Args: - water_temp_c: Water temperature in Celsius + TwaterC: Water temperature in Celsius kpoc_20: POC hydrolysis rate at 20 degrees Celsius (1/d) - theta: Arrhenius coefficient for kpoc """ - return arrhenius_correction(water_temp_c, kpoc_20, theta) + return arrhenius_correction(TwaterC, kpoc_20, 1.047) @numba.njit def POC_hydrolysis( - kpoc_T: xr.DataArray, + kpoc_tc: xr.DataArray, POC: xr.DataArray, ) -> xr.DataArray: """Calculate the POC concentration change due to hydrolysis for a given timestep Args: - kpoc_T: POC hydrolysis rate at given water temperature (1/d) + kpoc_tc: POC hydrolysis rate at given water temperature (1/d) POC: POC concentration (mg/L) """ - return kpoc_T * POC + return kpoc_tc * POC @numba.njit @@ -59,7 +54,7 @@ def POC_settling( def POC_algal_mortality( f_pocp: xr.DataArray, - kdp_T: xr.DataArray, + kdp_tc: xr.DataArray, rca: xr.DataArray, Ap: xr.DataArray, use_Algae: xr.DataArray @@ -68,12 +63,12 @@ def POC_algal_mortality( Args: f_pocp: Fraction of algal mortality into POC - kdp_T: Algal death rate at water temperature (1/d) + kdp_tc: Algal death rate at water temperature (1/d) rca: Algal C to chlorophyll-a ratio (mg-C/ugChla) Ap: Algae concentration (mg/L) use_Algae: Option for considering algae in POC budget (boolean) """ - da: xr.DataArray = xr.where(use_Algae == True, f_pocp * kdp_T * rca * Ap, 0) + da: xr.DataArray = xr.where(use_Algae == True, f_pocp * kdp_tc * rca * Ap, 0) return da @@ -81,7 +76,7 @@ def POC_algal_mortality( def POC_benthic_algae_mortality( depth: xr.DataArray, F_pocb: xr.DataArray, - kdb_T: xr.DataArray, + kdb_tc: xr.DataArray, rcb: xr.DataArray, Ab: xr.DataArray, Fb: xr.DataArray, @@ -93,14 +88,14 @@ def POC_benthic_algae_mortality( Args: depth: Water depth in cell (m) F_pocb: Fraction of benthic algal mortality into POC - kdb_T: Benthic algae death rate (1/d) + kdb_tc: Benthic algae death rate (1/d) rcb: Benthic algae C to biomass weight ratio (mg-C/mg-D) Ab: Benthic algae concentration (mg/L) Fb: Fraction of bottom area available for benthic algae growth Fw: Fraction of benthic algae mortality into water column use_Balgae: Option for considering benthic algae in POC budget (boolean) """ - da: xr.DataArray = xr.where(use_Balgae == True, (1 / depth) * F_pocb * kdb_T * rcb * Ab * Fb * Fw, 0) + da: xr.DataArray = xr.where(use_Balgae == True, (1 / depth) * F_pocb * kdb_tc * rcb * Ab * Fb * Fw, 0) return da @@ -123,7 +118,7 @@ def dPOCdt( @numba.njit -def POC_new( +def POC( POC: xr.DataArray, dPOCdt: xr.DataArray, timestep: xr.DataArray @@ -140,7 +135,7 @@ def POC_new( def DOC_algal_mortality( f_pocp: xr.DataArray, - kdp_T: xr.DataArray, + kdp_tc: xr.DataArray, rca: xr.DataArray, Ap: xr.DataArray, use_Algae: xr.DataArray @@ -149,12 +144,12 @@ def DOC_algal_mortality( Args: f_pocp: Fraction of algal mortality into POC - kdp_T: Algal death rate at water temperature (1/d) + kdp_tc: Algal death rate at water temperature (1/d) rca: Algal C to chlorophyll-a ratio (mg-C/ug-Chla) Ap: Algae concentration (mg/L) use_Algae: Option for considering algae in DOC budget (boolean) """ - da: xr.DataArray = xr.where(use_Algae == True, (1 - f_pocp) * kdp_T * rca * Ap, 0) + da: xr.DataArray = xr.where(use_Algae == True, (1 - f_pocp) * kdp_tc * rca * Ap, 0) return da @@ -162,7 +157,7 @@ def DOC_algal_mortality( def DOC_benthic_algae_mortality( depth: xr.DataArray, F_pocb: xr.DataArray, - kdb_T: xr.DataArray, + kdb_tc: xr.DataArray, rcb: xr.DataArray, Ab: xr.DataArray, Fb: xr.DataArray, @@ -174,38 +169,36 @@ def DOC_benthic_algae_mortality( Args: depth: Water depth in cell (m) F_pocb: Fraction of benthic algal mortality into POC - kdb_T: Benthic algae death rate (1/d) + kdb_tc: Benthic algae death rate (1/d) rcb: Benthic algae C to biomass weight ratio (mg-C/mg-D) Ab: Benthic algae concentration (mg/L) Fb: Fraction of bottom area available for benthic algae growth Fw: Fraction of benthic algae mortality into water column use_Balgae: Option for considering benthic algae in DOC budget (boolean) """ - da: xr.DataArray = xr.where(use_Balgae == True, (1 / depth) * (1 - F_pocb) * kdb_T * rcb * Ab * Fb * Fw, 0) + da: xr.DataArray = xr.where(use_Balgae == True, (1 / depth) * (1 - F_pocb) * kdb_tc * rcb * Ab * Fb * Fw, 0) return da @numba.njit -def kdoc_T( - water_temp_c: xr.DataArray, +def kdoc_tc( + TwaterC: xr.DataArray, kdoc_20: xr.DataArray, - theta: xr.DataArray ) -> xr.DataArray: """Calculate the temperature adjusted DOC oxidation rate (1/d) Args: - water_temp_c: Water temperature in Celsius + TwaterC: Water temperature in Celsius kdoc_20: DOC oxidation rate at 20 degrees Celsius (1/d) - theta: Arrhenius coefficient """ - return arrhenius_correction(water_temp_c, kdoc_20, theta) + return arrhenius_correction(TwaterC, kdoc_20, 1.047) def DOC_oxidation( DOX: xr.DataArray, KsOxmc: xr.DataArray, - kdoc_T: xr.DataArray, + kdoc_tc: xr.DataArray, DOC: xr.DataArray, use_DOX: xr.DataArray ) -> xr.DataArray: @@ -214,11 +207,11 @@ def DOC_oxidation( Args: DOX: Concentration of dissolved oxygen (mg/L) KsOxmc: Half saturation oxygen attenuation constant for DOC oxidation rate (mg-O2/L) - kdoc_T: DOC oxidation rate (1/d) + kdoc_tc: DOC oxidation rate (1/d) DOC: Concentration of dissolved organic carbon (mg/L) use_DOX: Option for considering dissolved oxygen concentration in DOC oxidation calculation (boolean) """ - da: xr.DataArray = xr.where(use_DOX == True, DOX / (KsOxmc + DOX) * kdoc_T * DOC, kdoc_T * DOC) + da: xr.DataArray = xr.where(use_DOX == True, DOX / (KsOxmc + DOX) * kdoc_tc * DOC, kdoc_tc * DOC) return da @@ -244,7 +237,7 @@ def dDOCdt( @numba.njit -def DOC_new( +def DOC( DOC: xr.DataArray, dDOCdt: xr.DataArray, timestep: xr.DataArray @@ -261,35 +254,18 @@ def DOC_new( @numba.njit def Henrys_k( - water_temp_c: xr.DataArray + TwaterC: xr.DataArray ) -> xr.DataArray: """Calculates the temperature dependent Henry's coefficient (mol/L/atm) Args: - water_temp_c: Water temperature in celsius + TwaterC: Water temperature in celsius """ - return 10**(2385.73 / (water_temp_c + 273.15) + .0152642 * (water_temp_c + 273.15) - 14.0184) - - -@numba.njit -def kac_T( - water_temp_c: xr.DataArray, - kac_20: xr.DataArray, - theta: xr.DataArray -) -> xr.DataArray: - """Calculate the temperature adjusted CO2 reaeration rate (1/d) - - Args: - water_temp_c: Water temperature in Celsius - kac_20: CO2 reaeration rate at 20 degrees Celsius (1/d) - theta: Arrhenius coefficient - """ - return arrhenius_correction(water_temp_c, kac_20, theta) - + return 10**(2385.73 / (TwaterC + 273.15) + .0152642 * (TwaterC + 273.15) - 14.0184) @numba.njit def Atmospheric_CO2_reaeration( - kac_T: xr.DataArray, + ka_tc: xr.DataArray, K_H: xr.DataArray, pCO2: xr.DataArray, FCO2: xr.DataArray, @@ -298,13 +274,13 @@ def Atmospheric_CO2_reaeration( """Calculates the atmospheric input of CO2 into the waterbody Args: - kac_T: CO2 reaeration rate adjusted for temperature (1/d) + ka_tc: CO2 reaeration rate adjusted for temperature, same as O2 reaeration rate (1/d) K_H: Henry's Law constant (mol/L/atm) pCO2: Partial pressure of CO2 in the atmosphere (ppm) FCO2: Fraction of CO2 in total inorganic carbon DIC: Dissolved inorganic carbon concentration (mg/L) """ - return 12 * kac_T * (10**-3 * K_H * pCO2 - 10**3 * FCO2 * DIC) + return 12 * ka_tc * (10**-3 * K_H * pCO2 - 10**3 * FCO2 * DIC) def DIC_algal_respiration( @@ -387,7 +363,7 @@ def DIC_CBOD_oxidation( DOX: xr.DataArray, CBOD: xr.DataArray, roc: xr.DataArray, - kbod_T: xr.DataArray, #imported from CBOD module + kbod_tc: xr.DataArray, #imported from CBOD module KsOxbod: xr.DataArray, #imported from CBOD module use_DOX: xr.DataArray ) -> xr.DataArray: @@ -397,12 +373,12 @@ def DIC_CBOD_oxidation( DOX: Dissolved oxygen concentration (mg/L) CBOD: Carbonaceous biochemical oxygen demand concentration (mg/L) roc: Ratio of O2 to carbon for carbon oxidation (mg-O2/mg-C) - kbod_T: CBOD oxidation rate (1/d) + kbod_tc: CBOD oxidation rate (1/d) KsOxbod: Half saturation oxygen attenuation constant for CBOD oxidation (mg-O2/L) use_DOX: Option to consider dissolved oxygen in CBOD oxidation calculation (boolean) """ - da: xr.DataArray = xr.where(use_DOX == True, (1 / roc) * (DOX / (KsOxbod + DOX)) * kbod_T * CBOD, CBOD * kbod_T) + da: xr.DataArray = xr.where(use_DOX == True, (1 / roc) * (DOX / (KsOxbod + DOX)) * kbod_tc * CBOD, CBOD * kbod_tc) return da @@ -418,7 +394,7 @@ def DIC_sed_release( """Computes the sediment release of DIC Args: - SOD_T: Sediment oxygen demand adjusted for water temperature (mg-O2/L/d) + SOD_tc: Sediment oxygen demand adjusted for water temperature (mg-O2/L/d) roc: Ratio of O2 to carbon for carbon oxidation (mg-O2/mg-C) depth: Water depth (m) JDIC: Sediment-water flux of dissolved inorganic carbon (g-C/m2/d) @@ -454,7 +430,7 @@ def dDICdt( @numba.njit -def DIC_new( +def DIC( DIC: xr.DataArray, dDICdt: xr.DataArray, timestep: xr.DataArray diff --git a/src/clearwater_modules/nsm1/carbon/static_variables.py b/src/clearwater_modules/nsm1/carbon/static_variables.py index 5a64574..91780df 100644 --- a/src/clearwater_modules/nsm1/carbon/static_variables.py +++ b/src/clearwater_modules/nsm1/carbon/static_variables.py @@ -1,41 +1,24 @@ -# TODO: figure out what 'model' to import +""" +File contains static variables related to the Carbon module +""" + import clearwater_modules.base as base -from clearwater_modules.nsm1.carbon.model import EnergyBudget +from clearwater_modules.nsm1.model import NutrientBudget -@base.register_variable(models=EnergyBudget) +@base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... -# TODO: verify all these values - -### -# To get from algae/benthic: rca?, rcb?, kdp_20 (static), kdp_T (dyn), Ap (state), depth (state), Ab (state), Fw, Fb Variable( - name='F_pocp', + name='f_pocp', long_name='Fraction of algal mortality into POC', units='unitless', description='Fraction of dead algae that converts to particulate organic carbon', use='static' ) -Variable( - name='rca', - long_name='algal C : Chla ratio', - units='mg-C/ug-Chla', - description='Ratio of algal carbon to chlorophyll-a', - use='static' -) - -Variable( - name='rcb', - long_name='benthic algae C : D ratio', - units='mg-C/mg-D', - description='Ratio of benthic algal carbon to algal biomass', - use='static' -) - Variable( name='kdoc_20', long_name='Dissolved organic carbon oxidation rate', @@ -45,15 +28,7 @@ class Variable(base.Variable): ) Variable( - name='vsoc', - long_name='POC settling velocity', - units='m/d', - description='POC settling velocity', - use='static' -) - -Variable( - name='F_pocb', + name='f_pocb', long_name='fraction of benthic algal mortality into POC', units='unitless', description='fraction of benthic algal mortality into POC', @@ -69,22 +44,13 @@ class Variable(base.Variable): ) Variable( - name='K_sOxmc', + name='KsOxmc', long_name='half saturation oxygen attenuation constant for DOC oxidation rate', units='mg-O2/L', description='half saturation oxygen attenuation constant for DOC oxidation rate', use='static' ) - -Variable( - name='kac_20', - long_name='CO2 reaeration rate', - units='1/d', - description='CO2 reaeration rate', - use='static' -) - Variable( name='pCO2', long_name='partial atmospheric CO2 pressure', @@ -100,3 +66,12 @@ class Variable(base.Variable): description='CO2 reaeration rate', use='static' ) + +#TODO define roc long name and description +Variable( + name='roc', + long_name='O2:C ratio for carbon oxidation', + units='mg-O2/mg-C', + description='32/12', + use='static' +) \ No newline at end of file diff --git a/src/clearwater_modules/nsm1/constants.py b/src/clearwater_modules/nsm1/constants.py index 5c176e8..37ed441 100644 --- a/src/clearwater_modules/nsm1/constants.py +++ b/src/clearwater_modules/nsm1/constants.py @@ -14,9 +14,9 @@ class AlgaeStaticVariables(TypedDict): KL: float KsN: float KsP: float - mu_max: float - kdp: float - krp: float + mu_max_20: float + kdp_20: float + krp_20: float vsap: float growth_rate_option: int light_limitation_option: int @@ -27,47 +27,34 @@ class AlgaeStaticVariables(TypedDict): AWn= 7.2, AWp= 1, AWa= 1000, + KL= 10, KsN= 0.04, KsP= 0.0012, - mu_max= 1, - kdp= 0.15, - krp= 0.2, + mu_max_20= 1, + kdp_20= 0.15, + krp_20= 0.2, vsap= 0.15, growth_rate_option = 1, light_limitation_option = 1 ) -class NitrogenStaticVariables(TypedDict): - KNR: float - knit_20: float - kon_20: float - kdnit_20: float - rnh4_20: float - vno3_20: float - KsOxdn: float - PN: float - PNb: float - -DEFAULT_NITROGEN = NitrogenStaticVariables( - KNR= 0.6 , - knit_20= 0.1, - kon_20=0.1, - kdnit_20=0.002, - rnh4_20=0, - vno3_20=0, - KsOxdn=0.1, - PN=0.5, - PNb=0.5 -) - -class PhosphorusStaticVariables(TypedDict): - kop_20: float - rpo4_20: float - -DEFAULT_PHOSPHORUS = PhosphorusStaticVariables( - kop_20 = 0.1, - rpo4_20 =0 +class AlkalinityStaticVariables(TypedDict): + r_alkaa: float + r_alkan: float + r_alkn: float + r_alkden: float + r_alkba: float + r_alkbn: float + +DEFAULT_Alkalinity = AlkalinityStaticVariables( + r_alkaa = 14.0 / 106.0 / 12.0 / 1000.0, + r_alkan= 18.0 / 106.0 / 12.0 / 1000.0, + r_alkn = 2.0 / 14.0 / 1000.0, + r_alkden = 4.0 / 14.0 / 1000.0, + r_alkba = 14.0 / 106.0 / 12.0 / 1000.0, + r_alkbn =18.0 / 106.0 / 12.0 / 1000.0 + ) class BalgaeStaticVariables(TypedDict): @@ -81,9 +68,9 @@ class BalgaeStaticVariables(TypedDict): KsNb: float KsPb: float Ksb: float - mub_max: float - krb: float - kdb: float + mub_max_20: float + krb_20: float + kdb_20: float b_growth_rate_option: float b_light_limitation_option: float Fw: float @@ -100,65 +87,290 @@ class BalgaeStaticVariables(TypedDict): KsNb= 0.25, KsPb=0.125, Ksb=10, - mub_max=0.4, - krb=0.2, - kdb=0.3, + mub_max_20=0.4, + krb_20=0.2, + kdb_20=0.3, b_growth_rate_option=1, b_light_limitation_option=1, Fw=0.9, - Fb=0.9, + Fb=0.9 +) + + +class NitrogenStaticVariables(TypedDict): + KNR: float + knit_20: float + kon_20: float + kdnit_20: float + rnh4_20: float + vno3_20: float + KsOxdn: float + PN: float + PNb: float + +DEFAULT_NITROGEN = NitrogenStaticVariables( + KNR= 0.6 , + knit_20= 0.1, + kon_20=0.1, + kdnit_20=0.002, + rnh4_20=0, + vno3_20=0, + KsOxdn=0.1, + PN=0.5, + PNb=0.5 +) + + +class CarbonStaticVariables(TypedDict): + f_pocp: float + kdoc_20: float + f_pocb: float + kpoc_20: float + KsOxmc: float + kac_20: float + pCO2: float + FCO2: float + roc: float + +DEFAULT_Carbon = CarbonStaticVariables( + f_pocp = 0.9, + kdoc_20= 0.01, + f_pocb=0.9, + kpoc_20= 0.005, + KsOxmc=1.0, + pCO2 = 383.0, + FCO2 = 0.2, + roc = 32.0/12.0 +) + +class CBODStaticVariables(TypedDict): + kbod_20: float + ksbod_20: float + KsOxbod: float + +DEFAULT_CBOD = CBODStaticVariables( + kbod_20 = 0.12, + ksbod_20 = 0.0, + KsOxbod = 0.5 +) + +class DOXStaticVariables(TypedDict): + ron : float + KsSOD : float + +DEFAULT_DOX = DOXStaticVariables( + ron = 2.0 * 32.0 / 14.0, + KsSOD =1, ) class N2StaticVariables(TypedDict): - pass + pass DEFAULT_N2 = N2StaticVariables( + +) + +class PhosphorusStaticVariables(TypedDict): + kop_20: float + rpo4_20: float + +DEFAULT_PHOSPHORUS = PhosphorusStaticVariables( + kop_20 = 0.1, + rpo4_20 = 0 +) + + +class POMStaticVariables(TypedDict): + kpom_20: float + +DEFAULT_POM = POMStaticVariables( + kpom_20 = 0.1 +) + + +class CBODStaticVariables(TypedDict): + kbod_20: float + ksbod_20: float + ksOxbod: float + +DEFAULT_CBOD = CBODStaticVariables( + kbod_20 = 0.12, + ksbod_20 = 0, + ksOxbod = 0.5 ) + +class CarbonStaticVariables(TypedDict): + F_pocp: float + kdoc_20: float + F_pocb: float + kpoc_20: float + K_sOxmc: float + pCO2: float + FCO2: float + +DEFAULT_CARBON = CarbonStaticVariables( + F_pocp = 0.9, + kdoc_20 = 0.01, + F_pocb = 0.9, + kpoc_20 = 0.005, + K_sOxmc = 1, + pCO2 = 383, + FCO2 = 0.2 +) + + +class DOXStaticVariables(TypedDict): + ... + +DEFAULT_DOX = DOXStaticVariables( + +) + + class PathogenStaticVariables(TypedDict): - kdx: float + kdx_20: float apx: float vx: float DEFAULT_PATHOGEN = PathogenStaticVariables( - kdx=0.8, + kdx_20=0.8, apx=1, - vx=1, + vx=1 +) + +class AlkalinityStaticVariables(TypedDict): + r_alkaa: float + r_alkan: float + r_alkn: float + r_alkden: float + r_alkba: float + r_alkbn: float + +DEFAULT_ALKALINITY = AlkalinityStaticVariables( + r_alkaa = 1, + r_alkan = 1, + r_alkn = 1, + r_alkden = 1, + r_alkba = 1, + r_alkbn = 1 +) + +class PhosphorusStaticVariables(TypedDict): + kop_20: float + rpo4_20: float + kdpo4: float + +DEFAULT_PHOSPHORUS = PhosphorusStaticVariables( + kop_20 = 0.1, + rpo4_20 =0, + kdpo4 = 0.0, +) + +class POMStaticVariables(TypedDict): + kpom_20: float + +DEFAULT_POM = POMStaticVariables( + kpom_20 = 0.01, ) class GlobalParameters(TypedDict): use_NH4 : bool use_NO3 : bool - use_OrgN: bool + use_OrgN: bool + use_OrgP: bool use_TIP : bool use_SedFlux: bool + use_POC: bool + use_DOC: bool use_DOX: bool + use_DIC: bool use_Algae: bool use_Balgae: bool - use_TIP: bool + use_N2: bool + use_Pathogen: bool + use_Alk: bool + use_POM: bool + DEFAULT_GLOBALPARAMETERS = GlobalParameters( use_NH4= True, use_NO3= True, use_OrgN= True, + use_OrgP = True, use_TIP= True, - use_SedFlux= True, + use_SedFlux= False, + use_POC = True, + use_DOC = True, use_DOX= True, + use_DIC= True, use_Algae= True, use_Balgae= True, - use_TIP= True, + use_N2 = True, + use_Pathogen = True, + use_Alk = True, + use_POM = True ) + class GlobalVars(TypedDict): - L : float #lambda - fdp: float - PAR: float - vson: float + vson: float + vsoc: float + vsop: float + vs: float + SOD_20: float + SOD_theta: float + vb: float + fcom: float + kaw_20_user: float + kah_20_user: float + hydraulic_reaeration_option: int + wind_reaeration_option: int + Fr_PAR: float + lambda0: float + lambda1: float + lambda2: float + lambdas: float + lambdam: float + timestep: float + velocity: float + flow: float + topwidth: float + slope: float + shear_velocity: float + pressure_atm: float + wind_speed: float + q_solar: float + Solid: int + DEFAULT_GLOBALVARS = GlobalVars( - L = 1, #lambda - fdp= 0.5, - PAR= 1, vson = 0.01, + vsoc = 0.01, + vsop = 999, + vs = 999, + SOD_20 = 999, + SOD_theta = 999, + vb = 0.01, + fcom = 0.4, + kaw_20_user = 999, + kah_20_user = 999, + hydraulic_reaeration_option = 2, + wind_reaeration_option = 2, + Fr_PAR = .5, + lambda0 = .5, + lambda1 = .5, + lambda2 = .5, + lambdas = .5, + lambdam = .5, + timestep = 86400, + velocity = 1, + flow = 2, + topwidth = 1, + slope = 2, + shear_velocity = 4, + pressure_atm = 2, + wind_speed = 4, + q_solar = 4, + Solid = 1 ) - diff --git a/src/clearwater_modules/nsm1/dynamic_variables_global.py b/src/clearwater_modules/nsm1/dynamic_variables_global.py index 70269bf..414e58f 100644 --- a/src/clearwater_modules/nsm1/dynamic_variables_global.py +++ b/src/clearwater_modules/nsm1/dynamic_variables_global.py @@ -1,7 +1,10 @@ import clearwater_modules.base as base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.shared.processes as shared_processes +import clearwater_modules.tsm as tsm +import clearwater_modules.tsm.processes as tsm_processes + +import clearwater_modules.shared.processes as shared_processes @base.register_variable(models=NutrientBudget) class Variable(base.Variable): @@ -17,11 +20,102 @@ class Variable(base.Variable): process=shared_processes.compute_depth ) +Variable( + name='TwaterC', + long_name='Water Temperature', + units='C', + description='Water Temperature Degree Celsius', + use='dynamic', + process=tsm_processes.t_water_c +) + +Variable( + name='TwaterK', + long_name='Water Temperature K', + units='K', + description='Water temperature degree kelvin', + use='dynamic', + process=shared_processes.TwaterK +) + Variable( name='SOD_tc', - long_name='Sediment oxygen demand adjusted for temperature', - units='mg-O2/L/d', - description='Sediment oxygen demand adjusted for temperature', + long_name='Sediment Oxygen Demand at water temperature tc', + units='mg/L', + description='Sediment Oxygen Demand at water temperature tc', + use='dynamic', + process=shared_processes.SOD_tc +) + +Variable( + name='kah_20', + long_name='Hydraulic oxygen reaeration rate adjusted for hydraulics', + units='1/d', + description='Hydraulic oxygen reaeration rate adjusted for hydraulic parameters according to XX lit', + use='dynamic', + process=shared_processes.kah_20 +) + +Variable( + name='kah_tc', + long_name='Hydraulic oxygen reaeration rate adjusted for temperature', + units='1/d', + description='Hydraulic oxygen reaeration rate adjusted for temperature', + use='dynamic', + process=shared_processes.kah_tc +) + +Variable( + name='kaw_20', + long_name='Wind oxygen reaeration velocity adjusted for hydraulics', + units='m/d', + description='Wind oxygen reaeration velocity adjusted for hydraulic parameters according to XX lit', + use='dynamic', + process=shared_processes.kaw_20 +) + +Variable( + name='kaw_tc', + long_name='Wind oxygen reaeration velocity adjusted for temperature', + units='m/d', + description='Wind oxygen reaeration velocity adjusted for temperature', + use='dynamic', + process=shared_processes.kaw_tc +) + +Variable( + name='ka_tc', + long_name='Oxygen reaeration rate', + units='1/d', + description='Oxygen reaeration rate', + use='dynamic', + process=shared_processes.ka_tc +) + + +Variable( + name='L', + long_name='Light attenuation coefficient', + units='unitless', + description='Light attenuation coefficient', + use='dynamic', + process=shared_processes.L +) + +Variable( + name='PAR', + long_name='surface light intensity', + units='W/m2', + description='surface light intensity', + use='dynamic', + process=shared_processes.PAR +) + +Variable( + name='fdp', + long_name='Fraction phosphorus dissolved', + units='Unitless', + description='Fraction phosphorus dissolved', use='dynamic', - process=shared_processes.arrhenius_correction + process=shared_processes.fdp ) diff --git a/src/clearwater_modules/nsm1/model.py b/src/clearwater_modules/nsm1/model.py index 9cb6097..1da9c1d 100644 --- a/src/clearwater_modules/nsm1/model.py +++ b/src/clearwater_modules/nsm1/model.py @@ -19,34 +19,211 @@ class NutrientBudget(base.Model): def __init__( self, initial_state_values: Optional[base.InitialVariablesDict] = None, + updateable_static_variables: Optional[list[str]] = None, algae_parameters: Optional[dict[str, float]] = None, + alkalinity_parameters: Optional[dict[str, float]] = None, + balgae_parameters: Optional[dict[str, float]] = None, + carbon_parameters: Optional[dict[str, float]] = None, + CBOD_parameters: Optional[dict[str, float]] = None, + DOX_parameters: Optional[dict[str, float]] = None, + nitrogen_parameters: Optional[dict[str, float]] = None, + POM_parameters: Optional[dict[str, float]] = None, + N2_parameters: Optional[dict[str, float]] = None, + phosphorus_parameters: Optional[dict[str, float]] = None, + pathogen_parameters: Optional[dict[str, float]] = None, + global_parameters: Optional[dict[str, float]] = None, + global_vars: Optional[dict[str, float]] = None, track_dynamic_variables: bool = True, hotstart_dataset: Optional[xr.Dataset] = None, time_dim: Optional[str] = None, ) -> None: - self.__algae_parameters: constants.Algae = constants.DEFAULT_Algae + self.__algae_parameters: constants.AlgaeStaticVariables = constants.DEFAULT_ALGAE + self.__alkalinity_parameters: constants.AlkalinityStaticVariables = constants.DEFAULT_ALKALINITY + self.__balgae_parameters: constants.BalgaeStaticVariables = constants.DEFAULT_BALGAE + self.__carbon_parameters: constants.CarbonStaticVariables = constants.DEFAULT_CARBON + self.__CBOD_parameters: constants.CBODStaticVariables = constants.DEFAULT_CBOD + self.__DOX_parameters: constants.DOXStaticVariables = constants.DEFAULT_DOX + self.__nitrogen_parameters: constants.NitrogenStaticVariables = constants.DEFAULT_NITROGEN + self.__POM_parameters: constants.POMStaticVariables = constants.DEFAULT_POM + self.__N2_parameters: constants.N2StaticVariables = constants.DEFAULT_N2 + self.__phosphorus_parameters: constants.PhosphorusStaticVariables = constants.DEFAULT_PHOSPHORUS + self.__pathogen_parameters: constants.PathogenStaticVariables = constants.DEFAULT_PATHOGEN + self.__global_parameters: constants.PathogenStaticVariables = constants.DEFAULT_GLOBALPARAMETERS + self.__global_vars: constants.PathogenStaticVariables = constants.DEFAULT_GLOBALVARS + - if algae_parameters is None: - algae_parameters_parameters = {} + if algae_parameters is None: + algae_parameters = {} + if alkalinity_parameters is None: + alkalinity_parameters = {} + if balgae_parameters is None: + balgae_parameters = {} + if carbon_parameters is None: + carbon_parameters = {} + if CBOD_parameters is None: + CBOD_parameters = {} + if DOX_parameters is None: + DOX_parameters = {} + if nitrogen_parameters is None: + nitrogen_parameters = {} + if POM_parameters is None: + POM_parameters = {} + if N2_parameters is None: + N2_parameters = {} + if phosphorus_parameters is None: + phosphorus_parameters = {} + if pathogen_parameters is None: + pathogen_parameters = {} + if global_parameters is None: + global_parameters = {} + if global_vars is None: + global_vars = {} + # set default values for key, value in self.__algae_parameters.items(): self.__algae_parameters[key] = algae_parameters.get( key, value, ) + for key, value in self.__alkalinity_parameters.items(): + self.__alkalinity_parameters[key] = alkalinity_parameters.get( + key, + value, + ) + for key, value in self.__balgae_parameters.items(): + self.__balgae_parameters[key] = balgae_parameters.get( + key, + value, + ) + for key, value in self.__carbon_parameters.items(): + self.__carbon_parameters[key] = carbon_parameters.get( + key, + value, + ) + for key, value in self.__CBOD_parameters.items(): + self.__CBOD_parameters[key] = CBOD_parameters.get( + key, + value, + ) + for key, value in self.__DOX_parameters.items(): + self.__DOX_parameters[key] = DOX_parameters.get( + key, + value, + ) + for key, value in self.__nitrogen_parameters.items(): + self.__nitrogen_parameters[key] = nitrogen_parameters.get( + key, + value, + ) + for key, value in self.__POM_parameters.items(): + self.__POM_parameters[key] = POM_parameters.get( + key, + value, + ) + for key, value in self.__N2_parameters.items(): + self.__N2_parameters[key] = N2_parameters.get( + key, + value, + ) + for key, value in self.__phosphorus_parameters.items(): + self.__phosphorus_parameters[key] = phosphorus_parameters.get( + key, + value, + ) + for key, value in self.__pathogen_parameters.items(): + self.__pathogen_parameters[key] = pathogen_parameters.get( + key, + value, + ) + + for key, value in self.__global_parameters.items(): + self.__global_parameters[key] = global_parameters.get( + key, + value, + ) + + for key, value in self.__global_vars.items(): + self.__global_vars[key] = global_vars.get( + key, + value, + ) static_variable_values = { - **self.__algae_parameters} + **self.__algae_parameters, + **self.__alkalinity_parameters, + **self.__balgae_parameters, + **self.__carbon_parameters, + **self.__CBOD_parameters, + **self.__DOX_parameters, + **self.__nitrogen_parameters, + **self.__POM_parameters, + **self.__N2_parameters, + **self.__phosphorus_parameters, + **self.__pathogen_parameters, + **self.__global_parameters, + **self.__global_vars} + + # TODO: make sure this feature works -> test it, but post demo + #static_variable_values['use_sed_temp'] = use_sed_temp super().__init__( initial_state_values=initial_state_values, static_variable_values=static_variable_values, + updateable_static_variables=updateable_static_variables, track_dynamic_variables=track_dynamic_variables, hotstart_dataset=hotstart_dataset, time_dim=time_dim, ) @property - def algae_parameters(self) -> constants.algae: + def algae_parameters(self) -> constants.AlgaeStaticVariables: return self.__algae_parameters + + @property + def alkalinity_parameters(self) -> constants.AlkalinityStaticVariables: + return self.__alkalinity_parameters + + @property + def balgae_parameters(self) -> constants.BalgaeStaticVariables: + return self.__balgae_parameters + + @property + def carbon_parameters(self) -> constants.CarbonStaticVariables: + return self.__carbon_parameters + + @property + def CBOD_parameters(self) -> constants.CBODStaticVariables: + return self.__CBOD_parameters + + @property + def DOX_parameters(self) -> constants.DOXStaticVariables: + return self.__DOX_parameters + + @property + def nitrogen_parameters(self) -> constants.NitrogenStaticVariables: + return self.__nitrogen_parameters + + @property + def POM_parameters(self) -> constants.POMStaticVariables: + return self.__POM_parameters + + @property + def N2_parameters(self) -> constants.N2StaticVariables: + return self.__N2_parameters + + @property + def phosphorus_parameters(self) -> constants.PhosphorusStaticVariables: + return self.__phosphorus_parameters + + @property + def pathogen_parameters(self) -> constants.PathogenStaticVariables: + return self.__pathogen_parameters + + @property + def global_parameters(self) -> constants.GlobalParameters: + return self.__global_parameters + + @property + def global_vars(self) -> constants.GlobalVars: + return self.__global_vars diff --git a/src/clearwater_modules/nsm1/n2/dynamic_variables_n2.py b/src/clearwater_modules/nsm1/n2/dynamic_variables.py similarity index 84% rename from src/clearwater_modules/nsm1/n2/dynamic_variables_n2.py rename to src/clearwater_modules/nsm1/n2/dynamic_variables.py index 5141258..9734417 100644 --- a/src/clearwater_modules/nsm1/n2/dynamic_variables_n2.py +++ b/src/clearwater_modules/nsm1/n2/dynamic_variables.py @@ -5,7 +5,7 @@ import clearwater_modules.shared.processes as shared_processes from clearwater_modules import base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.n2.n2_processes as n2_processes +import clearwater_modules.nsm1.n2.processes as processes @base.register_variable(models=NutrientBudget) @@ -18,7 +18,7 @@ class Variable(base.Variable): units='mol/L/atm', description='Henrys law constant temperature corrected', use='dynamic', - process=n2_processes.KHN2_tc + process=processes.KHN2_tc ) Variable( @@ -27,7 +27,7 @@ class Variable(base.Variable): units='atm', description='Partial pressure water vapor', use='dynamic', - process=n2_processes.P_wv + process=processes.P_wv ) Variable( @@ -36,7 +36,7 @@ class Variable(base.Variable): units='mg-N/L', description='N2 at saturation f(Twater and atm pressure)', use='dynamic', - process=n2_processes.N2sat + process=processes.N2sat ) Variable( @@ -45,7 +45,7 @@ class Variable(base.Variable): units='mg-N/L/d', description='Change in N2 air concentration', use='dynamic', - process=n2_processes.dN2dt + process=processes.dN2dt ) Variable( @@ -54,5 +54,5 @@ class Variable(base.Variable): units='%', description='Total dissolved gas', use='dynamic', - process=n2_processes.TDG + process=processes.TDG ) \ No newline at end of file diff --git a/src/clearwater_modules/nsm1/n2/n2_processes.py b/src/clearwater_modules/nsm1/n2/processes.py similarity index 78% rename from src/clearwater_modules/nsm1/n2/n2_processes.py rename to src/clearwater_modules/nsm1/n2/processes.py index 2b1509e..75341f4 100644 --- a/src/clearwater_modules/nsm1/n2/n2_processes.py +++ b/src/clearwater_modules/nsm1/n2/processes.py @@ -1,17 +1,11 @@ -import math -from clearwater_modules.shared.processes import arrhenius_correction, celsius_to_kelvin +""" +File contains dynamic variables related to the N2 module +""" + import numba import xarray as xr - -@numba.njit -def TwaterK( - TwaterC : xr.DataArray, -) -> xr.DataArray : - """Calculate temperature in kelvin (K) - Args: - TwaterC: water temperature celcius (C) - """ - return celsius_to_kelvin(TwaterK) +from clearwater_modules.shared.processes import arrhenius_correction +import math @numba.njit def KHN2_tc( @@ -47,7 +41,7 @@ def P_wv( return math.exp(11.8571 - (3840.70 / TwaterK) - (216961.0 / (TwaterK**2))) @numba.njit -#N2 saturation + def N2sat( KHN2_tc : xr.DataArray, pressure_atm: xr.DataArray, @@ -63,14 +57,13 @@ def N2sat( """ N2sat = 2.8E+4 * KHN2_tc * 0.79 * (pressure_atm - P_wv) - if (N2sat < 0.0) : #Trap saturation concentration to ensure never negative - N2sat = 0.0 + N2sat = xr.where(N2sat < 0.0,0.0,N2sat) #Trap saturation concentration to ensure never negative return N2sat @numba.njit def dN2dt( - ka_tc : xr.DataArray, #TODO this should be calculated in Carbon based on kah_tc and kaw_tc + ka_tc : xr.DataArray, N2sat : xr.DataArray, N2: xr.DataArray, ) -> xr.DataArray: @@ -86,7 +79,7 @@ def dN2dt( return 1.034 * ka_tc * (N2sat - N2) @numba.njit -def N2_new( +def N2( N2: xr.DataArray, dN2dt : xr.DataArray, ) -> xr.DataArray: @@ -105,7 +98,7 @@ def TDG( N2: xr.DataArray, N2sat : xr.DataArray, DOX: xr.DataArray, - O2sat: xr.DataArray, + DOX_sat: xr.DataArray, use_DOX: bool, ) -> xr.DataArray: @@ -115,12 +108,8 @@ def TDG( N2: Nitrogen concentration air (mg-N/L) N2sat: N2 at saturation f(Twater and atm pressure) (mg-N/L) DOX: Dissolved oxygen concentration (mg-O2/L) - O2sat: O2 at saturation f(Twater and atm pressure) (mg-O2/L) + DOX_sat: O2 at saturation f(Twater and atm pressure) (mg-O2/L) use_DOX: true/false use dissolved oxygen module (true/false) """ - if use_DOX : - TDG = (79.0 * N2 / N2sat) + (21.0 * DOX / O2sat) - else: - TDG = N2/N2sat - return TDG \ No newline at end of file + return xr.where(use_DOX,(79.0 * N2 / N2sat) + (21.0 * DOX / DOX_sat), N2/N2sat) \ No newline at end of file diff --git a/src/clearwater_modules/nsm1/n2/static_variables_n2.py b/src/clearwater_modules/nsm1/n2/static_variables.py similarity index 62% rename from src/clearwater_modules/nsm1/n2/static_variables_n2.py rename to src/clearwater_modules/nsm1/n2/static_variables.py index fa2769f..2a0da2e 100644 --- a/src/clearwater_modules/nsm1/n2/static_variables_n2.py +++ b/src/clearwater_modules/nsm1/n2/static_variables.py @@ -1,12 +1,12 @@ """ -File includes static variables only used in N2 module +File contains static variables related to the N2 module """ import clearwater_modules.base as base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.n2.n2_processes as n2_processes @base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... + diff --git a/src/clearwater_modules/nsm1/nitrogen/dynamic_variables.py b/src/clearwater_modules/nsm1/nitrogen/dynamic_variables.py new file mode 100644 index 0000000..1135f2d --- /dev/null +++ b/src/clearwater_modules/nsm1/nitrogen/dynamic_variables.py @@ -0,0 +1,295 @@ +""" +File contains dynamic variables related to the Nitrogen module +""" + +import clearwater_modules.shared.processes as shared_processes +from clearwater_modules import base +from clearwater_modules.nsm1.model import NutrientBudget +import clearwater_modules.nsm1.nitrogen.processes as processes + + +@base.register_variable(models=NutrientBudget) +class Variable(base.Variable): + ... + + +Variable( + name='knit_tc', + long_name='Nitrification rate ammonia decay', + units='1/d', + description='Nitrification rate ammonia decay temperature correction', + use='dynamic', + process=processes.knit_tc +) + +Variable( + name='rnh4_tc', + long_name='Sediment release rate of NH4', + units='1/d', + description=' Sediment release rate of NH4 temperature correction', + use='dynamic', + process=processes.rnh4_tc +) + +Variable( + name='vno3_tc', + long_name='Sediment denitrification velocity', + units='m/d', + description='Sediment denitrification velocity temperature correction', + use='dynamic', + process=processes.vno3_tc +) + +Variable( + name='kon_tc', + long_name='Decay rate of OrgN to NH4', + units='1/d', + description='Decay rate of OrgN to NH4 temperature correction', + use='dynamic', + process=processes.kon_tc +) + +Variable( + name='kdnit_tc', + long_name='Denitrification rate', + units='1/d', + description='Denitrification rate temperature correction', + use='dynamic', + process=processes.kdnit_tc +) + +Variable( + name='ApUptakeFr_NH4', + long_name='Fraction of actual floating algal uptake from ammonia pool', + units='unitless', + description='Fraction of actual floating algal uptake from ammonia pool', + use='dynamic', + process=processes.ApUptakeFr_NH4 +) + +Variable( + name='ApUptakeFr_NO3', + long_name='Fraction of actual floating algal uptake from nitrate pool', + units='unitless', + description='Fraction of actual floating algal uptake from nitrate pool', + use='dynamic', + process=processes.ApUptakeFr_NO3 +) + +Variable( + name='AbUptakeFr_NH4', + long_name='Fraction of actual benthic algal uptake from ammonia pool', + units='unitless', + description='Fraction of actual benthic algal uptake from ammonia pool', + use='dynamic', + process=processes.AbUptakeFr_NH4 +) + +Variable( + name='AbUptakeFr_NO3', + long_name='Fraction of actual benthic algal uptake from nitrate pool', + units='unitless', + description='Fraction of actual benthic algal uptake from nitrate pool', + use='dynamic', + process=processes.AbUptakeFr_NO3 +) + +Variable( + name='ApDeath_OrgN', + long_name='Algae -> OrgN', + units='mg-N/L/d', + description='Algae conversion to Organic nitrogen', + use='dynamic', + process=processes.ApDeath_OrgN +) + +Variable( + name='AbDeath_OrgN', + long_name='Benthic Algae -> OrgN', + units='mg-N/L/d', + description='Benthic algae conversion to Organic nitrogen', + use='dynamic', + process=processes.AbDeath_OrgN +) + +Variable( + name='OrgN_NH4_Decay', + long_name='OrgN -> NH4', + units='mg-N/L/d', + description='Organic nitrogen to ammonium decay', + use='dynamic', + process=processes.OrgN_NH4_Decay +) + +Variable( + name='OrgN_Settling', + long_name='OrgN -> bed', + units='mg-N/L/d', + description='Organic nitrogen to bed settling', + use='dynamic', + process=processes.OrgN_Settling +) + +Variable( + name='dOrgNdt', + long_name='Change in organic nitrogen', + units='mg-N/L', + description='Change in organic nitrogen', + use='dynamic', + process=processes.dOrgNdt +) + +Variable( + name='NH4_Nitrification', + long_name='NH4 -> NO3 Nitrification', + units='mg-N/L/d', + description='NH4 Nitrification', + use='dynamic', + process=processes.NH4_Nitrification +) + +Variable( + name='NH4fromBed', + long_name='bed -> NH4 (diffusion)', + units='mg-N/L/d', + description='Sediment bed release of NH4', + use='dynamic', + process=processes.NH4fromBed +) + +Variable( + name='NH4_ApRespiration', + long_name='Floating algae -> NH4', + units='mg-N/L/d', + description='Floating algae to NH4', + use='dynamic', + process=processes.NH4_ApRespiration +) + +Variable( + name='NH4_ApGrowth', + long_name='NH4 -> Floating algae', + units='mg-N/L/d', + description='NH4 uptake to algae', + use='dynamic', + process=processes.NH4_ApGrowth +) + +Variable( + name='NH4_AbRespiration', + long_name='Benthic algae -> NH4', + units='mg-N/L/d', + description='Benthic algae release of NH4', + use='dynamic', + process=processes.NH4_AbRespiration +) + +Variable( + name='NH4_AbGrowth', + long_name='NH4 -> Benthic Algae', + units='mg-N/L/d', + description='Benthic algae uptake of NH4', + use='dynamic', + process=processes.NH4_AbGrowth +) + +Variable( + name='dNH4dt', + long_name='Change in ammonium concentration', + units='mg-N/L', + description='Change in ammonium concentration', + use='dynamic', + process=processes.dNH4dt +) + +Variable( + name='NO3_Denit', + long_name='NO3 -> Loss', + units='mg-N/L/d', + description='NO3 loss from denitrification', + use='dynamic', + process=processes.NO3_Denit +) + +Variable( + name='NO3_BedDenit', + long_name='Sediment denitrification', + units='mg-N/L/d', + description='Sediment denitrification', + use='dynamic', + process=processes.NO3_BedDenit +) + +Variable( + name='NO3_ApGrowth', + long_name='NO3 -> Floating algae', + units='mg-N/L/d', + description='NO3 uptake to floating algae', + use='dynamic', + process=processes.NO3_ApGrowth +) + +Variable( + name='NO3_AbGrowth', + long_name='NO3 -> Benthic algae', + units='mg-N/L/d', + description='NO3 uptake to benthic algae', + use='dynamic', + process=processes.NO3_AbGrowth +) + +Variable( + name='dNO3dt', + long_name='Change in nitrate concentration', + units='mg-N/L', + description='Change in nitrate concentration', + use='dynamic', + process=processes.dNO3dt +) + +Variable( + name='DIN', + long_name='Dissolve inorganic nitrogen', + units='mg-N/L', + description='Dissolve inorganic nitrogen', + use='dynamic', + process=processes.DIN +) + +Variable( + name='TON', + long_name='Total organic nitrogen', + units='mg-N/L', + description='Total organic nitrogen', + use='dynamic', + process=processes.TON +) + +Variable( + name='TKN', + long_name='Total kjeldhl nitrogen', + units='mg-N/L', + description='Total kjeldhl nitrogen', + use='dynamic', + process=processes.TKN +) + +Variable( + name='TN', + long_name='Total nitrogen', + units='mg-N/L', + description='Total nitrogen', + use='dynamic', + process=processes.TN +) + + + +Variable( + name='NitrificationInhibition', + long_name='Nitrification Inhibitation (limits nitrification under low DO conditions)', + units='unitless', + description='Nitrification Inhibitation (limits nitrification under low DO conditions)', + use='dynamic', + process=processes.NitrificationInhibition +) \ No newline at end of file diff --git a/src/clearwater_modules/nsm1/nitrogen/dynamic_variables_nitrogen.py b/src/clearwater_modules/nsm1/nitrogen/dynamic_variables_nitrogen.py deleted file mode 100644 index 3ffbfc9..0000000 --- a/src/clearwater_modules/nsm1/nitrogen/dynamic_variables_nitrogen.py +++ /dev/null @@ -1,158 +0,0 @@ -""" -File includes dynamic variables computed in Algae module. Dynamic variables may be accessed by other modules. -""" - -import clearwater_modules.shared.processes as shared_processes -from clearwater_modules import base -from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.nitrogen.nitrogen_processes as nitrogen_processes - - -@base.register_variable(models=NutrientBudget) -class Variable(base.Variable): - ... - - -Variable( - name='knit_tc', - long_name='Nitrification rate ammonia decay', - units='1/d', - description='Nitrification rate ammonia decay temperature correction', - use='dynamic', - process=nitrogen_processes.knit_tc -) - -Variable( - name='rnh4_tc', - long_name='Sediment release rate of NH4', - units='1/d', - description=' Sediment release rate of NH4 temperature correction', - use='dynamic', - process=nitrogen_processes.rnh4_tc -) - -Variable( - name='vno3_tc', - long_name='Sediment denitrification velocity', - units='m/d', - description='Sediment denitrification velocity temperature correction', - use='dynamic', - process=nitrogen_processes.vno3_tc -) - -Variable( - name='kon_tc', - long_name='Decay rate of OrgN to NH4', - units='1/d', - description='Decay rate of OrgN to NH4 temperature correction', - use='dynamic', - process=nitrogen_processes.kon_tc -) - -Variable( - name='kdnit_tc', - long_name='Denitrification rate', - units='1/d', - description='Denitrification rate temperature correction', - use='dynamic', - process=nitrogen_processes.kdnit_tc -) - -Variable( - name='ApUptakeFr_NH4', - long_name='Fraction of actual floating algal uptake from ammonia pool', - units='unitless', - description='Fraction of actual floating algal uptake from ammonia pool', - use='dynamic', - process=nitrogen_processes.ApUptakeFr_NH4 -) - -Variable( - name='ApUptakeFr_NO3', - long_name='Fraction of actual floating algal uptake from nitrate pool', - units='unitless', - description='Fraction of actual floating algal uptake from nitrate pool', - use='dynamic', - process=nitrogen_processes.ApUptakeFr_NO3 -) - -Variable( - name='AbUptakeFr_NH4', - long_name='Fraction of actual benthic algal uptake from ammonia pool', - units='unitless', - description='Fraction of actual benthic algal uptake from ammonia pool', - use='dynamic', - process=nitrogen_processes.AbUptakeFr_NH4 -) - -Variable( - name='AbUptakeFr_NO3', - long_name='Fraction of actual benthic algal uptake from nitrate pool', - units='unitless', - description='Fraction of actual benthic algal uptake from nitrate pool', - use='dynamic', - process=nitrogen_processes.AbUptakeFr_NO3 -) - -Variable( - name='dOrgNdt', - long_name='Change in organic nitrogen', - units='mg-N/L', - description='Change in organic nitrogen', - use='dynamic', - process=nitrogen_processes.dOrgNdt -) - -Variable( - name='dNH4dt', - long_name='Change in ammonium concentration', - units='mg-N/L', - description='Change in ammonium concentration', - use='dynamic', - process=nitrogen_processes.dNH4dt -) - -Variable( - name='dNO3dt', - long_name='Change in nitrate concentration', - units='mg-N/L', - description='Change in nitrate concentration', - use='dynamic', - process=nitrogen_processes.dNO3dt -) - -Variable( - name='DIN', - long_name='Dissolve inorganic nitrogen', - units='mg-N/L', - description='Dissolve inorganic nitrogen', - use='dynamic', - process=nitrogen_processes.DIN -) - -Variable( - name='TON', - long_name='Total organic nitrogen', - units='mg-N/L', - description='Total organic nitrogen', - use='dynamic', - process=nitrogen_processes.TON -) - -Variable( - name='TKN', - long_name='Total kjeldhl nitrogen', - units='mg-N/L', - description='Total kjeldhl nitrogen', - use='dynamic', - process=nitrogen_processes.TKN -) - -Variable( - name='TN', - long_name='Total nitrogen', - units='mg-N/L', - description='Total nitrogen', - use='dynamic', - process=nitrogen_processes.TN -) diff --git a/src/clearwater_modules/nsm1/nitrogen/nitrogen_processes.py b/src/clearwater_modules/nsm1/nitrogen/processes.py similarity index 51% rename from src/clearwater_modules/nsm1/nitrogen/nitrogen_processes.py rename to src/clearwater_modules/nsm1/nitrogen/processes.py index 1ced209..32f7409 100644 --- a/src/clearwater_modules/nsm1/nitrogen/nitrogen_processes.py +++ b/src/clearwater_modules/nsm1/nitrogen/processes.py @@ -1,10 +1,11 @@ """ File contains process to calculate nitrogen species concentration and associated dependent variables """ -import math -from clearwater_modules.shared.processes import arrhenius_correction + import numba import xarray as xr +from clearwater_modules.shared.processes import arrhenius_correction +import math @numba.njit def knit_tc( @@ -104,19 +105,13 @@ def ApUptakeFr_NH4( ApUptakeFr_NH4 = 0 # set value of UptakeFr_NH4/NO3 for special conditions - if use_NH4 and not use_NO3: - ApUptakeFr_NH4 = 1.0 - if not use_NH4 and use_NO3: - ApUptakeFr_NH4 = 0.0 - if not use_NH4 and not use_NO3: - ApUptakeFr_NH4 = 0.5 - - # Calculating Nitrogen Kinetics - if use_Algae and use_NH4 and use_NO3: - ApUptakeFr_NH4 = PN * NH4 / (PN * NH4 + (1.0 - PN) * NO3) + ApUptakeFr_NH4 = xr.where(use_NH4 and not use_NO3, 1.0, + xr.where(not use_NH4 and use_NO3, 0.0, + xr.where(not use_NH4 and not use_NO3, 0.5, + xr.where(use_Algae and use_NH4 and use_NO3, PN * NH4 / (PN * NH4 + (1.0 - PN) * NO3), "NaN")))) + # Check for case when NH4 and NO3 are very small. If so, force uptake_fractions appropriately. - if math.isnan(ApUptakeFr_NH4): - ApUptakeFr_NH4 = PN + ApUptakeFr_NH4 = xr.where(math.isnan(ApUptakeFr_NH4),PN,ApUptakeFr_NH4) return ApUptakeFr_NH4 @@ -155,23 +150,12 @@ def AbUptakeFr_NH4( NO3: Nitrate water concentration (mg-N/L) """ AbUptakeFr_NH4 = 0 - - # set value of UptakeFr_NH4/NO3 for special conditions - if use_NH4 and not use_NO3: - AbUptakeFr_NH4 = 1.0 - if not use_NH4 and use_NO3: - AbUptakeFr_NH4 = 0.0 - if not use_NH4 and not use_NO3: - AbUptakeFr_NH4 = 0.5 - - # Check for benthic and recompute if necessary - if use_Balgae and use_NH4 and use_NO3: - AbUptakeFr_NH4 = (PNb * NH4) / (PNb * NH4 + (1.0 - PNb) * NO3) - AbUptakeFr_NO3 = 1 - AbUptakeFr_NH4 - - # Check if NH4 and NO3 are very small. If so, force uptake_fractions appropriately. - if math.isnan(AbUptakeFr_NH4): - AbUptakeFr_NH4 = PNb + AbUptakeFr_NH4 = xr.where(use_NH4 and not use_NO3, 1.0, + xr.where(not use_NH4 and use_NO3, 0.0, + xr.where(not use_NH4 and not use_NO3, 0.5, + xr.where(use_Balgae and use_NH4 and use_NO3, (PNb * NH4) / (PNb * NH4 + (1.0 - PNb) * NO3), "NaN")))) + + AbUptakeFr_NH4 = xr.where(math.isnan(AbUptakeFr_NH4),PNb,AbUptakeFr_NH4) return AbUptakeFr_NH4 @@ -188,212 +172,424 @@ def AbUptakeFr_NO3( return 1 - AbUptakeFr_NH4 - @numba.njit -def dOrgNdt( - use_OrgN: bool, - use_Algae: bool, - use_Balgae: bool, +def OrgN_NH4_Decay( kon_tc: xr.DataArray, OrgN: xr.DataArray, + use_OrgN: bool +) -> xr.DataArray: + """Calculate OrgN_NH4: OrgN -> NH4 (mg-N/L/d) + + Args: + kon_tc: Decay rate of organic nitrogen to nitrate with temperature correction (1/d), + OrgN: Concentration of organic nitrogen (mg-N/L) + use_OrgN: true/false use organic nitrogen (t/f) + """ + + return xr.where(use_OrgN, kon_tc * OrgN,0) + +@numba.njit +def OrgN_Settling( vson: xr.DataArray, depth: xr.DataArray, + OrgN: xr.DataArray, +) -> xr.DataArray: + """Calculate OrgN_Settling: OrgN -> bed (mg-N/L/d) + + Args: + vson: Organic nitrogen settling velocity (m/d) + depth: water depth (m) + """ + + return vson / depth * OrgN + +@numba.njit +def ApDeath_OrgN( + use_Algae: bool, rna: xr.DataArray, + ApDeath: xr.DataArray, +) -> xr.DataArray: + """Calculate ApDeath_OrgN: Algae -> OrgN (mg-N/L/d) + + Args: + use_Algae: true/false to use algae module (unitless) + rna: Algal N: Chla ratio (mg-N/ug-Chla) + ApDeath: Algal death rate (ug-Chla/L/d) + """ + + return xr.where(use_Algae, rna * ApDeath, 0.0) + +@numba.njit +def AbDeath_OrgN( + use_Balgae: bool, rnb: xr.DataArray, Fw: xr.DataArray, Fb: xr.DataArray, - ApDeath: xr.DataArray, + depth: xr.DataArray, AbDeath: xr.DataArray, - ) -> xr.DataArray: - """Calculate dOrgNdt: Change in Organic Nitrogen (mg-N/L) + """Calculate ApDeath_OrgN: Algae -> OrgN (mg-N/L/d) Args: - use_OrgN: true/false to use organic nitrogen module (unitless) - use_Algae: true/false to use algae module (unitless) use_Balgae: true/false to use benthic algae module (unitless), - kon_tc: Decay rate of organic nitrogen to nitrate with temperature correction (1/d), - OrgN: Concentration of organic nitrogen (mg-N/L) - vson: Organic nitrogen settling velocity (m/d) - depth: water depth (m) - rna: Algal N: Chla ratio (mg-N/ug-Chla) rnb: Benthic algal N: Benthic Algal Dry Weight (mg-N/mg-D) Fw: Fraction benthic algae mortality into water column (unitless) Fb: Fraction of bottom area for benthic algae (unitless) - ApDeath: Algal death rate (ug-Chla/L/d) + depth: water depth (m) AbDeath: Benthic algal death rate (g/m^2/d) + """ - Organic Nitrogen (mg-N/d*L) - dOrgN/dt = Algae_OrgN (xr.DataArraying Algae -> OrgN) - - OrgN_NH4_Decay (OrgN -> NH4) - - OrgN_Settling (OrgN -> bed) - + Benthic Death (Benthic Algae -> OrgN) + return xr.where(use_Balgae, rnb * Fw * Fb * AbDeath / depth, 0.0) + +@numba.njit +def dOrgNdt( + use_OrgN: bool, + ApDeath_OrgN: xr.DataArray, + AbDeath_OrgN: xr.DataArray, + OrgN_NH4_Decay: xr.DataArray, + OrgN_Settling: xr.DataArray, + +) -> xr.DataArray: + """Calculate dOrgNdt: Change in Organic Nitrogen (mg-N/L/d) + + Args: + use_OrgN: true/false to use organic nitrogen module (unitless) + ApDeath_OrgN: Algae -> OrgN (mg-N/L/d) + AbDeath_OrgN: Benthic Algae -> OrgN (mg-N/L/d) + OrgN_NH4_Decay: OrgN -> NH4 (mg-N/L/d) + OrgN_Settling: OrgN -> bed (mg-N/L/d) """ - if use_OrgN: - OrgN_NH4_Decay = kon_tc * OrgN - OrgN_Settling = vson / depth * OrgN - if use_Algae: - ApDeath_OrgN = rna * ApDeath - else: - ApDeath_OrgN = 0.0 + return xr.where(use_OrgN, ApDeath_OrgN + AbDeath_OrgN - OrgN_NH4_Decay - OrgN_Settling,0) - if use_Balgae: - AbDeath_OrgN = rnb * Fw * Fb * AbDeath / depth - else: - AbDeath_OrgN = 0.0 +@numba.njit +def OrgN( + OrgN: xr.DataArray, + dOrgNdt: xr.DataArray, + timestep: xr.DataArray, - dOrgNdt = ApDeath_OrgN + AbDeath_OrgN - OrgN_NH4_Decay - OrgN_Settling - else: - dOrgNdt = 0 +) -> xr.DataArray: + """Calculate OrgN: New concentration OrgN (mg-N/L) - return dOrgNdt + Args: + OrgN: Concentration of organic nitrogen (mg-N/L) + dOrgNdt: Change in Organic Nitrogen (mg-N/L/d) + timestep: current iteration timestep (d) + """ + + return OrgN + dOrgNdt*timestep @numba.njit -def dNH4dt( - use_OrgN: bool, - use_Algae: bool, - use_Balgae: bool, +def NitrificationInhibition( use_DOX: bool, - use_SedFlux: bool, - use_NH4: bool, - depth: xr.DataArray, - rna: xr.DataArray, - rnb: xr.DataArray, - - Fb: xr.DataArray, KNR: xr.DataArray, DOX: xr.DataArray, + +) -> xr.DataArray: + """Calculate NitrificationInhibition: Nitrification Inhibitation (limits nitrification under low DO conditions) + + Args: + KNR: Oxygen inhabitation factor for nitrification (mg-O2/L), + DOX: Dissolved oxygen concentration (mg-O2/L), + use_DOX: true/false to use dissolve oxygen module (unitless), + + """ + + return xr.where (use_DOX, 1.0 - math.exp(-KNR * DOX), 1.0) + +@numba.njit +def NH4_Nitrification( + NitrificationInhibition: xr.DataArray, NH4: xr.DataArray, + knit_tc: xr.DataArray, + use_NH4: xr.DataArray + +) -> xr.DataArray: + """Calculate NH4_Nitrification: NH4 -> NO3 Nitrification (mg-N/L/day) + + Args: + NitrificationInhibition: Nitrification Inhibitation (limits nitrification under low DO conditions) + knit_tc: Nitrification rate ammonia decay NH4 to NO3 temperature correction (1/d). + NH4: Ammonium concentration (mg-N/L), + """ + + return xr.where(use_NH4,NitrificationInhibition * knit_tc * NH4,0) + +@numba.njit +def NH4fromBed( + use_SedFlux: bool, JNH4: xr.DataArray, + depth: xr.DataArray, + rnh4_tc: xr.DataArray, + +) -> xr.DataArray: + """Calculate NH4fromBed: bed -> NH4 (diffusion) (mg-N/L/day) + + Args: + use_SedFlux: true/false to use sediment flux module (unitless), + depth: water depth (m), + JNH4: Sediment water flux of ammonium (g-N/m^2/d), + rnh4_tc: Sediment release rate of NH4 temperature correction(1/d). + """ + + return xr.where(use_SedFlux, JNH4 / depth, rnh4_tc / depth) + +@numba.njit +def NH4_ApRespiration( + use_Algae: bool, ApRespiration: xr.DataArray, + rna: xr.DataArray, + +) -> xr.DataArray: + """Calculate NH4_ApRespiration: Floating algae -> NH4 (mg-N/L/day) + + Args: + use_Algae: true/false to use algae module (unitless), + rna: Algal N: Chla ratio (mg-N/ug-Chla), + ApRespiration: Algal respiration rate (ug-Chla/L/d), + """ + + return xr.where (use_Algae, rna * ApRespiration, 0.0) + +@numba.njit +def NH4_ApGrowth( + use_Algae: bool, ApGrowth: xr.DataArray, - AbRespiration: xr.DataArray, - AbGrowth: xr.DataArray, + rna: xr.DataArray, + ApUptakeFr_NH4: xr.DataArray, ) -> xr.DataArray: - """Calculate dNH4dt: Change in Ammonium (mg-N/L) + """Calculate NH4_ApGrowth: NH4 -> Floating algae (mg-N/L/day) Args: - use_OrgN: true/false to use organic nitrogen module (unitless), use_Algae: true/false to use algae module (unitless), + rna: Algal N: Chla ratio (mg-N/ug-Chla), + ApGrowth: Algal growth rate (ug-Chla/L/d), + ApUptakeFr_NH4: Fraction of actual xr.DataArraying algal uptake from ammonia pool + """ + + return xr.where(use_Algae, ApUptakeFr_NH4 * rna * ApGrowth, 0.0) + +@numba.njit +def NH4_AbRespiration( + use_Balgae: bool, + rnb: xr.DataArray, + AbRespiration: xr.DataArray, + +) -> xr.DataArray: + """Calculate NH4_AbRespiration: Benthic algae -> NH4 (mg-N/L/day) + + Args: use_Balgae: true/false to use benthic algae module (unitless), - use_DOX: true/false to use dissolve oxygen module (unitless), - use_SedFlux: true/false to use sediment flux module (unitless), - use_NH4: true/false to use ammonium module (unitless), + rnb: xr.DataArray, + AbRespiration: Benthic algal respiration rate (g/m^2/d), + """ + # TODO changed the calculation for respiration from the inital FORTRAN due to conflict with the reference guide - depth: water depth (m), - rna: Algal N: Chla ratio (mg-N/ug-Chla), - rnb: Benthic algal N: Benthic Algal Dry Weight (mg-N/mg-D), + return xr.where(use_Balgae, rnb * AbRespiration, 0.0 ) +@numba.njit +def NH4_AbGrowth( + use_Balgae: bool, + rnb: xr.DataArray, + AbGrowth: xr.DataArray, + AbUptakeFr_NH4: xr.DataArray, + Fb: xr.DataArray, + depth: xr.DataArray, + +) -> xr.DataArray: + """Calculate NH4_AbGrowth: NH4 -> Benthic Algae (g-N/L/day) + + Args: + use_Balgae: true/false to use benthic algae module (unitless), + rnb: xr.DataArray, + AbGrowth: Benthic alga growth rate (g/m^2/d), + depth: water depth (m), Fb: Fraction of bottom area for benthic algae (unitless), - KNR: Oxygen inhabitation factor for nitrification (mg-O2/L), - DOX: Dissolved oxygen concentration (mg-O2/L), - NH4: Ammonium concentration (mg-N/L), - JNH4: Sediment water flux of ammonium (g-N/m^2/d), + AbUptakeFr_NH4: Fraction of actual benthic algal uptake from ammonia pool + """ - ApRespiration: Algal respiration rate (ug-Chla/L/d), - ApGrowth: Algal growth rate (ug-Chla/L/d), - AbRespiration: Benthic algal respiration rate (g/m^2/d), - AbGrowth: Benthic alga growth rate (g/m^2/d), + return xr.where(use_Balgae,(AbUptakeFr_NH4 * rnb * Fb * AbGrowth) / depth, 0.0 ) + +@numba.njit +def dNH4dt( + use_NH4: bool, + NH4_Nitrification: xr.DataArray, + NH4fromBed: xr.DataArray, + NH4_ApRespiration: xr.DataArray, + NH4_ApGrowth: xr.DataArray, + NH4_AbRespiration: xr.DataArray, + NH4_AbGrowth: xr.DataArray, + + +) -> xr.DataArray: + """Calculate dNH4dt: Change in Ammonium (mg-N/L) + + Args: + use_OrgN: true/false to use organic nitrogen module (unitless), + use_NH4: true/false to use ammonium module (unitless), + NH4_Nitrification: NH4 -> NO3 Nitrification (mg-N/L/day) + NH4fromBed: bed -> NH4 (diffusion) (mg-N/L/day) + NH4_ApRespiration: Floating algae -> NH4 (mg-N/L/day) + NH4_ApGrowth: NH4 -> Floating algae (mg-N/L/day) + NH4_AbRespiration: Benthic algae -> NH4 (mg-N/L/day) + NH4_AbGrowth: NH4 -> Benthic Algae (g-N/L/day) Ammonia Nitrogen (NH4) (mg-N/day*L) - dNH4/dt = OrgN_NH4_Decay (OrgN -> NH4) + dNH4/dt = OrgN_NH4_Decay + (OrgN -> NH4) - NH4 Oxidation (NH4 -> NO3) - NH4AlgalUptake (NH4 -> xr.DataArraying Algae) + Benthos NH4 (Benthos -> NH4) - Benthic Algae Uptake (NH4 -> Benthic Algae) """ - NitrificationInhibition = 0 - if use_NH4: - if use_DOX: - NitrificationInhibition = 1.0 - math.exp(-KNR * DOX) - else: - NitrificationInhibition = 1.0 + return xr.where(use_NH4, OrgN_NH4_Decay - NH4_Nitrification + NH4fromBed + NH4_ApRespiration - NH4_ApGrowth + NH4_AbRespiration - NH4_AbGrowth, 0.0) - NH4_Nitrification = NitrificationInhibition * knit_tc * NH4 +@numba.njit +def NH4( + NH4: xr.DataArray, + dNH4dt: xr.DataArray, + timestep: xr.DataArray, - if use_SedFlux: - NH4fromBed = JNH4 / depth - else: - NH4fromBed = rnh4_tc / depth +) -> xr.DataArray: + """Calculate NH4: New concentration NH4 (mg-N/L) - if use_Algae: - NH4_ApRespiration = rna * ApRespiration - NH4_ApGrowth = ApUptakeFr_NH4 * rna * ApGrowth - else: - NH4_ApRespiration = 0.0 - NH4_ApGrowth = 0.0 + Args: + NH4: Concentration of NH4 (mg-N/L) + dNH4dt: Change in NH4 (mg-N/L/d) + timestep: current iteration timestep (d) - if use_Balgae: - # TODO changed the calculation for respiration from the inital FORTRAN due to conflict with the reference guide - NH4_AbRespiration = rnb * AbRespiration - NH4_AbGrowth = (AbUptakeFr_NH4 * rnb * Fb * AbGrowth) / depth - else: - NH4_AbRespiration = 0.0 - NH4_AbGrowth = 0.0 + """ - if not use_OrgN: - OrgN_NH4_Decay = 0.0 + return NH4 + dNH4dt*timestep - dNH4dt = OrgN_NH4_Decay - NH4_Nitrification + NH4fromBed + \ - NH4_ApRespiration - NH4_ApGrowth + NH4_AbRespiration - NH4_AbGrowth - else: - dNH4dt = 0 +@numba.njit +def NO3_Denit( + use_DOX: bool, + DOX: xr.DataArray, + KsOxdn: xr.DataArray, + kdnit_tc: xr.DataArray, + NO3: xr.DataArray, - return dNH4dt +) -> xr.DataArray: + """Calculate NO3_Denit: NO3 -> Loss (mg-N/L/day) + Args: + use_DOX: true/false to use dissolve oxygen module (unitless), + KsOxdn: Half-saturation oxygen inhibition constant for denitrification (mg-O2/L) + DOX: Dissolved oxygen concentration (mg-O2/L), + NO3: Nitrate concentration (mg-N/L), + kdnit_tc: Denitrification rate temperature correction (1/d) + + """ + return xr.where(use_DOX,xr.where(math.isnan((1.0 - (DOX / (DOX + KsOxdn))) * kdnit_tc * NO3),kdnit_tc * NO3,(1.0 - (DOX / (DOX + KsOxdn))) * kdnit_tc * NO3),0.0) @numba.njit -def dNO3dt( - use_Algae: bool, - use_Balgae: bool, - use_DOX: bool, +def NO3_BedDenit( use_SedFlux: bool, - use_NH4: bool, - use_NO3: bool, - + JNO3: xr.DataArray, depth: xr.DataArray, - rna: xr.DataArray, - rnb: xr.DataArray, - KsOxdn: xr.DataArray, - - Fb: xr.DataArray, - DOX: xr.DataArray, + vno3_tc: xr.DataArray, NO3: xr.DataArray, - JNO3: xr.DataArray, +) -> xr.DataArray: + """Calculate NO3_BedDenit: Sediment denitrification (mg-N/L/day) + + Args: + use_SedFlux: true/false to use sediment flux module (unitless), + depth: water depth (m), + NO3: Nitrate concentration (mg-N/L), + JNO3: Sediment water flux of nitrate (g-N/m^2/d), + vno3_tc: Sediment denitrification velocity temperature correction (m/d) + + """ + + return xr.where(use_SedFlux, JNO3 / depth,vno3_tc * NO3 / depth) + +@numba.njit +def NO3_ApGrowth( + use_Algae: bool, + ApUptakeFr_NO3: xr.DataArray, + rna: xr.DataArray, ApGrowth: xr.DataArray, - AbGrowth: xr.DataArray, ) -> xr.DataArray: - """Calculate dNO3dt: Change in nitrate (mg-N/L) + """Calculate NO3_ApGrowth: NO3 -> Floating algae (mg-N/L/day) Args: use_Algae: true/false to use algae module (unitless), - use_Balgae: true/false to use benthic algae module (unitless), - use_DOX: true/false to use dissolve oxygen module (unitless), - use_SedFlux: true/false to use sediment flux module (unitless), - use_NH4: true/false to use ammonium module (unitless), - use_NO3: true/false to use nitrate module (unitless), + rna: Algal N: Chla ratio (mg-N/ug-Chla), + ApGrowth: Algal growth rate (ug-Chla/L/d), + ApUptakeFr_NO3: Fraction of actual algal uptake from nitrate pool (unitless) + + + """ + return xr.where(use_Algae, ApUptakeFr_NO3 * rna * ApGrowth, 0.0) + +@numba.njit +def NO3_AbGrowth( + use_Balgae: bool, + AbUptakeFr_NO3: xr.DataArray, + rnb: xr.DataArray, + Fb: xr.DataArray, + AbGrowth: xr.DataArray, + depth: xr.DataArray, + +) -> xr.DataArray: + """Calculate NO3_AbGrowth: NO3 -> Benthic Algae (g-N/L/day) + + Args: + use_Balgae: true/false to use benthic algae module (unitless), depth: water depth (m), - rna: Algal N: Chla ratio (mg-N/ug-Chla), rnb: Benthic algal N: Benthic Algal Dry Weight (mg-N/mg-D), - KsOxdn: Half-saturation oxygen inhibition constant for denitrification (mg-O2/L) - Fb: Fraction of bottom area for benthic algae (unitless), - DOX: Dissolved oxygen concentration (mg-O2/L), - NO3: Nitrate concentration (mg-N/L), - JNO3: Sediment water flux of nitrate (g-N/m^2/d), + AbGrowth: Benthic alga growth rate (g/m^2/d), + AbUptakeFr_NO3: Fraction of actual benthic algal uptake from nitrate pool (unitless) + """ - ApGrowth: Algal growth rate (ug-Chla/L/d), - AbGrowth: Benthic alga growth rate (g/m^2/d), + return xr.where(use_Balgae, (AbUptakeFr_NO3 * rnb * Fb * AbGrowth) / depth, 0.0) + +@numba.njit +def NH4( + dNH4dt: xr.DataArray, + NH4: xr.DataArray + +) -> xr.DataArray: + """Calculates new ammonia concentration + + Args: + dNH4dt: Change in ammonia concentration over the timestep (mg-N/L/d) + NH4: Ammonia concentration (mg-N/L) + """ + return NH4 + dNH4dt + + +@numba.njit +def dNO3dt( + use_NO3: bool, + NH4_Nitrification: xr.DataArray, + NO3_Denit: xr.DataArray, + NO3_BedDenit: xr.DataArray, + NO3_ApGrowth: xr.DataArray, + NO3_AbGrowth: xr.DataArray, + +) -> xr.DataArray: + """Calculate dNO3dt: Change in nitrate (mg-N/L) + + Args: + use_NH4: true/false to use ammonium module (unitless), + use_NO3: true/false to use nitrate module (unitless), + NH4_Nitrification: NH4 -> NO3 Nitrification (mg-N/L/day) + NO3_Denit: NO3 -> Loss (mg-N/L/day), + NO3_BedDenit: Sediment denitrification (mg-N/L/day) + NO3_ApGrowth: NO3 -> Floating algae (mg-N/L/day) + NO3_AbGrowth: NO3 -> Benthic Algae (g-N/L/day) Nitrite Nitrogen (NO3) (mg-N/day*L) dNO3/dt = NH4 Oxidation (NH4 -> NO3) @@ -403,39 +599,40 @@ def dNO3dt( """ - if use_NO3: - if use_DOX: - NO3_Denit = (1.0 - (DOX / (DOX + KsOxdn))) * kdnit_tc * NO3 - if math.isnan(NO3_Denit): - NO3_Denit = kdnit_tc * NO3 - else: - NO3_Denit = 0.0 - if use_SedFlux: - NO3_BedDenit = JNO3 / depth - else: - NO3_BedDenit = vno3_tc * NO3 / depth + return xr.where(use_NO3, NH4_Nitrification - NO3_Denit - NO3_BedDenit - NO3_ApGrowth - NO3_AbGrowth ,0) - if use_Algae: - NO3_ApGrowth = ApUptakeFr_NO3 * rna * ApGrowth - else: - NO3_ApGrowth = 0.0 +@numba.njit +def NO3( + NO3: xr.DataArray, + dNO3dt: xr.DataArray, + timestep: xr.DataArray, + +) -> xr.DataArray: + """Calculate NO3: New concentration NO# (mg-N/L) - if use_Balgae: - NO3_AbGrowth = (AbUptakeFr_NO3 * rnb * Fb * AbGrowth) / depth - else: - NO3_AbGrowth = 0.0 + Args: + NO3: Concentration of NO3 (mg-N/L) + dNO3dt: Change in NO3(mg-N/L/d) + timestep: current iteration timestep (d) - if not use_NH4: - NH4_Nitrification = 0.0 + """ - dNO3dt = NH4_Nitrification - NO3_Denit - \ - NO3_BedDenit - NO3_ApGrowth - NO3_AbGrowth - else: - dNO3dt = 0 + return NO3 + dNO3dt*timestep - return dNO3dt +@numba.njit +def NO3( + dNO3dt: xr.DataArray, + NO3: xr.DataArray + +) -> xr.DataArray: + """Calculates new organic nitrogen concentration + Args: + dNO3dt: Change in nitrate concentration over the timestep (mg-N/L/d) + NO3: Nitrate concentration (mg-N/L) + """ + return NO3 + dNO3dt @numba.njit def DIN( @@ -454,11 +651,8 @@ def DIN( NO3: Nitrate concentration (mg-N/L), """ DIN = 0.0 - if use_NH4: - DIN = DIN + NH4 - - if use_NO3: - DIN = DIN + NO3 + DIN = xr.where(use_NH4, DIN + NH4,DIN) + DIN = xr.where(use_NO3, DIN + NO3, DIN) return DIN @@ -483,12 +677,9 @@ def TON( """ TON = 0.0 - if use_OrgN: - TON = TON + OrgN - - if use_Algae: - TON = TON + rna * Ap - + TON = xr.where(use_OrgN, TON + OrgN, TON) + TON = xr.where(use_Algae, TON + rna * Ap, TON) + return TON @@ -507,8 +698,7 @@ def TKN( TON: Total organic nitrogen (mg-N/L) """ TKN = 0.0 - if use_NH4: - TKN = TKN + NH4 + TKN = xr.where(use_NH4, TKN + NH4, TKN) return TKN + TON diff --git a/src/clearwater_modules/nsm1/nitrogen/static_variables_nitrogen.py b/src/clearwater_modules/nsm1/nitrogen/static_variables.py similarity index 89% rename from src/clearwater_modules/nsm1/nitrogen/static_variables_nitrogen.py rename to src/clearwater_modules/nsm1/nitrogen/static_variables.py index e4844e3..52934db 100644 --- a/src/clearwater_modules/nsm1/nitrogen/static_variables_nitrogen.py +++ b/src/clearwater_modules/nsm1/nitrogen/static_variables.py @@ -1,18 +1,15 @@ """ -File includes static variables only used in Nitrogen module +File contains static variables related to the Nitrogen module """ import clearwater_modules.base as base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.nitrogen.nitrogen_processes as nitrogen_processes @base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... -# TODO: verify all these values -# Only Nitrogen Variables Variable( @@ -52,7 +49,7 @@ class Variable(base.Variable): long_name='Sediment release rate of NH4 at 20C', units='g-N/m^2/d', description='Sediment release rate of NH4 at 20C', - use='static', + use='static' ) Variable( diff --git a/src/clearwater_modules/nsm1/_alkalinity.py b/src/clearwater_modules/nsm1/old/_alkalinity.py similarity index 100% rename from src/clearwater_modules/nsm1/_alkalinity.py rename to src/clearwater_modules/nsm1/old/_alkalinity.py diff --git a/src/clearwater_modules/nsm1/_benthic_algae.py b/src/clearwater_modules/nsm1/old/_benthic_algae.py similarity index 100% rename from src/clearwater_modules/nsm1/_benthic_algae.py rename to src/clearwater_modules/nsm1/old/_benthic_algae.py diff --git a/src/clearwater_modules/nsm1/_carbon.py b/src/clearwater_modules/nsm1/old/_carbon.py similarity index 100% rename from src/clearwater_modules/nsm1/_carbon.py rename to src/clearwater_modules/nsm1/old/_carbon.py diff --git a/src/clearwater_modules/nsm1/_cbod.py b/src/clearwater_modules/nsm1/old/_cbod.py similarity index 100% rename from src/clearwater_modules/nsm1/_cbod.py rename to src/clearwater_modules/nsm1/old/_cbod.py diff --git a/src/clearwater_modules/nsm1/_dox.py b/src/clearwater_modules/nsm1/old/_dox.py similarity index 100% rename from src/clearwater_modules/nsm1/_dox.py rename to src/clearwater_modules/nsm1/old/_dox.py diff --git a/src/clearwater_modules/nsm1/_globals.py b/src/clearwater_modules/nsm1/old/_globals.py similarity index 100% rename from src/clearwater_modules/nsm1/_globals.py rename to src/clearwater_modules/nsm1/old/_globals.py diff --git a/src/clearwater_modules/nsm1/_main.py b/src/clearwater_modules/nsm1/old/_main.py similarity index 100% rename from src/clearwater_modules/nsm1/_main.py rename to src/clearwater_modules/nsm1/old/_main.py diff --git a/src/clearwater_modules/nsm1/_n2.py b/src/clearwater_modules/nsm1/old/_n2.py similarity index 100% rename from src/clearwater_modules/nsm1/_n2.py rename to src/clearwater_modules/nsm1/old/_n2.py diff --git a/src/clearwater_modules/nsm1/_nitrogen.py b/src/clearwater_modules/nsm1/old/_nitrogen.py similarity index 100% rename from src/clearwater_modules/nsm1/_nitrogen.py rename to src/clearwater_modules/nsm1/old/_nitrogen.py diff --git a/src/clearwater_modules/nsm1/_pathogen.py b/src/clearwater_modules/nsm1/old/_pathogen.py similarity index 100% rename from src/clearwater_modules/nsm1/_pathogen.py rename to src/clearwater_modules/nsm1/old/_pathogen.py diff --git a/src/clearwater_modules/nsm1/_phosphorus.py b/src/clearwater_modules/nsm1/old/_phosphorus.py similarity index 100% rename from src/clearwater_modules/nsm1/_phosphorus.py rename to src/clearwater_modules/nsm1/old/_phosphorus.py diff --git a/src/clearwater_modules/nsm1/_pom.py b/src/clearwater_modules/nsm1/old/_pom.py similarity index 100% rename from src/clearwater_modules/nsm1/_pom.py rename to src/clearwater_modules/nsm1/old/_pom.py diff --git a/src/clearwater_modules/nsm1/_sed_flux.py b/src/clearwater_modules/nsm1/old/_sed_flux.py similarity index 100% rename from src/clearwater_modules/nsm1/_sed_flux.py rename to src/clearwater_modules/nsm1/old/_sed_flux.py diff --git a/src/clearwater_modules/nsm1/_temp_correction.py b/src/clearwater_modules/nsm1/old/_temp_correction.py similarity index 100% rename from src/clearwater_modules/nsm1/_temp_correction.py rename to src/clearwater_modules/nsm1/old/_temp_correction.py diff --git a/src/clearwater_modules/nsm1/pathogens/dynamic_variables_pathogen.py b/src/clearwater_modules/nsm1/pathogens/dynamic_variables.py similarity index 72% rename from src/clearwater_modules/nsm1/pathogens/dynamic_variables_pathogen.py rename to src/clearwater_modules/nsm1/pathogens/dynamic_variables.py index 4cc20d3..5522925 100644 --- a/src/clearwater_modules/nsm1/pathogens/dynamic_variables_pathogen.py +++ b/src/clearwater_modules/nsm1/pathogens/dynamic_variables.py @@ -1,11 +1,11 @@ """ -File includes dynamic variables computed in pathogen module. Dynamic variables may be accessed by other modules. +File contains dynamic variables related to the Pathogens module """ import clearwater_modules.shared.processes as shared_processes from clearwater_modules import base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.pathogens.pathogen_processes as pathogen_processes +import clearwater_modules.nsm1.pathogens.processes as processes @base.register_variable(models=NutrientBudget) @@ -18,7 +18,7 @@ class Variable(base.Variable): units='1/d', description='Pathogen death rate with temperature correction', use='dynamic', - process=pathogen_processes.kdx_tc + process=processes.kdx_tc ) Variable( @@ -27,7 +27,7 @@ class Variable(base.Variable): units='cfu/100mL/d', description='Pathogen natural death', use='dynamic', - process=pathogen_processes.PathogenDeath + process=processes.PathogenDeath ) Variable( @@ -36,7 +36,7 @@ class Variable(base.Variable): units='cfu/100mL/d', description='Pathogen death due to light', use='dynamic', - process=pathogen_processes.PathogenDecay + process=processes.PathogenDecay ) Variable( @@ -45,7 +45,7 @@ class Variable(base.Variable): units='cfu/100mL/d', description='Pathogen settling', use='dynamic', - process=pathogen_processes.PathogenSettling + process=processes.PathogenSettling ) Variable( @@ -54,14 +54,14 @@ class Variable(base.Variable): units='cfu/100mL/d', description='Change in pathogen concentration', use='dynamic', - process=pathogen_processes.dPXdt + process=processes.dPXdt ) Variable( - name='PX_new', + name='PX', long_name='New pathogen concentration', units='cfu/100mL', description='New pathogen concentration', use='dynamic', - process=pathogen_processes.PX_new + process=processes.PX ) \ No newline at end of file diff --git a/src/clearwater_modules/nsm1/pathogens/pathogen_processes.py b/src/clearwater_modules/nsm1/pathogens/processes.py similarity index 88% rename from src/clearwater_modules/nsm1/pathogens/pathogen_processes.py rename to src/clearwater_modules/nsm1/pathogens/processes.py index 0a54fd6..509094a 100644 --- a/src/clearwater_modules/nsm1/pathogens/pathogen_processes.py +++ b/src/clearwater_modules/nsm1/pathogens/processes.py @@ -1,11 +1,11 @@ """ -File contains process to calculate new algae biomass concentration and associated dependent variables +File contains process to calculate new pathogen concentration and associated dependent variables """ -import math -from clearwater_modules.shared.processes import arrhenius_correction import numba import xarray as xr +from clearwater_modules.shared.processes import arrhenius_correction +import math @numba.njit def kdx_tc( @@ -50,13 +50,13 @@ def PathogenDecay( Args: apx: light efficiency factor for pathogen decay, - q_solar: solar radiation (1/d), + q_solar: Incident short-wave solar radiation (W/m2), L: lambda (1/m), depth: water depth (m), PX: Pathogen concentration (cfu/100mL) """ - return apx * q_solar / (L*depth) * (1-math.exp(-L*depth)) * PX + return apx * q_solar / (L * depth) * (1 - math.exp(-L * depth)) * PX @numba.njit def PathogenSettling( @@ -93,13 +93,13 @@ def dPXdt( return -PathogenDeath - PathogenDecay - PathogenSettling @numba.njit -def PX_new( +def PX( PX:xr.DataArray, dPXdt: xr.DataArray ) -> xr.DataArray : - """Calculate PX_new: New pathogen concentration (cfu/100mL). + """Calculate PX: New pathogen concentration (cfu/100mL). Args: dPXdt: change in pathogen concentration (cfu/100mL/d) diff --git a/src/clearwater_modules/nsm1/pathogens/static_variables_pathogen.py b/src/clearwater_modules/nsm1/pathogens/static_variables.py similarity index 69% rename from src/clearwater_modules/nsm1/pathogens/static_variables_pathogen.py rename to src/clearwater_modules/nsm1/pathogens/static_variables.py index a2088ab..7281d45 100644 --- a/src/clearwater_modules/nsm1/pathogens/static_variables_pathogen.py +++ b/src/clearwater_modules/nsm1/pathogens/static_variables.py @@ -1,22 +1,22 @@ """ -File includes static variables only used in Pathogen module +File contains static variables related to the Pathogens module """ import clearwater_modules.base as base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.pathogens.pathogen_processes as pathogen_processes @base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... -# TODO: verify all these values + + Variable( - name='kdx', - long_name='Pathogen death rate', + name='kdx_20', + long_name='Pathogen death rate at 20C', units='1/d', - description='Pathogen death rate', + description='Pathogen death rate at 20C', use='static', ) diff --git a/src/clearwater_modules/nsm1/phosphorus/dynamic_variables_phosphorus.py b/src/clearwater_modules/nsm1/phosphorus/dynamic_variables.py similarity index 73% rename from src/clearwater_modules/nsm1/phosphorus/dynamic_variables_phosphorus.py rename to src/clearwater_modules/nsm1/phosphorus/dynamic_variables.py index 55426ad..bbe19de 100644 --- a/src/clearwater_modules/nsm1/phosphorus/dynamic_variables_phosphorus.py +++ b/src/clearwater_modules/nsm1/phosphorus/dynamic_variables.py @@ -1,11 +1,11 @@ """ -File includes dynamic variables computed in phosphorus module. Dynamic variables may be accessed by other modules. +File contains dynamic variables related to the Phosphorus module """ import clearwater_modules.shared.processes as shared_processes from clearwater_modules import base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.phosphorus.phosphorus_processes as phosphorus_processes +import clearwater_modules.nsm1.phosphorus.processes as processes @base.register_variable(models=NutrientBudget) @@ -18,16 +18,16 @@ class Variable(base.Variable): units='1/d', description='Decay rate of organic P to DIP temperature correction', use='dynamic', - process=phosphorus_processes.kop_tc + process=processes.kop_tc ) Variable( - name='rop4_tc', + name='rpo4_tc', long_name='Benthic sediment release rate of DIP', units='g-P/m2/d', description='Benthic sediment release rate of DIP temperature correction', use='dynamic', - process=phosphorus_processes.rop4_tc + process=processes.rpo4_tc ) Variable( @@ -36,7 +36,7 @@ class Variable(base.Variable): units='mg-P/L/d', description='Organic phosphorus decay to dissolve inorganic phosphorus', use='dynamic', - process=phosphorus_processes.OrgP_DIP_decay + process=processes.OrgP_DIP_decay ) Variable( @@ -45,7 +45,7 @@ class Variable(base.Variable): units='mg-P/L/d', description='Organic phosphorus settling to sediment', use='dynamic', - process=phosphorus_processes.OrgP_Settling + process=processes.OrgP_Settling ) Variable( @@ -54,7 +54,7 @@ class Variable(base.Variable): units='mg-P/L/d', description='Algal death turning into organic phosphorus', use='dynamic', - process=phosphorus_processes.ApDeath_OrgP + process=processes.ApDeath_OrgP ) Variable( @@ -63,7 +63,7 @@ class Variable(base.Variable): units='mg-P/L/d', description='Benthic algal death turning into organic phosphorus', use='dynamic', - process=phosphorus_processes.AbDeath_OrgP + process=processes.AbDeath_OrgP ) Variable( @@ -72,7 +72,7 @@ class Variable(base.Variable): units='mg-P/L/d', description='Change in organic phosphorus concentration', use='dynamic', - process=phosphorus_processes.dOrgPdt + process=processes.dOrgPdt ) Variable( @@ -81,16 +81,16 @@ class Variable(base.Variable): units='mg-P/L/d', description='Dissolved Organic Phosphorus coming from Bed calculated using SedFlux modules', use='dynamic', - process=phosphorus_processes.DIPfromBed_SedFlux + process=processes.DIPfromBed_SedFlux ) -Variable( - name='DIPfromBed_NoSedFlux', +Variable(#TODO: find correct process + name='DIPfromBed', long_name='Dissolved Organic Phosphorus coming from Bed calculated without SedFlux modules', units='mg-P/L/d', description='Dissolved Organic Phosphorus coming from Bed calculated without SedFlux modules', use='dynamic', - process=phosphorus_processes.DIPfromBed_NoSedFlux + process=processes.DIPfromBed ) Variable( @@ -99,7 +99,7 @@ class Variable(base.Variable): units='mg-P/L/d', description='Total inorganic phosphorus settling from water to bed', use='dynamic', - process=phosphorus_processes.TIP_Settling + process=processes.TIP_Settling ) Variable( @@ -108,7 +108,7 @@ class Variable(base.Variable): units='mg-P/L/d', description='Total organic phosphorus decaying to dissolved inorganic phosphrous', use='dynamic', - process=phosphorus_processes.OrgP_DIP_decay + process=processes.OrgP_DIP_decay ) Variable( @@ -117,7 +117,7 @@ class Variable(base.Variable): units='mg-P/L/d', description='Dissolved inorganic phosphorus released from algal respiration', use='dynamic', - process=phosphorus_processes.DIP_ApRespiration + process=processes.DIP_ApRespiration ) Variable( @@ -126,7 +126,7 @@ class Variable(base.Variable): units='mg-P/L/d', description='Dissolved inorganic phosphorus consumed for algal growth', use='dynamic', - process=phosphorus_processes.DIP_ApGrowth + process=processes.DIP_ApGrowth ) Variable( @@ -135,7 +135,7 @@ class Variable(base.Variable): units='mg-P/L/d', description='Dissolved inorganic phosphorus released for benthic algal respiration', use='dynamic', - process=phosphorus_processes.DIP_AbRespiration + process=processes.DIP_AbRespiration ) Variable( @@ -144,7 +144,7 @@ class Variable(base.Variable): units='mg-P/L/d', description='Dissolved inorganic phosphorus consumed for benthic algal growth', use='dynamic', - process=phosphorus_processes.DIP_AbGrowth + process=processes.DIP_AbGrowth ) Variable( @@ -153,26 +153,9 @@ class Variable(base.Variable): units='mg-P/L/d', description='Change in dissolved inorganic phosphorus water concentration', use='dynamic', - process=phosphorus_processes.dTIPdt -) - -Variable( - name='TIP_new', - long_name='New total inorganic phosphorus', - units='mg-P/L', - description='New total inorganic phosphorus', - use='dynamic', - process=phosphorus_processes.TIP_new + process=processes.dTIPdt ) -Variable( - name='OrgP_new', - long_name='New total organic phosphorus', - units='mg-P/L', - description='New total organic phosphorus', - use='dynamic', - process=phosphorus_processes.OrgP_new -) Variable( name='TOP', @@ -180,7 +163,7 @@ class Variable(base.Variable): units='mg-P/L', description='Total organic phosphorus', use='dynamic', - process=phosphorus_processes.TOP + process=processes.TOP ) Variable( @@ -189,7 +172,7 @@ class Variable(base.Variable): units='mg-P/L', description='Total phosphorus', use='dynamic', - process=phosphorus_processes.TP + process=processes.TP ) Variable( @@ -198,5 +181,5 @@ class Variable(base.Variable): units='mg-P/L', description='Dissolve inorganich phosphorus', use='dynamic', - process=phosphorus_processes.DIP + process=processes.DIP ) \ No newline at end of file diff --git a/src/clearwater_modules/nsm1/phosphorus/phosphorus_processes.py b/src/clearwater_modules/nsm1/phosphorus/processes.py similarity index 65% rename from src/clearwater_modules/nsm1/phosphorus/phosphorus_processes.py rename to src/clearwater_modules/nsm1/phosphorus/processes.py index 43c2955..640a869 100644 --- a/src/clearwater_modules/nsm1/phosphorus/phosphorus_processes.py +++ b/src/clearwater_modules/nsm1/phosphorus/processes.py @@ -1,54 +1,12 @@ """ File contains process to calculate new phosphorus concentration and associated dependent variables """ -""" -(Global) module_choices T/F -'use_Algae' -'use_BAlgae' -'use_OrgP' -'use_TIP' -'use_SedFlux' - -(Global) global_vars -'Ap' Algae concentration [ug/Chla/L] -'TwaterC' Water temperature [C] -'depth' Depth from water surface [m] -'OrgP' Organic phosphorus [mg-P/L] -'TIP' Total inorganic phosphorus [mg-P/L] -'vs' Sediment settling velocity [m/d] -'fdp' fraction P dissolved [unitless] - -phosphorus_constant_changes -'kop' Decay rate or orgnaic P to DIP [1/d] -'rpo4' Benthic sediment release rate of DIP [g-P/m2*d] - -from Algae -'rPa' AlgalP : Chla ratio [mg-P/ugChla] -'ApGrowth' Algal growth rate [ug-chla/L/d] -'ApDeath' Algal death rate [ug-chla/L/d] -'ApRespiration' AlgalRespiration rate [ug-chla/L/d] - -from Benthic Algae -'rpb' Benthic Algal P: Benthic Algal Dry Weight [mg-P/mg-D] -'AbGrowth' Benthic Algal growth rate [g/m^2*d] -'AbDeath' Benthic Algal death rate [g/m^2*d] -'AbRespiration' Benthic Algal respiration rate [g/m^2*d] - -from SedFlux -'JDIP' Sediment water flux of phosphate [g-P/m^2*d] - - Organic Phosphorus (mgP/day) - - dOrgP/dt = Algae_OrgP (Algae -> OrgP) - - OrgP Decay (OrgP -> DIP) - - OrgP Settling (OrgP -> bed) - + BenthicAlgalDeath (Benthic Algae -> OrgP) - -""" -import math -from clearwater_modules.shared.processes import arrhenius_correction import numba import xarray as xr +from clearwater_modules.shared.processes import arrhenius_correction +import math + + @numba.njit def kop_tc( @@ -66,24 +24,25 @@ def kop_tc( return arrhenius_correction(TwaterC, kop_20, 1.047) @numba.njit -def rop4_tc( +def rpo4_tc( TwaterC : xr.DataArray, - rop4_20: xr.DataArray + rpo4_20: xr.DataArray ) -> xr.DataArray : - """Calculate rop4_tc: Benthic sediment release rate of DIP temperature correction(g-P/m2/d). + """Calculate rpo4_tc: Benthic sediment release rate of DIP temperature correction(g-P/m2/d). Args: TwaterC: Water temperature (C) kop_20: Benthic sediment release rate of DIP at 20C (1/d) """ - return arrhenius_correction(TwaterC, rop4_20, 1.074) + return arrhenius_correction(TwaterC, rpo4_20, 1.074) @numba.njit def OrgP_DIP_decay( kop_tc : xr.DataArray, OrgP: xr.DataArray, + use_OrgP: bool, ) -> xr.DataArray : """Calculate OrgP_DIP: organic phosphorus decay to dissolve inorganic phosphorus (mg-P/L/d). @@ -91,8 +50,9 @@ def OrgP_DIP_decay( Args: kop_tc: Decay rate of organic P to DIP temperature correction (1/d) OrgP: Organic phosphorus concentration (mg-P/L) + use_OrgP: true/false use organic phosphorus (t/f) """ - return kop_tc * OrgP + return xr.where(use_OrgP,kop_tc * OrgP,0) @numba.njit def OrgP_Settling( @@ -114,6 +74,7 @@ def OrgP_Settling( def ApDeath_OrgP( rpa : xr.DataArray, ApDeath: xr.DataArray, + use_Algae: bool, ) -> xr.DataArray : """Calculate ApDeath_OrgP: Algal death turning into organic phosphorus (mg-P/L/d). @@ -121,10 +82,11 @@ def ApDeath_OrgP( Args: rpa: Algal P : Chla ratio (mg-P/ug-Chla) ApDeath: Algal death rate (ug-Chla/L/d) + use_Algae: true/false to use algae module (T/F) """ - return rpa * ApDeath + return xr.where(use_Algae, rpa * ApDeath,0) @numba.njit def AbDeath_OrgP( @@ -132,7 +94,8 @@ def AbDeath_OrgP( AbDeath: xr.DataArray, Fw: xr.DataArray, Fb: xr.DataArray, - depth: xr.DataArray + depth: xr.DataArray, + use_Balgae: bool ) -> xr.DataArray : """Calculate AbDeath_OrgP: Benthic algal death turning into organic phosphorus (mg-P/L/d). @@ -143,10 +106,11 @@ def AbDeath_OrgP( Fw: Fraction benthic algal death to water column (unitless) Fb: Fraction bottom area avalible for benthic algae (unitless) depth: water depth (m) + use_Balgae: true/false use benthic algae module (t/f) """ - return (rpb * Fw *Fb * AbDeath) / depth + return xr.where(use_Balgae, (rpb * Fw *Fb * AbDeath) / depth,0) @numba.njit def dOrgPdt( @@ -155,8 +119,6 @@ def dOrgPdt( OrgP_DIP_decay: xr.DataArray, OrgP_Settling: xr.DataArray, use_OrgP: bool, - use_Algae: bool, - use_Balgae: bool, ) -> xr.DataArray : """Calculate dOrgPdt: change in organic phosphorus concentration (mg-P/L/d). @@ -169,44 +131,40 @@ def dOrgPdt( use_Algae: true/false to use algae module (true/false) use_Balgae: true/false to use benthic algae module (true/false) """ - dOrgPdt=0 - if use_OrgP: - dOrgPdt=dOrgPdt-OrgP_DIP_decay-OrgP_Settling - - if use_Algae: - dOrgPdt=dOrgPdt+ApDeath_OrgP - if use_Balgae: - dOrgPdt=dOrgPdt+AbDeath_OrgP - return dOrgPdt + return xr.where(use_OrgP, -OrgP_DIP_decay-OrgP_Settling + ApDeath_OrgP + AbDeath_OrgP, 0) - -@numba.njit +#TODO will this be a problem if use_SedFlux is False def DIPfromBed_SedFlux( + use_SedFlux: bool, JDIP: xr.DataArray, - depth:xr.DataArray + depth:xr.DataArray, + rpo4_tc: xr.DataArray, ) -> xr.DataArray : """Calculate DIPfromBed_SedFlux: Dissolved Organic Phosphorus coming from Bed calculated using SedFlux modules (mg-P/L/d). Args: + use_SedFlux: true/false to use the sediment flux module (unitless) JDIP: Sediment-water flux of phosphate (g-P/m^2/d) depth: water depth (m) + rpo4_tc: Benthic sediment release rate of DIP temperature correction(g-P/m2/d) """ - return JDIP / depth + return xr.where(use_SedFlux, JDIP / depth, rpo4_tc/depth) @numba.njit -def DIPfromBed_NoSedFlux( +def DIPfromBed( + depth:xr.DataArray, rpo4_tc: xr.DataArray, - depth:xr.DataArray ) -> xr.DataArray : - """Calculate DIPfromBed_NoSedFlux: Dissolved Organic Phosphorus coming from Bed calculated without SedFlux modules (mg-P/L/d). + """Calculate DIPfromBed: Dissolved Organic Phosphorus coming from Bed calculated without a SedFlux module (mg-P/L/d). Args: - rpo4_tc: Benthic sediment release rate of DIP with temperature correction (g-P/m^2/d) depth: water depth (m) - """ + rpo4_tc: Benthic sediment release rate of DIP temperature correction(g-P/m2/d) + """ return rpo4_tc / depth +#TODO calcuate fdp? @numba.njit def TIP_Settling( vs: xr.DataArray, @@ -225,24 +183,11 @@ def TIP_Settling( """ return vs / depth * (1.0 - fdp) * TIP -@numba.njit -def OrgP_DIP_decay( - kop_tc: xr.DataArray, - OrgP: xr.DataArray, - -) -> xr.DataArray : - """Calculate OrgP_DIP_decay: Total organic phosphorus decaying to dissolved inorganic phosphrous (mg-P/L/d). - - Args: - kop_tc: Decay rate of organic phosphorus to dissolved inorganic phosphorus with temperature correction (1/d) - OrgP: Total organic phosphorus water concentration (mg-P/L) - """ - return kop_tc * OrgP - @numba.njit def DIP_ApRespiration( rpa: xr.DataArray, ApRespiration: xr.DataArray, + use_Algae: bool ) -> xr.DataArray : """Calculate DIP_ApRespiration: Dissolved inorganic phosphorus released from algal respiration (mg-P/L/d). @@ -250,13 +195,15 @@ def DIP_ApRespiration( Args: rpa: Algal P : Chla ratio (mg-P/ug-Chla) ApRespiration: Algal respiration rate (ug-Chla/L/d) + use_Algae: true/false to use algae module (t/f) """ - return rpa * ApRespiration + return xr.where(use_Algae, rpa * ApRespiration,0) @numba.njit def DIP_ApGrowth( rpa: xr.DataArray, ApGrowth: xr.DataArray, + use_Algae: bool ) -> xr.DataArray : """Calculate DIP_ApGrowth: Dissolved inorganic phosphorus consumed for algal growth (mg-P/L/d). @@ -264,13 +211,15 @@ def DIP_ApGrowth( Args: rpa: Algal P : Chla ratio (mg-P/ug-Chla) ApGrowth: Algal growth rate (ug-Chla/L/d) + use_Algae: true/false to use algae module (t/f) """ - return rpa * ApGrowth + return xr.where(use_Algae, rpa * ApGrowth,0) @numba.njit def DIP_AbRespiration( rpb: xr.DataArray, AbRespiration: xr.DataArray, + use_Balgae: bool ) -> xr.DataArray : """Calculate DIP_AbRespiration: Dissolved inorganic phosphorus released for benthic algal respiration (mg-P/L/d). @@ -278,15 +227,17 @@ def DIP_AbRespiration( Args: rpb: Benthic algal P : Benthic algal dry ratio (mg-P/mg-D) AbRespiration: Benthic algal respiration rate (g/m^2/d) + use_Blgae: true/false to use benthic algae module (t/f) """ - return rpb * AbRespiration + return xr.where(use_Balgae, rpb * AbRespiration,0) @numba.njit def DIP_AbGrowth( rpb: xr.DataArray, AbGrowth: xr.DataArray, Fb: xr.DataArray, - depth: xr.DataArray + depth: xr.DataArray, + use_Balgae: bool ) -> xr.DataArray : """Calculate DIP_AbGrowth: Dissolved inorganic phosphorus consumed for benthic algal growth (mg-P/L/d). @@ -296,24 +247,21 @@ def DIP_AbGrowth( AbGrowth: Benthic algal growth rate (g/m^2/d) Fb: Fraction of bottom area available for benthic algal (unitless) depth: water depth (m) + use_Balgae: true/false to use benthic algae module (t/f) """ - return rpb * Fb * AbGrowth / depth + return xr.where(use_Balgae, rpb * Fb * AbGrowth / depth,0) @numba.njit def dTIPdt( OrgP_DIP_decay: xr.DataArray, TIP_Settling: xr.DataArray, - DIPfromBed_NoSedFlux: xr.DataArray, - DIPfromBed_SedFlux: xr.DataArray, + DIPfromBed: xr.DataArray, DIP_ApRespiration: xr.DataArray, DIP_ApGrowth: xr.DataArray, DIP_AbRespiration: xr.DataArray, DIP_AbGrowth: xr.DataArray, use_TIP: bool, - use_SedFlux: bool, - use_OrgP: bool, - use_Algae: bool, - use_Balgae: bool, + ) -> xr.DataArray : """Calculate dTIPdt: Change in dissolved inorganic phosphorus water concentration (mg-P/L/d). @@ -328,10 +276,7 @@ def dTIPdt( DIP_AbRespiration: Dissolved inorganic phosphorus released for benthic algal respiration (mg-P/L/d), DIP_AbGrowth: Dissolved inorganic phosphorus consumed for benthic algal growth (mg-P/L/d), use_TIP: true/false to use total inorganic phosphorus module (true/false), - use_SedFlux: true/false to use sediment flux module (true/false) - use_OrgP: true/false to use organic phosphorus module (true/false), - use_Algae: true/false to use algae module (true/false), - use_Balgae: true/false to use benthic algae module (true/false), + dTIP/dt = OrgP Decay (OrgP -> DIP) - DIP AlgalUptake (DIP -> xr.DataArraying Algae) @@ -339,55 +284,46 @@ def dTIPdt( - TIP Settling (TIP -> bed) + DIP From Benthos (Benthos -> DIP) """ - dTIPdt = 0 - if use_TIP: - dTIPdt = dTIPdt - TIP_Settling - if use_SedFlux: - dTIPdt = dTIPdt + DIPfromBed_SedFlux - else: - dTIPdt = dTIPdt + DIPfromBed_NoSedFlux - if use_OrgP: - dTIPdt = dTIPdt + OrgP_DIP_decay - if use_Algae: - dTIPdt = dTIPdt + DIP_ApRespiration - DIP_ApGrowth - if use_Balgae: - dTIPdt = dTIPdt + DIP_AbRespiration - DIP_AbGrowth - - return dTIPdt + + return xr.where(use_TIP, - TIP_Settling + DIPfromBed + OrgP_DIP_decay + DIP_ApRespiration - DIP_ApGrowth + DIP_AbRespiration - DIP_AbGrowth, 0) + @numba.njit -def TIP_new( +def TIP( TIP: xr.DataArray, dTIPdt: xr.DataArray, + timestep: xr.DataArray ) -> xr.DataArray : - """Calculate TIP_new: New total inorganic phosphorus (mg-P/L). + """Calculate TIP: New total inorganic phosphorus (mg-P/L). Args: dTIPdt: Change in total inorganic phosphorus (mg-P/L/d) TIP: Total inorganic phosphorus water concentration (mg-P/L), - + timestep: current iteration timestep (d) """ - return TIP+dTIPdt + return TIP+dTIPdt*timestep + @numba.njit -def OrgP_new( +def OrgP( OrgP: xr.DataArray, dOrgPdt: xr.DataArray, + timestep: xr.DataArray ) -> xr.DataArray : - """Calculate OrgP_new: New total organic phosphorus (mg-P/L). + """Calculate OrgP: New total organic phosphorus (mg-P/L). Args: dOrgPdt: Change in total organic phosphorus (mg-P/L/d) OrgP: Total organic phosphorus water concentration (mg-P/L), - + timestep: current iteration timestep (d) """ - return OrgP+dOrgPdt + return OrgP+dOrgPdt*timestep @numba.njit def TOP( use_OrgP: bool, - OrgP_new: xr.DataArray, + OrgP: xr.DataArray, use_Algae: bool, rpa: xr.DataArray, Ap: xr.DataArray @@ -397,16 +333,15 @@ def TOP( Args: use_OrgP: true/false to use organic phosphorus module (true/false), - OrgP_new: New organic phosphorus water concentration (mg-P/L), + OrgP: New organic phosphorus water concentration (mg-P/L), use_Algae: true/false to use algae module (true/false), rpa: Algal P: Chla ratio (mg-P/ug-Chla), Ap: Algal water concentration (ug-Chla/L) """ TOP = 0.0 - if use_OrgP: - TOP = TOP + OrgP_new - if use_Algae: - TOP = TOP + rpa * Ap + TOP = xr.where(use_OrgP, TOP + OrgP,TOP) + TOP = xr.where(use_Algae, TOP + rpa*Ap, TOP) + return TOP @@ -414,31 +349,30 @@ def TOP( def TP( use_TIP: bool, TOP: xr.DataArray, - TIP_new: xr.DataArray + TIP: xr.DataArray ) -> xr.DataArray : """Calculate TP: Total phosphorus (mg-P/L). Args: use_TIP: true/false to use total inorganic phosphorus module (true/false), - TIP_new: New total inorganic phosphorus water concentration (mg-P/L), + TIP: New total inorganic phosphorus water concentration (mg-P/L), TOP: Total organic phosphorus water concentration (mg-P/L) """ TP = TOP - if use_TIP: - TP = TP + TIP_new + TP = xr.where(use_TIP,TP + TIP,TP) @numba.njit def DIP( fdp: xr.DataArray, - TIP_new: xr.DataArray + TIP: xr.DataArray ) -> xr.DataArray : """Calculate DIP: Dissolve inorganich phosphorus (mg-P/L). Args: fdp: fraction P dissolved - TIP_new: New total inorganic phosphorus water concentration (mg-P/L), + TIP: New total inorganic phosphorus water concentration (mg-P/L), """ - return TIP_new * fdp + return TIP * fdp diff --git a/src/clearwater_modules/nsm1/phosphorus/static_variables_phosphorus.py b/src/clearwater_modules/nsm1/phosphorus/static_variables.py similarity index 70% rename from src/clearwater_modules/nsm1/phosphorus/static_variables_phosphorus.py rename to src/clearwater_modules/nsm1/phosphorus/static_variables.py index 8a3e2aa..c990184 100644 --- a/src/clearwater_modules/nsm1/phosphorus/static_variables_phosphorus.py +++ b/src/clearwater_modules/nsm1/phosphorus/static_variables.py @@ -1,18 +1,14 @@ """ -File includes static variables only used in Algae module +File contains static variables related to the Phosphorus module """ import clearwater_modules.base as base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.phosphorus.phosphorus_processes as phosphorus_processes - @base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... -# TODO: verify all these values -#Only phosphorus variables Variable( name='kop_20', @@ -29,3 +25,12 @@ class Variable(base.Variable): description='Benthic sediment release rate of DIP', use='static', ) + + +Variable( + name='kdpo4', + long_name='solid partitioning coeff. of PO4', + units='L/kg', + description='solid partitioning coeff. of PO4', + use='static', +) \ No newline at end of file diff --git a/src/clearwater_modules/nsm1/sedflux/dynamic_variables_sedflux.py b/src/clearwater_modules/nsm1/sedflux/dynamic_variables.py similarity index 89% rename from src/clearwater_modules/nsm1/sedflux/dynamic_variables_sedflux.py rename to src/clearwater_modules/nsm1/sedflux/dynamic_variables.py index 2f351e9..8bc80f1 100644 --- a/src/clearwater_modules/nsm1/sedflux/dynamic_variables_sedflux.py +++ b/src/clearwater_modules/nsm1/sedflux/dynamic_variables.py @@ -1,10 +1,11 @@ """ -File includes dynamic variables computed in Algae module. Dynamic variables may be accessed by other modules. +File contains dynamic variables related to the SedFlux module """ import clearwater_modules.shared.processes as shared_processes from clearwater_modules import base from clearwater_modules.nsm1.model import NutrientBudget +from clearwater_modules.nsm1.sedflux import processes @base.register_variable(models=NutrientBudget) diff --git a/src/clearwater_modules/nsm1/sedflux/sedflux_processes.py b/src/clearwater_modules/nsm1/sedflux/processes.py similarity index 99% rename from src/clearwater_modules/nsm1/sedflux/sedflux_processes.py rename to src/clearwater_modules/nsm1/sedflux/processes.py index a77a7b3..9faec09 100644 --- a/src/clearwater_modules/nsm1/sedflux/sedflux_processes.py +++ b/src/clearwater_modules/nsm1/sedflux/processes.py @@ -2,12 +2,10 @@ File contains process to calculate sediment flux and associated dependent variables """ -import math -from clearwater_modules.shared.processes import arrhenius_correction -import numba -import numpy as np +import numba import xarray as xr - +from clearwater_modules.shared.processes import arrhenius_correction +import math # Note: OrgN, NH4, NO3, OrgP, TIP, POC and DOX must be on for SedFlux. @@ -3270,7 +3268,7 @@ def ra2( return xr.where(POCdiagenesis_part_option==2, xr.where(SO4>0.1, - xr.where(SedFlux_solution_option == 1, 2.0 * KL01 * JCc / h2, 2.0 * KL01 * JCc / h2 + (KL01 * SO4 + con_sox / KL01 * TH2S1) / dt), "NaN") "NaN") + xr.where(SedFlux_solution_option == 1, 2.0 * KL01 * JCc / h2, 2.0 * KL01 * JCc / h2 + (KL01 * SO4 + con_sox / KL01 * TH2S1) / dt), "NaN"), "NaN") @numba.njit @@ -3304,7 +3302,7 @@ def ra1( return xr.where(POCdiagenesis_part_option==2, xr.where(SO4>0.1, - xr.where(SedFlux_solution_option == 1, 2.0 * Dd_tc * JCc / h2, 2.0 * Dd_tc * JCc / h2 - 2.0 * KL01 * HSO4 * SO42 / dt), "NaN") "NaN") + xr.where(SedFlux_solution_option == 1, 2.0 * Dd_tc * JCc / h2, 2.0 * Dd_tc * JCc / h2 - 2.0 * KL01 * HSO4 * SO42 / dt), "NaN"), "NaN") @numba.njit @@ -3330,9 +3328,7 @@ def ra0( TH2S1: TH2S sediment layer 1 (mg-O/L) """ - return xr.where(POCdiagenesis_part_option==2, - xr.where(SO4>0.1, - xr.where(SedFlux_solution_option == 1, - 2.0 * Dd_tc * (KL01 * SO4 + con_sox / KL01 * TH2S1), - 2.0 * Dd_tc * (KL01 * SO4 + con_sox / KL01 * TH2S1)), "NaN") "NaN") + return xr.where(POCdiagenesis_part_option==2, xr.where(SO4>0.1, xr.where(SedFlux_solution_option == 1, - 2.0 * Dd_tc * (KL01 * SO4 + con_sox / KL01 * TH2S1), - 2.0 * Dd_tc * (KL01 * SO4 + con_sox / KL01 * TH2S1)), "NaN"), "NaN") @numba.njit diff --git a/src/clearwater_modules/nsm1/sedflux/static_variable_sedflux.py b/src/clearwater_modules/nsm1/sedflux/static_variable.py similarity index 98% rename from src/clearwater_modules/nsm1/sedflux/static_variable_sedflux.py rename to src/clearwater_modules/nsm1/sedflux/static_variable.py index 899215b..5dc4dd4 100644 --- a/src/clearwater_modules/nsm1/sedflux/static_variable_sedflux.py +++ b/src/clearwater_modules/nsm1/sedflux/static_variable.py @@ -1,18 +1,16 @@ """ -File includes static variables only used in sedflux module +File contains static variables related to the SedFlux module """ import clearwater_modules.base as base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.sedflux.sedflux_processes as sedflux_processes @base.register_variable(models=NutrientBudget) class Variable(base.Variable): ... -# TODO: verify all these values -# Only Algae Variables + Variable( @@ -23,6 +21,7 @@ class Variable(base.Variable): use='static', ) +""" self.sedFlux_constants = OrderedDict() self.sedFlux_constants = { 'Dd': 0.0025, @@ -256,4 +255,5 @@ class Variable(base.Variable): H2S = 0.0 CH4 = 0.0 ron = 2.0 * 32.0 / 14.0 - rcdn = 5.0 * 12.0 / (4.0 * 14.0) \ No newline at end of file + rcdn = 5.0 * 12.0 / (4.0 * 14.0) +""" \ No newline at end of file diff --git a/src/clearwater_modules/nsm1/state_variables.py b/src/clearwater_modules/nsm1/state_variables.py index d124200..b986abd 100644 --- a/src/clearwater_modules/nsm1/state_variables.py +++ b/src/clearwater_modules/nsm1/state_variables.py @@ -1,13 +1,18 @@ -from clearwater_modules_python import base -from clearwater_modules_python.nsm1.model import NutrientBudget -import clearwater_modules_python.nsm1.algae.algae_processes as algae_processes -import clearwater_modules_python.nsm1.nitrogen.nitrogen_processes as nitrogen_processes -import clearwater_modules_python.nsm1.carbon.carbon_processes as carbon_processes -import clearwater_modules_python.nsm1.CBOD.CBOD_processes as CBOD_processes -import clearwater_modules_python.nsm1.DOX.DOX_processes as DOX_processes +from clearwater_modules import base +from clearwater_modules.nsm1.model import NutrientBudget +import clearwater_modules.nsm1.algae.processes as algae_processes import clearwater_modules.nsm1.alkalinity.processes as alkalinity_processes -import clearwater_modules_python.shared.processes as shared_processes -import clearwater_modules_python.tsm as tsm +import clearwater_modules.nsm1.balgae.processes as balgae_processes +import clearwater_modules.nsm1.carbon.processes as carbon_processes +import clearwater_modules.nsm1.CBOD.processes as CBOD_processes +import clearwater_modules.nsm1.DOX.processes as DOX_processes +import clearwater_modules.nsm1.n2.processes as n2_processes +import clearwater_modules.nsm1.nitrogen.processes as nitrogen_processes +import clearwater_modules.nsm1.pathogens.processes as pathogens_processes +import clearwater_modules.nsm1.phosphorus.processes as phosphorus_processes +import clearwater_modules.nsm1.POM.processes as POM_processes +import clearwater_modules.shared.processes as shared_processes + @base.register_variable(models=NutrientBudget) class Variable(base.Variable): @@ -35,7 +40,7 @@ def mock_equation(water_temp_c: float) -> float: units='g-D/m^2', description='Benthic Algae Concentration', use='state', - process=mock_equation #TODO depends on benthic algae module + process=balgae_processes.Ab ) Variable( @@ -44,7 +49,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-N/L', description='Ammonium Concentration', use='state', - process=nitrogen_processes.NH4_new + process=nitrogen_processes.NH4 ) Variable( @@ -53,7 +58,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-N/L', description='Nitrate Concentration', use='state', - process=nitrogen_processes.NO3_new + process=nitrogen_processes.NO3 ) Variable( @@ -62,7 +67,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-N/L', description='Organic Nitrogen Concentration', use='state', - process=nitrogen_processes.OrgN_new + process=nitrogen_processes.OrgN ) Variable( @@ -71,7 +76,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-N/L', description='Nitrogen concentration air', use='state', - process=n2_processes.N2_new + process=n2_processes.N2 ) Variable( @@ -80,7 +85,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-P/L', description='Total Inorganic Phosphorus Concentration', use='state', - process=mock_equation #TODO this variable only changes with phosphorous module + process=phosphorus_processes.TIP ) Variable( @@ -89,7 +94,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-P/L', description='Total Organic Phosphorus Concentration', use='state', - process=mock_equation #TODO this variable only changes with phosphorous module + process=phosphorus_processes.OrgP ) Variable( @@ -98,7 +103,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-C/L', description='Particulate Organic Carbon Concentration', use='state', - process=carbon_processes.POC_new + process=carbon_processes.POC ) Variable( @@ -107,7 +112,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-C/L', description='Dissolved Organic Carbon Concentration', use='state', - process=carbon_processes.DOC_new + process=carbon_processes.DOC ) Variable( @@ -116,7 +121,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-C/L', description='Dissolved Inorganic Carbon Concentration', use='state', - process=carbon_processes.DIC_new + process=carbon_processes.DIC ) Variable( @@ -125,7 +130,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-D/L', description='Particulate Organic Matter Concentration', use='state', - process=mock_equation #TODO this variable only changes with pom module + process=POM_processes.POM ) Variable( @@ -134,7 +139,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-D/L', description='Sediment Particulate Organic Matter Concentration', use='state', - process=mock_equation #TODO this variable only changes with pom module + process=mock_equation#TODO might be Sedflux ) Variable( @@ -143,7 +148,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-O2/L', description='Carbonaceous Biochemical Oxygen Demand Concentration', use='state', - process=CBOD_processes.CBOD_new + process=CBOD_processes.CBOD ) Variable( @@ -152,7 +157,7 @@ def mock_equation(water_temp_c: float) -> float: units='mg-O2/L', description='Dissolved Oxygen', use='state', - process=DOX_processes.DOX_new + process=DOX_processes.DOX ) Variable( @@ -161,7 +166,7 @@ def mock_equation(water_temp_c: float) -> float: units='cfu/100mL', description='Pathogen concentration', use='state', - process=pathogen_processes.PX_new + process=pathogens_processes.PX ) Variable( @@ -170,24 +175,5 @@ def mock_equation(water_temp_c: float) -> float: units='mg-CaCO3/L', description='Alkalinity concentration', use='state', - process=alkalinity_processes.Alk_new #TODO this variable only changes with alkalinity module -) - -#TODO not sure the order of calling with tsm -Variable( - name='TwaterC', - long_name='Water Temperature', - units='C', - description='Water Temperature Degree Celsius', - use='state', - process=tsm.processes.t_water_c -) - -Variable( - name='depth', - long_name='Water Depth', - units='m', - description='Water depth from surface', - use='state', - process=shared_processes.depth_calc + process=alkalinity_processes.Alk ) \ No newline at end of file diff --git a/src/clearwater_modules/nsm1/static_variables_global.py b/src/clearwater_modules/nsm1/static_variables_global.py index 7e2dad3..57537c3 100644 --- a/src/clearwater_modules/nsm1/static_variables_global.py +++ b/src/clearwater_modules/nsm1/static_variables_global.py @@ -1,7 +1,7 @@ import clearwater_modules.base as base from clearwater_modules.nsm1.model import NutrientBudget -import clearwater_modules.nsm1.algae.algae_processes as algae_processes -import clearwater_modules.nsm1.nitrogen.nitrogen_processes as nitrogen_processes +import clearwater_modules.nsm1.algae.processes as processes +import clearwater_modules.nsm1.nitrogen.processes as processes @base.register_variable(models=NutrientBudget) @@ -11,31 +11,6 @@ class Variable(base.Variable): # Global parameters - -Variable( - name='L', - long_name='Light attenuation coefficient', - units='unitless', - description='Light attenuation coefficient', - use='static', -) - -Variable( - name='fdp', - long_name='Fraction P dissolved', - units='unitless', - description='Fraction P dissolved', - use='static', -) - -Variable( - name='PAR', - long_name='Surface light intensity', - units='W/m^2', - description='Surface light intensity', - use='static', -) - Variable( name='vson', long_name='Organic N settling velocity', @@ -52,6 +27,22 @@ class Variable(base.Variable): use='static' ) +Variable( + name='vsop', + long_name='Organic phosphorus settling velocity', + units='m/d', + description='Organic phosphorus settling velocity', + use='static' +) + +Variable( + name='vs', + long_name='Sediment settling velocity', + units='m/d', + description='Sediment settling velocity', + use='static' +) + Variable( name='SOD_20', long_name='Sediment oxygen demand at 20 degrees C', @@ -68,6 +59,55 @@ class Variable(base.Variable): use='static' ) +Variable( + name='fcom', + long_name='Fraction of carbon in organic matter', + units='mg-C/mg-D', + description='Fraction of carbon in organic matter', + use='static' +) + +Variable( + name='vb', + long_name='Burial velocity', + units='m/d', + description='Rate at which constituents are buried on the bottom', + use='static' +) + + +Variable( + name='kaw_20_user', + long_name='Wind oxygen reaeration velocity at 20C', + units='m/d', + description='Wind oxygen reaeration velocity at 20C', + use='static' +) + +Variable( + name='kah_20_user', + long_name='Hydraulic oxygen reaeration rate at 20C', + units='1/d', + description='Hydraulic oxygen reaeration rate at 20C', + use='static' +) + +Variable( + name='hydraulic_reaeration_option', + long_name='Option for chosing the method by which O2 reaeration rate is calculated', + units='unitless', + description='Selects method for computing O2 reaeration rate', + use='static' +) + +Variable( + name='wind_reaeration_option', + long_name='Option for chosing the method by which wind reaeration is calculated', + units='unitless', + description='Selects method for computing O2 reaeration due to wind', + use='static' +) + # Global module options Variable( @@ -191,9 +231,142 @@ class Variable(base.Variable): ) Variable( - name='use_POM2', + name='use_POM', long_name='Use particulate organic matter module', units='unitless', description='True/False use particulate organic matter module', use='static', ) + + +Variable(## + name='Fr_PAR', + long_name='fraction of solar radiation within the PAR of the spectrum', + units='unitless', + description='fraction of solar radiation within the PAR of the spectrum', + use='static', +) + +Variable( + name='lambda0', + long_name='background portion', + units='1/m', + description='background portion', + use='static', +) + +Variable( + name='lambda1', + long_name='linear self shading', + units='1/m/(ug Chla/L)', + description='linear self shading', + use='static', +) + +Variable( + name='lambda2', + long_name='non-linear', + units='unitless', + description='non-linear', + use='static', +) + +Variable( + name='lambdas', + long_name='ISS portion', + units='L/mg/m', + description='ISS portion', + use='static', +) + +Variable( + name='lambdam', + long_name='POM portion', + units='L/mg/m', + description='POM portion', + use='static', +) + +#Assume these are static within the one cell calculation but is pulled from the flow model + +Variable( + name='timestep', + long_name='timestep', + units='d', + description='calculation timestep', + use='static', +) + +Variable( + name='velocity', + long_name='velocity', + units='m/s', + description='Average water velocity in cell', + use='static', +) + +Variable( + name='flow', + long_name='flow', + units='m3/s', + description='Average flow rate in cell', + use='static', +) + +Variable( + name='topwidth', + long_name='topwidth', + units='m', + description='Average topwidth of cell', + use='static', +) + +#TODO find units for slope +Variable( + name='slope', + long_name='slope', + units='TODO', + description='Average slope of bottom surface', + use='static', +) + +Variable( + name='shear_velocity', + long_name='shear_velocity', + units='TODO', + description='Average shear velocity on bottom surface', + use='static', +) + +Variable( + name='pressure_atm', + long_name='pressure_atm', + units='TODO', + description='atmospheric pressure in atm', + use='static', +) + +Variable( + name='wind_speed', + long_name='Wind speed at 10 meters above the water surface', + units='m/s', + description='Wind speed at 10 meters above the water surface', + use='static', +) + +Variable( + name='q_solar', + long_name='Incident short-wave solar radiation', + units='W/m2', + description='Incident short-wave solar radiation', + use='static', +) + +#TODO figure out what Solid is +Variable( + name='Solid', + long_name='Solid reaeration option', + units='Unknown', + description='Solid', + use='static', +) diff --git a/src/clearwater_modules/nsm1/test_nsm1_static_vars.py b/src/clearwater_modules/nsm1/test_nsm1_static_vars.py new file mode 100644 index 0000000..3f67a3e --- /dev/null +++ b/src/clearwater_modules/nsm1/test_nsm1_static_vars.py @@ -0,0 +1,772 @@ +import clearwater_modules.base as base +from clearwater_modules.nsm1.model import NutrientBudget + + +@base.register_variable(models=NutrientBudget) +class Variable(base.Variable): + ... + + +Variable( + name='L', + long_name='Light attenuation coefficient', + units='unitless', + description='Light attenuation coefficient', + use='static', +) + +Variable( + name='fdp', + long_name='Fraction P dissolved', + units='unitless', + description='Fraction P dissolved', + use='static', +) + +Variable( + name='PAR', + long_name='Surface light intensity', + units='W/m^2', + description='Surface light intensity', + use='static', +) + +Variable( + name='vson', + long_name='Organic N settling velocity', + units='m/d', + description='Organic N settling velocity', + use='static', +) + +Variable( + name='vsoc', + long_name='POC settling velocity', + units='m/d', + description='POC settling velocity', + use='static' +) + +Variable( + name='vsop', + long_name='Organic phosphorus settling velocity', + units='m/d', + description='Organic phosphorus settling velocity', + use='static' +) + +Variable( + name='vs', + long_name='Sediment settling velocity', + units='m/d', + description='Sediment settling velocity', + use='static' +) + +Variable( + name='SOD_20', + long_name='Sediment oxygen demand at 20 degrees C', + units='g-O2/m/d', + description='Sediment oxygen demand at 20 degrees C', + use='static' +) + +Variable( + name='SOD_theta', + long_name='Arrhenius coefficient for sediment oxygen demand', + units='unitless', + description='Arrhenius coefficient for sediment oxygen demand', + use='static' +) + +Variable( + name='fcom', + long_name='Fraction of carbon in organic matter', + units='mg-C/mg-D', + description='Fraction of carbon in organic matter', + use='static' +) + +Variable( + name='vb', + long_name='Burial velocity', + units='m/d', + description='Rate at which constituents are buried on the bottom', + use='static' +) + + +Variable( + name='kaw_20_user', + long_name='Wind oxygen reaeration velocity at 20C', + units='m/d', + description='Wind oxygen reaeration velocity at 20C', + use='static' +) + +Variable( + name='kah_20_user', + long_name='Hydraulic oxygen reaeration rate at 20C', + units='1/d', + description='Hydraulic oxygen reaeration rate at 20C', + use='static' +) + +Variable( + name='hydraulic_reaeration_option', + long_name='Option for chosing the method by which O2 reaeration rate is calculated', + units='unitless', + description='Selects method for computing O2 reaeration rate', + use='static' +) + +Variable( + name='wind_reaeration_option', + long_name='Option for chosing the method by which wind reaeration is calculated', + units='unitless', + description='Selects method for computing O2 reaeration due to wind', + use='static' +) + +# Global module options + +Variable( + name='use_NH4', + long_name='Use ammonium module', + units='unitless', + description='True/False use ammonium module', + use='static', +) + +Variable( + name='use_NO3', + long_name='Use nitrate module', + units='unitless', + description='True/False use nitrate module', + use='static', +) + +Variable( + name='use_OrgN', + long_name='Use organic nitrogen module', + units='unitless', + description='True/False use organic nitrogen module', + use='static', +) + +Variable( + name='use_SedFlux', + long_name='Use sediment flux module', + units='unitless', + description='True/False use sediment flux module', + use='static', +) + +Variable( + name='use_DOX', + long_name='Use dissolved oxygen module', + units='unitless', + description='True/False use dissolved oxygen module', + use='static', +) + +Variable( + name='use_Algae', + long_name='Use algae module', + units='unitless', + description='True/False use algae module', + use='static', +) + +Variable( + name='use_Balgae', + long_name='Use benthic algae module', + units='unitless', + description='True/False use benthic algae module', + use='static', +) + +Variable( + name='use_TIP', + long_name='Use total inorganic phosphorus module', + units='unitless', + description='True/False use total inorganic phosphorus module', + use='static', +) + +Variable( + name='use_OrgP', + long_name='Use total organic phosphorus module', + units='unitless', + description='True/False use total organic phosphorus module', + use='static', +) + +Variable( + name='use_POC', + long_name='Use particulate organic carbon module', + units='unitless', + description='True/False use particulate organic carbon module', + use='static', +) + +Variable( + name='use_DOC', + long_name='Use dissolved organic carbon module', + units='unitless', + description='True/False use dissolved organic carbon module', + use='static', +) + +Variable( + name='use_DIC', + long_name='Use dissolved inorganic carbon module', + units='unitless', + description='True/False use dissolved inorganic carbon module', + use='static', +) + +Variable( + name='use_N2', + long_name='Use dissolved N2 module', + units='unitless', + description='True/False use N2 module', + use='static', +) + +Variable( + name='use_Pathogen', + long_name='Use pathogen module', + units='unitless', + description='True/False use pathogen module', + use='static', +) + +Variable( + name='use_Alk', + long_name='Use alkalinity module', + units='unitless', + description='True/False use alkalinity module', + use='static', +) + +Variable( + name='use_POM', + long_name='Use particulate organic matter module', + units='unitless', + description='True/False use particulate organic matter module', + use='static', +) + + + +Variable( + name='kpom_20', + long_name='POM dissolution rate at 20C', + units='1/d', + description='POM dissolution rate at 20C', + use='static' +) + +Variable( + name='kop_20', + long_name='Decay rate of organic P to DIP', + units='1/d', + description='Decay rate of organic P to DIP', + use='static', +) + +Variable( + name='rpo4_20', + long_name='Benthic sediment release rate of DIP', + units='g-P/m^2/d', + description='Benthic sediment release rate of DIP', + use='static', +) + +Variable( + name='kdx', + long_name='Pathogen death rate', + units='1/d', + description='Pathogen death rate', + use='static', +) + +Variable( + name='apx', + long_name='Light efficiency factor for pathogen decay', + units='unitless', + description='Light efficiency factor for pathogen decay', + use='static', +) + +Variable( + name='vx', + long_name='Pathogen net settling velocity', + units='unitless', + description='Pathogen net settling velocity', + use='static', +) + + + +Variable( + name='KNR', + long_name='Oxygen inhabitation factor for nitrification', + units='mg-O2/L', + description='Oxygen inhabitation factor for nitrification', + use='static', +) + +Variable( + name='knit_20', + long_name='Nitrification Rate Ammonia decay at 20C', + units='1/d', + description='Nitrification Rate Ammonia NH4 -> NO3 decay at 20C', + use='static', +) + +Variable( + name='kon_20', + long_name='Decay Rate of OrgN to NH4 at 20C', + units='1/d', + description='Decay Rate of OrgN to NH4 at 20C', + use='static', +) + +Variable( + name='kdnit_20', + long_name='Denitrification rate at 20C', + units='1/d', + description='Denitrification rate at 20C', + use='static', +) + +Variable( + name='rnh4_20', + long_name='Sediment release rate of NH4 at 20C', + units='g-N/m^2/d', + description='Sediment release rate of NH4 at 20C', + use='static' +) + +Variable( + name='vno3_20', + long_name='Sediment denitrification velocity at 20C', + units='m/d', + description='Sediment denitrification velocity at 20C', + use='static', +) + +Variable( + name='KsOxdn', + long_name='Half-saturation oxygen inhibition constant for denitrification', + units='mg-O2/L', + description='Half-saturation oxygen inhibition constant for denitrification', + use='static', +) + + +Variable( + name='PN', + long_name='NH4 preference factor algae', + units='unitless', + description='NH4 preference factor algae (1=full NH4, 0=full NO3)', + use='static', +) + +Variable( + name='PNb', + long_name='NH4 preference factor benthic algae', + units='unitless', + description='NH4 preference factor benthic algae (1=full NH4, 0=full NO3)', + use='static', +) + +Variable( + name='kbod_20', + long_name='CBOD oxidation rate at 20C', + units='1/d', + description='CBOD oxidation rate at 20C', + use='static' +) + +Variable( + name='ksbod_20', + long_name='CBOD sedimentation rate at 20C', + units='m/d', + description='CBOD sedimentation rate at 20C', + use='static' +) + +Variable( + name='ksOxbod', + long_name='Half saturation oxygen attenuation constant for CBOD oxidation', + units='mg-O2/L', + description='Half saturation oxygen attenuation constant for CBOD oxidation', + use='static' +) + +Variable( + name='F_pocp', + long_name='Fraction of algal mortality into POC', + units='unitless', + description='Fraction of dead algae that converts to particulate organic carbon', + use='static' +) + +Variable( + name='kdoc_20', + long_name='Dissolved organic carbon oxidation rate', + units='1/d', + description='Dissolved organic carbon oxidation rate', + use='static' +) + +Variable( + name='F_pocb', + long_name='fraction of benthic algal mortality into POC', + units='unitless', + description='fraction of benthic algal mortality into POC', + use='static' +) + +Variable( + name='kpoc_20', + long_name='POC hydrolysis rate at 20 degrees Celsius', + units='1/d', + description='POC hydrolysis rate at 20 degrees Celsius', + use='static' +) + +Variable( + name='K_sOxmc', + long_name='half saturation oxygen attenuation constant for DOC oxidation rate', + units='mg-O2/L', + description='half saturation oxygen attenuation constant for DOC oxidation rate', + use='static' +) + +Variable( + name='pCO2', + long_name='partial atmospheric CO2 pressure', + units='ppm', + description='partial pressure of CO2 in the atmosphere', + use='static' +) + +Variable( + name='FCO2', + long_name='CO2 reaeration rate', + units='1/d', + description='CO2 reaeration rate', + use='static' +) + +Variable( + name='Fw', + long_name='Fraction of benthic algae mortality into water column', + units='unitless', + description='Fraction of benthic algae mortality into water column', + use='static', +) + +Variable( + name='Fb', + long_name='Fraction of bottom area available for benthic algae', + units='unitless', + description='Fraction of bottom area available for benthic algae', + use='static', +) + +Variable( + name='BWd', + long_name='Benthic algae dry weight', + units='unitless', + description='Benthic algae dry weight', + use='static', +) + +Variable( + name='BWc', + long_name='Benthic algae carbon', + units='unitless', + description='Benthic algae carbon', + use='static', +) + +Variable( + name='BWn', + long_name='Benthic algae nitrogen', + units='unitless', + description='Benthic algae nitrogen', + use='static', +) + +Variable( + name='BWp', + long_name='Benthic algae phosphorus', + units='unitless', + description='Benthic algae phosphorus', + use='static', +) + +Variable( + name='BWa', + long_name='Benthic algae Chla', + units='unitless', + description='Benthic algae Chla', + use='static', +) + +Variable( + name='KLb', + long_name='Light limiting constant for benthic algae growth', + units='W/m^2', + description='Light limiting constant for benthic algae growth', + use='static', +) + +Variable( + name='KsNb', + long_name='Half-Saturation N limiting constant for Benthic algae', + units='mg-N/L', + description='Half-Saturation N limiting constant for Benthic algae', + use='static', +) + +Variable( + name='KsPb', + long_name='Half-Saturation P limiting constant for Benthic algae', + units='mg-P/L', + description='Half-Saturation P limiting constant for Benthic algae', + use='static', +) + +Variable( + name='Ksb', + long_name='Half-Saturation density constant for benthic algae growth', + units='g-D/m^2', + description='Half-Saturation density constant for benthic algae growth', + use='static', +) + +Variable( + name='mub_max_20', + long_name='Maximum benthic algal growth rate', + units='1/d', + description='maximum benthic algal growth rate', + use='static', +) + +Variable( + name='krb_20', + long_name='Benthic algal respiration rate', + units='1/d', + description='Benthic algal respiration rate', + use='static', +) + +Variable( + name='kdb_20', + long_name='Benthic algal mortality rate', + units='1/d', + description='Benthic algal mortality rate', + use='static', +) + +Variable( + name='b_growth_rate_option', + long_name='Benthic Algal growth rate options', + units='unitless', + description='Benthic Algal growth rate with two options: 1) Multiplicative, 2) Limiting Nutritent', + use='static', +) + +Variable( + name='b_light_limitation_option', + long_name='Benthic Algal light limitation rate options', + units='unitless', + description='Benthic Algal light limitation rate with three options: 1) Half-saturation formulation, 2) Smiths Model, 3) Steeles Model', + use='static', +) + +Variable( + name='r_alkaa', + long_name='Ratio translating algal growth into Alk if NH4 is the N source', + units='eq/ug-Chla', + description='Ratio translating algal growth into Alk if NH4 is the N source', + use='static' +) + +Variable( + name='r_alkan', + long_name='Ratio translating algal growth into Alk if NO3 is the N source', + units='eq/ug-Chla', + description='Ratio translating algal growth into Alk if NO3 is the N source', + use='static' +) + +Variable( + name='r_alkn', + long_name='Ratio translating NH4 nitrification into Alk', + units='eq/mg-N', + description='Ratio translating NH4 nitrification into Alk', + use='static' +) + +Variable( + name='r_alkden', + long_name='Ratio translating NO3 denitrification into Alk', + units='eq/mg-N', + description='Ratio translating NO3 denitrification into Alk', + use='static' +) + +Variable( + name='r_alkba', + long_name='Ratio translating benthic algae growth into Alk if NH4 is the N source', + units='eq/mg-D', + description='Ratio translating benthic algae growth into Alk if NH4 is the N source', + use='static' +) + +Variable( + name='r_alkbn', + long_name='Ratio translating benthic algae growth into Alk if NO3 is the N source', + units='eq/mg-D', + description='Ratio translating benthic algae growth into Alk if NO3 is the N source', + use='static' +) + +Variable( + name='Awd', + long_name='Algal Dry Weight', + units='mg', + description='Algal Dry Weight', + use='static', +) + +Variable( + name='AWc', + long_name='Carbon Weight', + units='mg', + description='Carbon Weight', + use='static', +) + +Variable( + name='AWn', + long_name='Nitrogen Weight', + units='mg', + description='Nitrogen Weight', + use='static', +) + +Variable( + name='AWp', + long_name='Phosphorus Weight', + units='mg', + description='Phosphorus Weight', + use='static', +) + +Variable( + name='AWa', + long_name='Algal Chlorophyll', + units='ug Chla', + description='Algal Chlorophyll', + use='static', +) + + +Variable( + name='KL', + long_name='Light Limiting Constant for Algal Growth', + units='W/m^2', + description='Light Limiting Constant for Algal Growth', + use='static', +) + +Variable( + name='KsN', + long_name='Half-Saturation N Limiting Constant for Algal Growth', + units='mg-N/L', + description='Half-Saturation N Limiting Constant for Algal Growth', + use='static', +) + +Variable( + name='KsP', + long_name='Half-Saturation P Limiting Constant for Algal Growth', + units='mg-P/L', + description='Half-Saturation P Limiting Constant for Algal Growth', + use='static', +) + +Variable( + name='mu_max_20', + long_name='Max Algae Growth', + units='1/d', + description='Max Algae Growth at 20C', + use='static', +) + +Variable( + name='kdp_20', + long_name='Algal Mortality Rate', + units='1/d', + description='Algal Mortality Rate at 20C', + use='static', +) + +Variable( + name='krp_20', + long_name='Algal Respiration Rate', + units='1/d', + description='Algal Respiration Rate at 20C', + use='static', +) + +Variable( + name='vsap', + long_name='Algal Setting Velocity', + units='m/d', + description='Algal Setting Velocity', + use='static', +) + +Variable( + name='growth_rate_option', + long_name='Growth Rate Option', + units='1/d', + description='Algal growth rate option 1) multiplicative, 2) Limiting Nutrient, 3) Harmonic Mean Option', + use='static', +) + +Variable( + name='growth_rate_option', + long_name='Growth Rate Option', + units='1/d', + description='Algal growth rate option 1) multiplicative, 2) Limiting Nutrient, 3) Harmonic Mean Option', + use='static', +) + +Variable( + name='light_limitation_option', + long_name='Light Limitation Option', + units='1/d', + description='Algal light limitation 1) half-saturation, 2) Smith model, 3) Steele model', + use='static', +) + + + + + + + + + + + diff --git a/src/clearwater_modules/shared/processes.py b/src/clearwater_modules/shared/processes.py index 5054770..6204f62 100644 --- a/src/clearwater_modules/shared/processes.py +++ b/src/clearwater_modules/shared/processes.py @@ -50,3 +50,222 @@ def compute_depth( volume: state variable for volume of computational cell provided by CWR engine """ return volume / surface_area + +@numba.njit +def TwaterK( + TwaterC : xr.DataArray, +) -> xr.DataArray : + """Calculate temperature in kelvin (K) + Args: + TwaterC: water temperature celcius (C) + """ + return celsius_to_kelvin(TwaterC) + + +def kah_20( + kah_20_user: xr.DataArray, + hydraulic_reaeration_option: xr.DataArray, + velocity: xr.DataArray, + depth: xr.DataArray, + flow: xr.DataArray, + topwidth: xr.DataArray, + slope: xr.DataArray, + shear_velocity: xr.DataArray +) -> xr.DataArray: + """Calculate hydraulic oxygen reaeration rate based on flow parameters in different cells + + Args: + kah_20_user: User defined O2 reaeration rate at 20 degrees (1/d) + hydraulic_reaeration_option: Integer value which selects method for computing O2 reaeration rate + velocity: Average water velocity in cell (m/s) + depth: Average water depth in cell (m) + flow: Average flow rate in cell (m3/s) + topwidth: Average topwidth of cell (m) + slope: Average slope of bottom surface + shear_velocity: Average shear velocity on bottom surface (m/s) + """ + + da: xr.DataArray = xr.where(hydraulic_reaeration_option == 1, kah_20_user, + xr.where(hydraulic_reaeration_option == 2, (3.93 * velocity**0.5) / (depth**1.5), + xr.where(hydraulic_reaeration_option == 3, (5.32 * velocity**0.67) / (depth**1.85), + xr.where(hydraulic_reaeration_option == 4, (5.026 * velocity) / (depth**1.67), + xr.where(hydraulic_reaeration_option == 5, xr.where(depth < 0.61, (5.32 * velocity**0.67) / (depth**1.85), xr.where(depth > 0.61, (3.93 * velocity**0.5) / (depth**1.5), (5.026 * velocity) / (depth**1.67))), + xr.where(hydraulic_reaeration_option == 6, xr.where(flow < 0.556, 517 * (velocity * slope)**0.524 * flow**-0.242, 596 * (velocity * slope)**0.528 * flow**-0.136), + xr.where(hydraulic_reaeration_option == 7, xr.where(flow < 0.556, 88 * (velocity * slope)**0.313 * depth**-0.353, 142 * (velocity * slope)**0.333 * depth**-0.66 * topwidth**-0.243), + xr.where(hydraulic_reaeration_option == 8, xr.where(flow < 0.425, 31183 * velocity * slope, 15308 * velocity * slope), + xr.where(hydraulic_reaeration_option == 9, 2.16 * (1 + 9 * (velocity / (9.81 * depth)**0.5)**0.25) * shear_velocity / depth, -9999 + ))))))))) + return da + + +@numba.njit +def kah_tc( + water_temp_c: xr.DataArray, + kah_20: xr.DataArray, + theta: xr.DataArray +) -> xr.DataArray: + """Calculate the temperature adjusted hydraulic oxygen reaeration rate (/d) + + Args: + water_temp_c: Water temperature in Celsius + kah_20: Hydraulic oxygen reaeration rate at 20 degrees Celsius + theta: Arrhenius coefficient + """ + return arrhenius_correction(water_temp_c, kah_20, theta) + + +def kaw_20( + kaw_20_user: xr.DataArray, + wind_speed: xr.DataArray, + wind_reaeration_option: xr.DataArray +) -> xr.DataArray: + """Calculate the wind oxygen reaeration velocity (m/d) based on wind speed, r stands for regional + + Args: + kaw_20_user: User defined wind oxygen reaeration velocity at 20 degrees C (m/d) + wind_speed: Wind speed at 10 meters above the water surface (m/s) + wind_reaeration_option: Integer value which selects method for computing wind oxygen reaeration velocity + """ + Uw10 = wind_speed * (10 / 2)**0.143 + + da: xr.DataArray = xr.where(wind_reaeration_option == 1, kaw_20_user, + xr.where(wind_reaeration_option == 2, 0.864 * Uw10, + xr.where(wind_reaeration_option == 3, xr.where(Uw10 <= 3.5, 0.2 * Uw10, 0.057 * Uw10**2), + xr.where(wind_reaeration_option == 4, 0.728 * Uw10**0.5 - 0.317 * Uw10 + 0.0372 * Uw10**2, + xr.where(wind_reaeration_option == 5, 0.0986 * Uw10**1.64, + xr.where(wind_reaeration_option == 6, 0.5 + 0.05 * Uw10**2, + xr.where(wind_reaeration_option == 7, xr.where(Uw10 <= 5.5, 0.362 * Uw10**0.5, 0.0277 * Uw10**2), + xr.where(wind_reaeration_option == 8, 0.64 + 0.128 * Uw10**2, + xr.where(wind_reaeration_option == 9, xr.where(Uw10 <= 4.1, 0.156 * Uw10**0.63, 0.0269 * Uw10**1.9), + xr.where(wind_reaeration_option == 10, 0.0276 * Uw10**2, + xr.where(wind_reaeration_option == 11, 0.0432 * Uw10**2, + xr.where(wind_reaeration_option == 12, 0.319 * Uw10, + xr.where(wind_reaeration_option == 13, xr.where(Uw10 < 1.6, 0.398, 0.155 * Uw10**2), -9999 + ))))))))))))) + + return da + + +@numba.njit +def kaw_tc( + water_temp_c: xr.DataArray, + kaw_20: xr.DataArray, + theta: xr.DataArray +) -> xr.DataArray: + """Calculate the temperature adjusted wind oxygen reaeration velocity (m/d) + + Args: + water_temp_c: Water temperature in Celsius + kaw_20: Wind oxygen reaeration velocity at 20 degrees Celsius + theta: Arrhenius coefficient + """ + return arrhenius_correction(water_temp_c, kaw_20, theta) + + +@numba.njit +def ka_tc( + kah_tc: xr.DataArray, + kaw_tc: xr.DataArray, + depth: xr.DataArray +) -> xr.DataArray: + """Compute the oxygen reaeration rate, adjusted for temperature (1/d) + + Args: + kah_tc: Oxygen reaeration rate adjusted for temperature (1/d) + kaw_tc: Wind oxygen reaeration velocity adjusted for temperature (m/d) + depth: Average water depth in cell (m) + """ + return kaw_tc / depth + kah_tc + +def SOD_tc( + SOD_20: xr.DataArray, + t_water_C: xr.DataArray, + theta: xr.DataArray, + DOX: xr.DataArray, + KsSOD: xr.DataArray, + use_DOX: xr.DataArray +) -> xr.DataArray: + """Compute the sediment oxygen demand corrected by temperature and dissolved oxygen concentration + + Args: + SOD_20: Sediment oxygen demand at 20 degrees celsius (mg-O2/m2) + t_water_C: Water temperature in degrees C + theta: Arrhenius coefficient + use_DOX: Option to consider DOX concentration in water in calculation of sediment oxygen demand + """ + SOD_tc = arrhenius_correction(t_water_C, SOD_20, theta) + + da: xr.DataArray = xr.where(use_DOX == True, SOD_tc * DOX / (DOX + KsSOD), SOD_tc) + + return da + +@numba.njit +def L( + lambda0: xr.DataArray, + lambda1: xr.DataArray, + lambda2: xr.DataArray, + lambdas: xr.DataArray, + lambdam: xr.DataArray, + Solid: xr.DataArray, + POC: xr.DataArray, + fcom: xr.DataArray, + use_Algae: xr.DataArray, + use_POC: xr.DataArray, + Ap: xr.DataArray, + +) -> xr.DataArray: + """Compute L: lambda: light extinction coefficient (unitless) + + Args: + lambda0: background portion (1/m) + lambda1: linear self shading (1/m/(ug Chla/L)) + lambda2: non-linear (unitless), + lambdas: ISS portion (L/mg/m), + lambdam: POM portion (L/mg/m) + Solid: #TODO define this + POC: particulate organic carbon (mg-C/L) + fcom: ratio of carbon to organic matter (mg-C/mg-D) + use_Algae: true/false use algae module (t/f) + use_POC: true/falseo use particulate organic carbon module (t/f) + Ap: algae concentration (ug-Chla/L) + """ + L=lambda0 + lambdas * Solid + + L=xr.where (use_POC, L+lambdam*POC/fcom, L) + L=xr.where (use_Algae, L+lambda1*Ap + lambda2*Ap**0.66667, L) + + return L + +@numba.njit +def PAR( + use_Algae : bool, + use_Balgae: bool, + q_solar: xr.DataArray, + Fr_PAR: xr.DataArray, +) -> xr.DataArray : + """Calculate temperature in kelvin (K) + Args: + use_Algae : true/false use algae module (t/f) + use_Balgae: true/falsoe use balgae module (t/f) + q_solar: solar radiation (1/d), + Fr_PAR: fraction of solar radiation within the PAR of the spectrum + """ + return xr.where (use_Algae or use_Balgae, q_solar * Fr_PAR) + + +@numba.njit +def fdp( + use_TIP: bool, + Solid : xr.DataArray, + kdpo4: xr.DataArray +) -> xr.DataArray : + + """Calculate kop_tc: Decay rate of organic P to DIP temperature correction (1/d). + + Args: + use_TIP: true/false use total inorganic phosphrous, + Solid : #TODO define this + kdpo4: solid partitioning coeff. of PO4 (L/kg) + """ + + return xr.where(use_TIP, 1/(1+kdpo4 * Solid/0.000001), 0) diff --git a/src/clearwater_modules/tsm/model.py b/src/clearwater_modules/tsm/model.py index f1580e7..a572bd1 100644 --- a/src/clearwater_modules/tsm/model.py +++ b/src/clearwater_modules/tsm/model.py @@ -1,4 +1,4 @@ -"""Water Nutrient Simulation Model (NSM1) module.""" +"""Temperature Simulation Model (TSM) module.""" import xarray as xr import numpy as np from enum import Enum @@ -36,6 +36,7 @@ def __init__( if temp_parameters is None: temp_parameters = {} + # set default values for key, value in self.__meteo_parameters.items(): self.__meteo_parameters[key] = meteo_parameters.get( diff --git a/src/clearwater_modules/tsm/state_variables.py b/src/clearwater_modules/tsm/state_variables.py index d77e128..270b8c1 100644 --- a/src/clearwater_modules/tsm/state_variables.py +++ b/src/clearwater_modules/tsm/state_variables.py @@ -9,7 +9,6 @@ class Variable(base.Variable): """TSM state variables.""" ... - Variable( name='water_temp_c', long_name='Water temperature', @@ -21,7 +20,6 @@ class Variable(base.Variable): # TODO: remove mock_equation - def mock_surface_area(surface_area: xr.DataArray) -> xr.DataArray: return surface_area